From 2db84a8ee7cd3873e11eb0c13abc09869caa0042 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Thu, 22 Sep 2022 11:52:48 +0200 Subject: Fix all merge conflicts There was a lot of conflicts and to solve, I refresh manually the source folders. --- source/blender/CMakeLists.txt | 4 +- source/blender/blendthumb/CMakeLists.txt | 2 +- .../blender/blendthumb/src/blendthumb_extract.cc | 8 +- source/blender/blenfont/BLF_api.h | 8 +- source/blender/blenfont/CMakeLists.txt | 1 - source/blender/blenfont/intern/blf.c | 28 +- source/blender/blenfont/intern/blf_font.c | 516 +- source/blender/blenfont/intern/blf_font_default.c | 71 +- source/blender/blenfont/intern/blf_glyph.c | 222 +- source/blender/blenfont/intern/blf_internal.h | 29 +- .../blender/blenfont/intern/blf_internal_types.h | 157 +- source/blender/blenfont/intern/blf_thumbs.c | 29 +- source/blender/blenkernel/BKE_DerivedMesh.h | 13 - source/blender/blenkernel/BKE_appdir.h | 31 +- source/blender/blenkernel/BKE_attribute.hh | 169 +- source/blender/blenkernel/BKE_attribute_math.hh | 225 +- source/blender/blenkernel/BKE_blender_version.h | 4 +- source/blender/blenkernel/BKE_bpath.h | 3 + source/blender/blenkernel/BKE_cachefile.h | 10 +- source/blender/blenkernel/BKE_cdderivedmesh.h | 4 - source/blender/blenkernel/BKE_cloth.h | 2 +- source/blender/blenkernel/BKE_collection.h | 13 +- source/blender/blenkernel/BKE_collision.h | 1 + source/blender/blenkernel/BKE_compute_contexts.hh | 46 + source/blender/blenkernel/BKE_crazyspace.hh | 53 + source/blender/blenkernel/BKE_cryptomatte.h | 2 + source/blender/blenkernel/BKE_cryptomatte.hh | 26 +- source/blender/blenkernel/BKE_curves.h | 2 +- source/blender/blenkernel/BKE_curves.hh | 131 +- source/blender/blenkernel/BKE_curves_utils.hh | 355 +- source/blender/blenkernel/BKE_customdata.h | 60 +- source/blender/blenkernel/BKE_deform.h | 80 +- source/blender/blenkernel/BKE_displist.h | 6 - source/blender/blenkernel/BKE_effect.h | 1 + source/blender/blenkernel/BKE_fcurve.h | 26 +- source/blender/blenkernel/BKE_fcurve_driver.h | 1 - source/blender/blenkernel/BKE_geometry_fields.hh | 176 +- source/blender/blenkernel/BKE_geometry_set.h | 3 +- source/blender/blenkernel/BKE_geometry_set.hh | 102 +- source/blender/blenkernel/BKE_global.h | 2 +- source/blender/blenkernel/BKE_gpencil_geom.h | 22 +- source/blender/blenkernel/BKE_icons.h | 4 +- source/blender/blenkernel/BKE_idprop.h | 8 +- source/blender/blenkernel/BKE_idprop.hh | 9 +- source/blender/blenkernel/BKE_idtype.h | 8 +- source/blender/blenkernel/BKE_image.h | 38 +- source/blender/blenkernel/BKE_image_format.h | 2 +- source/blender/blenkernel/BKE_image_save.h | 4 +- source/blender/blenkernel/BKE_key.h | 10 +- source/blender/blenkernel/BKE_lattice.h | 1 - source/blender/blenkernel/BKE_layer.h | 151 +- source/blender/blenkernel/BKE_lib_id.h | 83 +- source/blender/blenkernel/BKE_lib_override.h | 15 + .../blenkernel/BKE_lib_principle_properties.h | 85 - source/blender/blenkernel/BKE_lib_query.h | 1 - source/blender/blenkernel/BKE_lib_remap.h | 8 +- source/blender/blenkernel/BKE_main.h | 20 +- source/blender/blenkernel/BKE_main_namemap.h | 66 + source/blender/blenkernel/BKE_mball.h | 24 +- source/blender/blenkernel/BKE_mball_tessellate.h | 8 +- source/blender/blenkernel/BKE_mesh.h | 237 +- source/blender/blenkernel/BKE_mesh_fair.h | 14 +- .../blender/blenkernel/BKE_mesh_legacy_convert.h | 29 + source/blender/blenkernel/BKE_mesh_mapping.h | 64 +- source/blender/blenkernel/BKE_mesh_remap.h | 16 +- source/blender/blenkernel/BKE_mesh_remesh_voxel.h | 4 +- source/blender/blenkernel/BKE_mesh_sample.hh | 46 +- source/blender/blenkernel/BKE_modifier.h | 23 +- source/blender/blenkernel/BKE_nla.h | 2 +- source/blender/blenkernel/BKE_node.h | 77 +- source/blender/blenkernel/BKE_node_runtime.hh | 486 +- source/blender/blenkernel/BKE_node_tree_update.h | 1 + source/blender/blenkernel/BKE_object.h | 31 +- source/blender/blenkernel/BKE_outliner_treehash.h | 46 - source/blender/blenkernel/BKE_outliner_treehash.hh | 76 + source/blender/blenkernel/BKE_paint.h | 259 +- source/blender/blenkernel/BKE_particle.h | 2 +- source/blender/blenkernel/BKE_pbvh.h | 183 +- source/blender/blenkernel/BKE_pbvh_pixels.hh | 10 +- source/blender/blenkernel/BKE_pointcache.h | 2 +- source/blender/blenkernel/BKE_pointcloud.h | 8 +- source/blender/blenkernel/BKE_scene.h | 6 +- source/blender/blenkernel/BKE_screen.h | 12 +- source/blender/blenkernel/BKE_shrinkwrap.h | 4 +- source/blender/blenkernel/BKE_sound.h | 2 +- source/blender/blenkernel/BKE_spline.hh | 688 --- source/blender/blenkernel/BKE_subdiv_ccg.h | 1 + source/blender/blenkernel/BKE_subdiv_eval.h | 2 +- source/blender/blenkernel/BKE_subdiv_mesh.h | 8 +- source/blender/blenkernel/BKE_volume.h | 9 + source/blender/blenkernel/BKE_writeffmpeg.h | 2 +- source/blender/blenkernel/CMakeLists.txt | 44 +- source/blender/blenkernel/intern/DerivedMesh.cc | 223 +- source/blender/blenkernel/intern/action.c | 7 +- source/blender/blenkernel/intern/action_mirror.c | 2 +- source/blender/blenkernel/intern/anim_data.c | 13 +- source/blender/blenkernel/intern/anim_sys.c | 11 +- source/blender/blenkernel/intern/appdir.c | 54 +- source/blender/blenkernel/intern/armature.c | 12 +- source/blender/blenkernel/intern/armature_deform.c | 54 +- source/blender/blenkernel/intern/armature_update.c | 2 +- source/blender/blenkernel/intern/asset_catalog.cc | 2 +- source/blender/blenkernel/intern/attribute.cc | 241 +- .../blender/blenkernel/intern/attribute_access.cc | 451 +- .../blenkernel/intern/attribute_access_intern.hh | 55 +- source/blender/blenkernel/intern/attribute_math.cc | 69 +- .../blender/blenkernel/intern/blender_copybuffer.c | 2 +- source/blender/blenkernel/intern/blendfile.c | 6 +- .../blenkernel/intern/blendfile_link_append.c | 22 +- source/blender/blenkernel/intern/boids.c | 2 +- source/blender/blenkernel/intern/brush.cc | 24 +- source/blender/blenkernel/intern/bvhutils.cc | 64 +- source/blender/blenkernel/intern/cachefile.c | 8 +- source/blender/blenkernel/intern/camera.c | 95 +- source/blender/blenkernel/intern/cdderivedmesh.c | 40 - source/blender/blenkernel/intern/cloth.c | 23 +- source/blender/blenkernel/intern/collection.c | 98 +- source/blender/blenkernel/intern/collision.c | 3 +- source/blender/blenkernel/intern/colorband.c | 2 +- .../blender/blenkernel/intern/compute_contexts.cc | 38 + source/blender/blenkernel/intern/constraint.c | 118 +- source/blender/blenkernel/intern/context.c | 4 +- source/blender/blenkernel/intern/crazyspace.c | 584 --- source/blender/blenkernel/intern/crazyspace.cc | 655 +++ source/blender/blenkernel/intern/cryptomatte.cc | 79 +- .../blender/blenkernel/intern/cryptomatte_test.cc | 2 +- source/blender/blenkernel/intern/curve.cc | 8 +- .../blender/blenkernel/intern/curve_catmull_rom.cc | 26 +- source/blender/blenkernel/intern/curve_eval.cc | 587 --- .../blenkernel/intern/curve_legacy_convert.cc | 14 +- source/blender/blenkernel/intern/curve_nurbs.cc | 40 +- source/blender/blenkernel/intern/curve_poly.cc | 17 +- .../blenkernel/intern/curve_to_mesh_convert.cc | 10 +- source/blender/blenkernel/intern/curves.cc | 111 +- .../blender/blenkernel/intern/curves_geometry.cc | 340 +- source/blender/blenkernel/intern/curves_utils.cc | 27 + source/blender/blenkernel/intern/customdata.cc | 785 +-- source/blender/blenkernel/intern/data_transfer.c | 143 +- source/blender/blenkernel/intern/deform.c | 195 +- source/blender/blenkernel/intern/displist.cc | 164 +- source/blender/blenkernel/intern/dynamicpaint.c | 65 +- .../blender/blenkernel/intern/editmesh_tangent.c | 434 -- .../blender/blenkernel/intern/editmesh_tangent.cc | 331 ++ source/blender/blenkernel/intern/effect.c | 6 +- source/blender/blenkernel/intern/fcurve.c | 80 +- source/blender/blenkernel/intern/fcurve_driver.c | 1 + source/blender/blenkernel/intern/fcurve_test.cc | 1 - source/blender/blenkernel/intern/fluid.c | 192 +- source/blender/blenkernel/intern/fmodifier.c | 30 +- .../blenkernel/intern/geometry_component_curve.cc | 1463 ------ .../blenkernel/intern/geometry_component_curves.cc | 100 +- .../intern/geometry_component_edit_data.cc | 58 + .../intern/geometry_component_instances.cc | 3 +- .../blenkernel/intern/geometry_component_mesh.cc | 291 +- .../intern/geometry_component_pointcloud.cc | 24 +- .../blender/blenkernel/intern/geometry_fields.cc | 365 ++ source/blender/blenkernel/intern/geometry_set.cc | 115 +- .../blenkernel/intern/geometry_set_instances.cc | 4 +- source/blender/blenkernel/intern/gpencil.c | 6 +- source/blender/blenkernel/intern/gpencil_curve.c | 11 +- source/blender/blenkernel/intern/gpencil_geom.cc | 177 +- .../blender/blenkernel/intern/gpencil_modifier.c | 7 +- source/blender/blenkernel/intern/icons_rasterize.c | 2 +- source/blender/blenkernel/intern/idprop.c | 10 +- source/blender/blenkernel/intern/idprop_create.cc | 12 +- source/blender/blenkernel/intern/idtype.c | 39 +- source/blender/blenkernel/intern/image.cc | 378 +- source/blender/blenkernel/intern/image_gpu.cc | 20 +- .../blenkernel/intern/image_partial_update.cc | 2 +- source/blender/blenkernel/intern/image_save.cc | 35 +- source/blender/blenkernel/intern/image_test.cc | 19 +- source/blender/blenkernel/intern/ipo.c | 6 +- source/blender/blenkernel/intern/key.c | 185 +- source/blender/blenkernel/intern/lattice.c | 17 +- source/blender/blenkernel/intern/lattice_deform.c | 6 +- source/blender/blenkernel/intern/layer.c | 211 +- source/blender/blenkernel/intern/layer_utils.c | 127 +- source/blender/blenkernel/intern/lib_id.c | 368 +- source/blender/blenkernel/intern/lib_id_delete.c | 9 +- .../blender/blenkernel/intern/lib_id_remapper.cc | 1 + .../blenkernel/intern/lib_id_remapper_test.cc | 1 + source/blender/blenkernel/intern/lib_id_test.cc | 386 +- source/blender/blenkernel/intern/lib_override.cc | 583 ++- .../blenkernel/intern/lib_principle_properties.c | 106 - source/blender/blenkernel/intern/lib_query.c | 14 +- source/blender/blenkernel/intern/lib_remap.c | 7 +- source/blender/blenkernel/intern/library.c | 26 +- source/blender/blenkernel/intern/light.c | 3 +- source/blender/blenkernel/intern/lightprobe.c | 2 +- source/blender/blenkernel/intern/linestyle.c | 7 +- source/blender/blenkernel/intern/main.c | 5 + source/blender/blenkernel/intern/main_namemap.cc | 500 ++ source/blender/blenkernel/intern/mask.c | 4 +- source/blender/blenkernel/intern/material.c | 23 +- source/blender/blenkernel/intern/mball.c | 754 --- source/blender/blenkernel/intern/mball.cc | 715 +++ .../blender/blenkernel/intern/mball_tessellate.c | 87 +- source/blender/blenkernel/intern/mesh.cc | 482 +- .../blenkernel/intern/mesh_boolean_convert.cc | 114 +- .../blender/blenkernel/intern/mesh_calc_edges.cc | 14 +- source/blender/blenkernel/intern/mesh_convert.cc | 612 +-- source/blender/blenkernel/intern/mesh_debug.cc | 9 +- source/blender/blenkernel/intern/mesh_evaluate.cc | 226 +- source/blender/blenkernel/intern/mesh_fair.cc | 85 +- source/blender/blenkernel/intern/mesh_iterators.c | 33 +- .../blenkernel/intern/mesh_legacy_convert.cc | 306 +- source/blender/blenkernel/intern/mesh_mapping.c | 1158 ----- source/blender/blenkernel/intern/mesh_mapping.cc | 1167 +++++ source/blender/blenkernel/intern/mesh_merge.c | 96 +- .../blenkernel/intern/mesh_merge_customdata.cc | 15 +- source/blender/blenkernel/intern/mesh_mirror.c | 40 +- source/blender/blenkernel/intern/mesh_normals.cc | 160 +- source/blender/blenkernel/intern/mesh_remap.c | 95 +- .../blender/blenkernel/intern/mesh_remesh_voxel.cc | 104 +- source/blender/blenkernel/intern/mesh_runtime.cc | 54 +- source/blender/blenkernel/intern/mesh_sample.cc | 152 +- source/blender/blenkernel/intern/mesh_tangent.c | 741 --- source/blender/blenkernel/intern/mesh_tangent.cc | 605 +++ source/blender/blenkernel/intern/mesh_tessellate.c | 342 -- .../blender/blenkernel/intern/mesh_tessellate.cc | 343 ++ source/blender/blenkernel/intern/mesh_validate.cc | 188 +- source/blender/blenkernel/intern/mesh_wrapper.cc | 30 +- source/blender/blenkernel/intern/modifier.c | 53 +- source/blender/blenkernel/intern/movieclip.c | 2 +- source/blender/blenkernel/intern/multires.c | 25 +- .../blender/blenkernel/intern/multires_reshape.c | 3 +- .../blender/blenkernel/intern/multires_reshape.h | 8 + .../intern/multires_reshape_apply_base.c | 31 +- .../blenkernel/intern/multires_reshape_smooth.c | 21 +- .../blenkernel/intern/multires_reshape_subdivide.c | 23 +- .../blenkernel/intern/multires_reshape_util.c | 23 +- .../blenkernel/intern/multires_reshape_vertcos.c | 3 +- .../blenkernel/intern/multires_unsubdivide.c | 51 +- source/blender/blenkernel/intern/nla.c | 7 +- source/blender/blenkernel/intern/node.cc | 277 +- source/blender/blenkernel/intern/node_runtime.cc | 432 ++ .../blender/blenkernel/intern/node_tree_update.cc | 553 +-- source/blender/blenkernel/intern/object.cc | 191 +- source/blender/blenkernel/intern/object_deform.c | 16 +- source/blender/blenkernel/intern/object_dupli.cc | 46 +- source/blender/blenkernel/intern/object_update.c | 59 +- source/blender/blenkernel/intern/ocean.c | 7 +- .../blender/blenkernel/intern/outliner_treehash.c | 238 - .../blender/blenkernel/intern/outliner_treehash.cc | 209 + source/blender/blenkernel/intern/packedFile.c | 36 +- source/blender/blenkernel/intern/paint.c | 2348 --------- source/blender/blenkernel/intern/paint.cc | 2887 +++++++++++ source/blender/blenkernel/intern/particle.c | 69 +- source/blender/blenkernel/intern/particle_child.c | 9 +- .../blenkernel/intern/particle_distribute.c | 40 +- source/blender/blenkernel/intern/particle_system.c | 12 +- source/blender/blenkernel/intern/pbvh.c | 286 +- source/blender/blenkernel/intern/pbvh.cc | 26 +- source/blender/blenkernel/intern/pbvh_bmesh.c | 80 +- source/blender/blenkernel/intern/pbvh_intern.h | 16 +- source/blender/blenkernel/intern/pbvh_pixels.cc | 4 +- source/blender/blenkernel/intern/pointcache.c | 90 +- source/blender/blenkernel/intern/pointcloud.cc | 119 +- source/blender/blenkernel/intern/report.c | 16 +- source/blender/blenkernel/intern/rigidbody.c | 41 +- source/blender/blenkernel/intern/scene.cc | 86 +- source/blender/blenkernel/intern/screen.c | 44 +- source/blender/blenkernel/intern/shader_fx.c | 3 +- source/blender/blenkernel/intern/shrinkwrap.c | 27 +- source/blender/blenkernel/intern/simulation.cc | 6 +- source/blender/blenkernel/intern/softbody.c | 38 +- source/blender/blenkernel/intern/sound.c | 6 +- source/blender/blenkernel/intern/speaker.c | 2 +- source/blender/blenkernel/intern/spline_base.cc | 526 -- source/blender/blenkernel/intern/spline_bezier.cc | 646 --- source/blender/blenkernel/intern/spline_nurbs.cc | 395 -- source/blender/blenkernel/intern/spline_poly.cc | 97 - source/blender/blenkernel/intern/studiolight.c | 22 +- source/blender/blenkernel/intern/subdiv_ccg.c | 5 + source/blender/blenkernel/intern/subdiv_ccg_mask.c | 7 +- .../blenkernel/intern/subdiv_ccg_material.c | 14 +- .../blenkernel/intern/subdiv_converter_mesh.c | 42 +- .../intern/subdiv_displacement_multires.c | 7 +- source/blender/blenkernel/intern/subdiv_eval.c | 16 +- source/blender/blenkernel/intern/subdiv_foreach.c | 85 +- source/blender/blenkernel/intern/subdiv_mesh.c | 1201 ----- source/blender/blenkernel/intern/subdiv_mesh.cc | 1230 +++++ source/blender/blenkernel/intern/subdiv_modifier.c | 5 - source/blender/blenkernel/intern/subsurf_ccg.c | 21 +- source/blender/blenkernel/intern/text.c | 2 +- source/blender/blenkernel/intern/texture.c | 3 +- source/blender/blenkernel/intern/tracking_detect.c | 4 +- .../blender/blenkernel/intern/tracking_stabilize.c | 2 +- .../blender/blenkernel/intern/type_conversions.cc | 61 +- source/blender/blenkernel/intern/unit.c | 40 +- source/blender/blenkernel/intern/vfont.c | 23 +- source/blender/blenkernel/intern/volume.cc | 52 +- source/blender/blenkernel/intern/volume_to_mesh.cc | 6 +- source/blender/blenkernel/intern/workspace.c | 596 --- source/blender/blenkernel/intern/workspace.cc | 610 +++ source/blender/blenkernel/intern/world.c | 3 +- source/blender/blenkernel/intern/writeffmpeg.c | 2 +- source/blender/blenkernel/nla_private.h | 13 +- source/blender/blenlib/BLI_any.hh | 4 +- source/blender/blenlib/BLI_array_store.h | 1 - source/blender/blenlib/BLI_array_utils.hh | 110 + source/blender/blenlib/BLI_bit_vector.hh | 529 ++ source/blender/blenlib/BLI_bitmap.h | 13 +- source/blender/blenlib/BLI_compute_context.hh | 173 + source/blender/blenlib/BLI_cpp_type.hh | 2 +- source/blender/blenlib/BLI_fileops.h | 1 + source/blender/blenlib/BLI_float3x3.hh | 16 + source/blender/blenlib/BLI_float4x4.hh | 2 +- source/blender/blenlib/BLI_function_ref.hh | 1 - source/blender/blenlib/BLI_generic_span.hh | 57 + .../blender/blenlib/BLI_generic_virtual_array.hh | 1 + source/blender/blenlib/BLI_index_range.hh | 46 +- source/blender/blenlib/BLI_kdopbvh.h | 23 + source/blender/blenlib/BLI_lazy_threading.hh | 83 + source/blender/blenlib/BLI_length_parameterize.hh | 49 +- source/blender/blenlib/BLI_listbase.h | 23 +- source/blender/blenlib/BLI_listbase_wrapper.hh | 4 +- source/blender/blenlib/BLI_map.hh | 4 +- source/blender/blenlib/BLI_math_color.h | 4 +- source/blender/blenlib/BLI_math_geom.h | 4 +- source/blender/blenlib/BLI_math_matrix.h | 272 +- source/blender/blenlib/BLI_math_rotation.h | 101 +- source/blender/blenlib/BLI_math_rotation.hh | 9 + source/blender/blenlib/BLI_math_vector.h | 88 +- source/blender/blenlib/BLI_memory_utils.hh | 43 +- source/blender/blenlib/BLI_multi_value_map.hh | 13 + source/blender/blenlib/BLI_path_util.h | 32 +- source/blender/blenlib/BLI_pool.hh | 84 + source/blender/blenlib/BLI_probing_strategies.hh | 6 +- source/blender/blenlib/BLI_scanfill.h | 2 +- source/blender/blenlib/BLI_serialize.hh | 2 - source/blender/blenlib/BLI_set.hh | 4 +- source/blender/blenlib/BLI_string_utf8.h | 16 +- source/blender/blenlib/BLI_task.hh | 20 + source/blender/blenlib/BLI_utildefines.h | 2 +- source/blender/blenlib/BLI_vector_adaptor.hh | 88 - source/blender/blenlib/BLI_vector_set.hh | 2 +- source/blender/blenlib/BLI_virtual_array.hh | 88 +- source/blender/blenlib/CMakeLists.txt | 26 +- source/blender/blenlib/intern/BLI_index_range.cc | 30 + source/blender/blenlib/intern/BLI_kdopbvh.c | 2 +- source/blender/blenlib/intern/BLI_memarena.c | 3 +- source/blender/blenlib/intern/BLI_memblock.c | 1 - source/blender/blenlib/intern/array_utils.cc | 31 + source/blender/blenlib/intern/bitmap.c | 20 + source/blender/blenlib/intern/boxpack_2d.c | 1 - source/blender/blenlib/intern/compute_context.cc | 48 + source/blender/blenlib/intern/cpp_type.cc | 1 + source/blender/blenlib/intern/filereader_zstd.c | 5 +- .../blenlib/intern/generic_virtual_array.cc | 4 +- source/blender/blenlib/intern/index_mask.cc | 5 +- source/blender/blenlib/intern/lazy_threading.cc | 30 + source/blender/blenlib/intern/math_geom.c | 6 +- source/blender/blenlib/intern/math_matrix.c | 174 +- source/blender/blenlib/intern/math_rotation.c | 150 +- source/blender/blenlib/intern/math_rotation.cc | 13 + source/blender/blenlib/intern/mesh_boolean.cc | 34 +- source/blender/blenlib/intern/mesh_intersect.cc | 2 +- source/blender/blenlib/intern/noise.c | 8 +- source/blender/blenlib/intern/noise.cc | 69 +- source/blender/blenlib/intern/path_util.c | 105 +- source/blender/blenlib/intern/rct.c | 4 +- source/blender/blenlib/intern/scanfill.c | 2 +- source/blender/blenlib/intern/smallhash.c | 3 +- source/blender/blenlib/intern/storage.c | 2 +- source/blender/blenlib/intern/string_cursor_utf8.c | 2 +- source/blender/blenlib/intern/string_search.cc | 4 +- source/blender/blenlib/intern/string_utf8.c | 56 +- source/blender/blenlib/intern/system.c | 14 +- source/blender/blenlib/intern/task_pool.cc | 8 +- source/blender/blenlib/intern/task_range.cc | 3 + source/blender/blenlib/intern/winstuff.c | 33 +- .../blender/blenlib/tests/BLI_array_store_test.cc | 10 +- .../blender/blenlib/tests/BLI_bit_vector_test.cc | 186 + source/blender/blenlib/tests/BLI_bitmap_test.cc | 46 + source/blender/blenlib/tests/BLI_float3x3_test.cc | 16 + .../blender/blenlib/tests/BLI_index_range_test.cc | 63 + .../blenlib/tests/BLI_length_parameterize_test.cc | 20 +- .../blenlib/tests/BLI_math_rotation_test.cc | 103 + source/blender/blenlib/tests/BLI_path_util_test.cc | 7 + source/blender/blenlib/tests/BLI_pool_test.cc | 51 + source/blender/blenlib/tests/BLI_set_test.cc | 10 +- .../blenlib/tests/BLI_string_search_test.cc | 2 +- source/blender/blenlib/tests/BLI_vector_test.cc | 2 +- .../blenlib/tests/performance/CMakeLists.txt | 4 + source/blender/blenloader/BLO_blend_validate.h | 8 + source/blender/blenloader/BLO_read_write.h | 5 +- source/blender/blenloader/BLO_readfile.h | 8 +- source/blender/blenloader/BLO_undofile.h | 8 + source/blender/blenloader/CMakeLists.txt | 27 +- source/blender/blenloader/intern/blend_validate.c | 200 - source/blender/blenloader/intern/blend_validate.cc | 202 + source/blender/blenloader/intern/readblenentry.c | 453 -- source/blender/blenloader/intern/readblenentry.cc | 458 ++ source/blender/blenloader/intern/readfile.c | 5152 ------------------- source/blender/blenloader/intern/readfile.cc | 5211 ++++++++++++++++++++ source/blender/blenloader/intern/readfile.h | 10 + .../blender/blenloader/intern/readfile_tempload.c | 59 - .../blender/blenloader/intern/readfile_tempload.cc | 60 + source/blender/blenloader/intern/undofile.c | 344 -- source/blender/blenloader/intern/undofile.cc | 349 ++ source/blender/blenloader/intern/versioning_250.c | 19 +- source/blender/blenloader/intern/versioning_280.c | 15 +- source/blender/blenloader/intern/versioning_290.c | 26 +- source/blender/blenloader/intern/versioning_300.c | 3288 ------------ source/blender/blenloader/intern/versioning_300.cc | 3441 +++++++++++++ source/blender/blenloader/intern/versioning_400.cc | 52 + .../blender/blenloader/intern/versioning_common.cc | 2 + .../blender/blenloader/intern/versioning_cycles.c | 3 - .../blenloader/intern/versioning_defaults.c | 32 +- .../blender/blenloader/intern/versioning_legacy.c | 8 +- .../blender/blenloader/intern/versioning_userdef.c | 5 +- source/blender/blenloader/intern/writefile.c | 1608 ------ source/blender/blenloader/intern/writefile.cc | 1618 ++++++ .../tests/blendfile_loading_base_test.cc | 10 +- source/blender/blentranslation/BLT_translation.h | 2 +- .../blender/blentranslation/msgfmt/CMakeLists.txt | 21 +- source/blender/bmesh/CMakeLists.txt | 4 +- source/blender/bmesh/bmesh.h | 2 +- source/blender/bmesh/intern/bmesh_construct.c | 46 +- source/blender/bmesh/intern/bmesh_core.c | 2 +- source/blender/bmesh/intern/bmesh_delete.c | 7 +- source/blender/bmesh/intern/bmesh_interp.c | 4 +- source/blender/bmesh/intern/bmesh_log.c | 32 +- source/blender/bmesh/intern/bmesh_log.h | 115 +- source/blender/bmesh/intern/bmesh_marking.c | 4 +- source/blender/bmesh/intern/bmesh_mesh.c | 1354 ----- source/blender/bmesh/intern/bmesh_mesh.cc | 1367 +++++ source/blender/bmesh/intern/bmesh_mesh.h | 2 +- source/blender/bmesh/intern/bmesh_mesh_convert.cc | 346 +- source/blender/bmesh/intern/bmesh_mesh_convert.h | 2 +- .../blender/bmesh/intern/bmesh_mesh_tessellate.c | 2 +- source/blender/bmesh/intern/bmesh_query.h | 1 - source/blender/bmesh/intern/bmesh_query_uv.c | 187 - source/blender/bmesh/intern/bmesh_query_uv.cc | 191 + source/blender/bmesh/intern/bmesh_structure.c | 3 +- source/blender/bmesh/operators/bmo_connect.c | 2 +- .../bmesh/operators/bmo_connect_nonplanar.c | 4 +- source/blender/bmesh/operators/bmo_connect_pair.c | 24 +- source/blender/bmesh/operators/bmo_create.c | 1 - .../blender/bmesh/operators/bmo_join_triangles.c | 4 +- source/blender/bmesh/operators/bmo_poke.c | 15 +- source/blender/bmesh/operators/bmo_primitive.c | 32 +- source/blender/bmesh/operators/bmo_removedoubles.c | 4 +- source/blender/bmesh/operators/bmo_subdivide.c | 4 +- .../bmesh/operators/bmo_subdivide_edgering.c | 28 +- source/blender/bmesh/operators/bmo_utils.c | 4 +- source/blender/bmesh/tools/bmesh_bevel.c | 146 +- source/blender/bmesh/tools/bmesh_path_region_uv.c | 9 +- source/blender/bmesh/tools/bmesh_path_region_uv.h | 6 +- source/blender/bmesh/tools/bmesh_path_uv.c | 207 +- source/blender/bmesh/tools/bmesh_path_uv.h | 10 +- source/blender/compositor/CMakeLists.txt | 1328 ++--- source/blender/compositor/COM_compositor.h | 2 +- source/blender/compositor/intern/COM_Node.h | 4 +- .../blender/compositor/nodes/COM_AlphaOverNode.cc | 4 +- .../compositor/nodes/COM_AntiAliasingNode.cc | 4 +- source/blender/compositor/nodes/COM_BlurNode.cc | 4 +- .../blender/compositor/nodes/COM_BokehBlurNode.cc | 2 +- .../blender/compositor/nodes/COM_BokehImageNode.cc | 2 +- source/blender/compositor/nodes/COM_BoxMaskNode.cc | 2 +- .../blender/compositor/nodes/COM_BrightnessNode.cc | 2 +- .../compositor/nodes/COM_ChannelMatteNode.cc | 4 +- .../compositor/nodes/COM_ChromaMatteNode.cc | 2 +- .../compositor/nodes/COM_ColorBalanceNode.cc | 2 +- .../compositor/nodes/COM_ColorCorrectionNode.cc | 2 +- .../blender/compositor/nodes/COM_ColorCurveNode.cc | 4 +- .../blender/compositor/nodes/COM_ColorMatteNode.cc | 2 +- .../blender/compositor/nodes/COM_ColorRampNode.cc | 2 +- .../blender/compositor/nodes/COM_ColorSpillNode.cc | 2 +- .../compositor/nodes/COM_CombineColorNode.cc | 2 +- .../compositor/nodes/COM_CombineColorNodeLegacy.cc | 2 +- .../blender/compositor/nodes/COM_CompositorNode.cc | 2 +- .../compositor/nodes/COM_ConvertAlphaNode.cc | 2 +- .../compositor/nodes/COM_ConvertColorSpaceNode.cc | 4 +- source/blender/compositor/nodes/COM_CropNode.cc | 2 +- .../compositor/nodes/COM_CryptomatteNode.cc | 5 +- source/blender/compositor/nodes/COM_DefocusNode.cc | 4 +- source/blender/compositor/nodes/COM_DenoiseNode.cc | 4 +- .../blender/compositor/nodes/COM_DespeckleNode.cc | 2 +- .../compositor/nodes/COM_DifferenceMatteNode.cc | 2 +- .../compositor/nodes/COM_DilateErodeNode.cc | 9 +- .../compositor/nodes/COM_DirectionalBlurNode.cc | 2 +- .../compositor/nodes/COM_DistanceMatteNode.cc | 4 +- .../compositor/nodes/COM_DoubleEdgeMaskNode.cc | 2 +- source/blender/compositor/nodes/COM_FilterNode.cc | 16 +- source/blender/compositor/nodes/COM_GlareNode.cc | 4 +- .../nodes/COM_HueSaturationValueCorrectNode.cc | 2 +- source/blender/compositor/nodes/COM_IDMaskNode.cc | 2 +- source/blender/compositor/nodes/COM_ImageNode.cc | 2 +- source/blender/compositor/nodes/COM_InpaintNode.cc | 2 +- source/blender/compositor/nodes/COM_InvertNode.cc | 2 +- source/blender/compositor/nodes/COM_KeyingNode.cc | 4 +- .../compositor/nodes/COM_KeyingScreenNode.cc | 2 +- .../compositor/nodes/COM_LensDistortionNode.cc | 2 +- .../compositor/nodes/COM_LuminanceMatteNode.cc | 2 +- source/blender/compositor/nodes/COM_MapUVNode.cc | 2 +- .../blender/compositor/nodes/COM_MapValueNode.cc | 2 +- source/blender/compositor/nodes/COM_MaskNode.cc | 4 +- source/blender/compositor/nodes/COM_MixNode.cc | 2 +- .../blender/compositor/nodes/COM_MovieClipNode.cc | 2 +- .../compositor/nodes/COM_MovieDistortionNode.cc | 2 +- .../blender/compositor/nodes/COM_OutputFileNode.cc | 10 +- .../compositor/nodes/COM_PlaneTrackDeformNode.cc | 2 +- source/blender/compositor/nodes/COM_ScaleNode.cc | 15 +- .../compositor/nodes/COM_SeparateColorNode.cc | 4 +- .../nodes/COM_SeparateColorNodeLegacy.cc | 2 +- .../compositor/nodes/COM_SplitViewerNode.cc | 2 +- .../compositor/nodes/COM_Stabilize2dNode.cc | 2 +- .../blender/compositor/nodes/COM_SunBeamsNode.cc | 2 +- .../blender/compositor/nodes/COM_SwitchViewNode.cc | 2 +- source/blender/compositor/nodes/COM_TextureNode.cc | 2 +- source/blender/compositor/nodes/COM_TimeNode.cc | 2 +- source/blender/compositor/nodes/COM_TonemapNode.cc | 2 +- .../compositor/nodes/COM_TrackPositionNode.cc | 2 +- .../blender/compositor/nodes/COM_TranslateNode.cc | 4 +- .../blender/compositor/nodes/COM_VectorBlurNode.cc | 4 +- .../compositor/nodes/COM_VectorCurveNode.cc | 2 +- source/blender/compositor/nodes/COM_ViewerNode.cc | 2 +- .../operations/COM_BokehImageOperation.h | 4 +- .../compositor/operations/COM_BoxMaskOperation.h | 4 +- .../operations/COM_BrightnessOperation.cc | 4 +- .../operations/COM_CompositorOperation.cc | 9 +- .../operations/COM_CurveBaseOperation.cc | 2 +- .../compositor/operations/COM_CurveBaseOperation.h | 2 +- .../compositor/operations/COM_DenoiseOperation.cc | 4 +- .../compositor/operations/COM_DenoiseOperation.h | 6 +- .../operations/COM_DirectionalBlurOperation.h | 4 +- .../operations/COM_DistanceRGBMatteOperation.h | 4 +- .../operations/COM_DoubleEdgeMaskOperation.cc | 4 +- .../compositor/operations/COM_GlareBaseOperation.h | 8 +- .../operations/COM_GlareFogGlowOperation.cc | 2 +- .../operations/COM_GlareFogGlowOperation.h | 2 +- .../operations/COM_GlareGhostOperation.cc | 2 +- .../operations/COM_GlareGhostOperation.h | 2 +- .../operations/COM_GlareSimpleStarOperation.cc | 2 +- .../operations/COM_GlareSimpleStarOperation.h | 2 +- .../operations/COM_GlareStreaksOperation.cc | 2 +- .../operations/COM_GlareStreaksOperation.h | 2 +- .../operations/COM_GlareThresholdOperation.h | 4 +- .../compositor/operations/COM_MapValueOperation.cc | 4 +- .../compositor/operations/COM_MapValueOperation.h | 4 +- .../operations/COM_MovieClipOperation.cc | 2 +- .../operations/COM_OutputFileMultiViewOperation.cc | 4 +- .../operations/COM_OutputFileMultiViewOperation.h | 4 +- .../operations/COM_OutputFileOperation.cc | 2 +- .../operations/COM_OutputFileOperation.h | 2 +- .../compositor/operations/COM_RenderLayersProg.cc | 4 +- .../compositor/operations/COM_SMAAOperation.cc | 2 +- .../compositor/operations/COM_ScaleOperation.cc | 2 +- .../compositor/operations/COM_TextureOperation.cc | 5 +- .../compositor/operations/COM_TonemapOperation.cc | 4 +- .../compositor/operations/COM_TonemapOperation.h | 8 +- .../operations/COM_VectorBlurOperation.cc | 7 +- .../operations/COM_VectorBlurOperation.h | 4 +- .../compositor/operations/COM_ViewerOperation.cc | 6 +- .../compositor/realtime_compositor/CMakeLists.txt | 66 + .../realtime_compositor/COM_compile_state.hh | 170 + .../compositor/realtime_compositor/COM_context.hh | 72 + .../COM_conversion_operation.hh | 158 + .../compositor/realtime_compositor/COM_domain.hh | 166 + .../realtime_compositor/COM_evaluator.hh | 170 + .../realtime_compositor/COM_input_descriptor.hh | 34 + .../COM_input_single_value_operation.hh | 46 + .../realtime_compositor/COM_node_operation.hh | 56 + .../realtime_compositor/COM_operation.hh | 175 + .../COM_realize_on_domain_operation.hh | 49 + .../COM_reduce_to_single_value_operation.hh | 31 + .../compositor/realtime_compositor/COM_result.hh | 234 + .../realtime_compositor/COM_scheduler.hh | 21 + .../realtime_compositor/COM_shader_node.hh | 87 + .../realtime_compositor/COM_shader_operation.hh | 242 + .../realtime_compositor/COM_simple_operation.hh | 64 + .../COM_static_shader_manager.hh | 33 + .../realtime_compositor/COM_texture_pool.hh | 86 + .../realtime_compositor/COM_utilities.hh | 71 + .../realtime_compositor/intern/compile_state.cc | 168 + .../realtime_compositor/intern/context.cc | 36 + .../intern/conversion_operation.cc | 239 + .../realtime_compositor/intern/domain.cc | 38 + .../realtime_compositor/intern/evaluator.cc | 170 + .../intern/input_single_value_operation.cc | 59 + .../realtime_compositor/intern/node_operation.cc | 67 + .../realtime_compositor/intern/operation.cc | 206 + .../intern/realize_on_domain_operation.cc | 130 + .../intern/reduce_to_single_value_operation.cc | 67 + .../realtime_compositor/intern/result.cc | 257 + .../realtime_compositor/intern/scheduler.cc | 314 ++ .../realtime_compositor/intern/shader_node.cc | 157 + .../realtime_compositor/intern/shader_operation.cc | 526 ++ .../realtime_compositor/intern/simple_operation.cc | 55 + .../intern/static_shader_manager.cc | 24 + .../realtime_compositor/intern/texture_pool.cc | 88 + .../realtime_compositor/intern/utilities.cc | 134 + source/blender/datatoc/datatoc_icon.py | 4 +- source/blender/depsgraph/CMakeLists.txt | 5 +- source/blender/depsgraph/DEG_depsgraph.h | 6 +- source/blender/depsgraph/DEG_depsgraph_build.h | 6 +- .../depsgraph/intern/builder/deg_builder.cc | 129 +- .../blender/depsgraph/intern/builder/deg_builder.h | 18 +- .../depsgraph/intern/builder/deg_builder_cache.cc | 17 +- .../depsgraph/intern/builder/deg_builder_cache.h | 24 +- .../depsgraph/intern/builder/deg_builder_cycle.h | 6 +- .../depsgraph/intern/builder/deg_builder_key.cc | 96 + .../depsgraph/intern/builder/deg_builder_key.h | 201 + .../depsgraph/intern/builder/deg_builder_map.h | 6 +- .../depsgraph/intern/builder/deg_builder_nodes.cc | 176 +- .../depsgraph/intern/builder/deg_builder_nodes.h | 37 +- .../intern/builder/deg_builder_nodes_view_layer.cc | 25 +- .../intern/builder/deg_builder_pchanmap.h | 6 +- .../intern/builder/deg_builder_relations.cc | 193 +- .../intern/builder/deg_builder_relations.h | 75 +- .../intern/builder/deg_builder_relations_drivers.h | 6 +- .../intern/builder/deg_builder_relations_impl.h | 6 +- .../intern/builder/deg_builder_relations_keys.cc | 179 - .../builder/deg_builder_relations_view_layer.cc | 3 +- .../intern/builder/deg_builder_remove_noop.h | 6 +- .../depsgraph/intern/builder/deg_builder_rna.cc | 12 +- .../depsgraph/intern/builder/deg_builder_rna.h | 6 +- .../intern/builder/deg_builder_transitive.h | 6 +- .../blender/depsgraph/intern/builder/pipeline.cc | 2 +- source/blender/depsgraph/intern/builder/pipeline.h | 6 +- .../intern/builder/pipeline_all_objects.cc | 4 +- .../intern/builder/pipeline_all_objects.h | 6 +- .../depsgraph/intern/builder/pipeline_compositor.h | 6 +- .../depsgraph/intern/builder/pipeline_from_ids.cc | 4 +- .../depsgraph/intern/builder/pipeline_from_ids.h | 6 +- .../depsgraph/intern/builder/pipeline_render.h | 6 +- .../depsgraph/intern/builder/pipeline_view_layer.h | 6 +- source/blender/depsgraph/intern/debug/deg_debug.h | 6 +- .../intern/debug/deg_debug_relations_graphviz.cc | 1 + .../depsgraph/intern/debug/deg_time_average.h | 6 +- source/blender/depsgraph/intern/depsgraph.cc | 8 +- source/blender/depsgraph/intern/depsgraph.h | 18 +- source/blender/depsgraph/intern/depsgraph_build.cc | 10 +- .../blender/depsgraph/intern/depsgraph_physics.cc | 2 +- .../blender/depsgraph/intern/depsgraph_physics.h | 6 +- source/blender/depsgraph/intern/depsgraph_query.cc | 3 +- .../depsgraph/intern/depsgraph_query_iter.cc | 15 +- .../blender/depsgraph/intern/depsgraph_registry.h | 6 +- .../blender/depsgraph/intern/depsgraph_relation.h | 8 +- source/blender/depsgraph/intern/depsgraph_tag.cc | 97 +- source/blender/depsgraph/intern/depsgraph_tag.h | 10 +- source/blender/depsgraph/intern/depsgraph_type.h | 6 +- source/blender/depsgraph/intern/depsgraph_update.h | 6 +- source/blender/depsgraph/intern/eval/deg_eval.cc | 154 +- source/blender/depsgraph/intern/eval/deg_eval.h | 6 +- .../intern/eval/deg_eval_copy_on_write.cc | 9 +- .../depsgraph/intern/eval/deg_eval_copy_on_write.h | 6 +- .../depsgraph/intern/eval/deg_eval_flush.cc | 16 +- .../blender/depsgraph/intern/eval/deg_eval_flush.h | 6 +- .../intern/eval/deg_eval_runtime_backup.cc | 2 + .../intern/eval/deg_eval_runtime_backup.h | 6 +- .../eval/deg_eval_runtime_backup_animation.h | 6 +- .../intern/eval/deg_eval_runtime_backup_gpencil.h | 6 +- .../intern/eval/deg_eval_runtime_backup_modifier.h | 6 +- .../eval/deg_eval_runtime_backup_movieclip.h | 6 +- .../intern/eval/deg_eval_runtime_backup_object.h | 6 +- .../intern/eval/deg_eval_runtime_backup_pose.h | 6 +- .../intern/eval/deg_eval_runtime_backup_scene.h | 6 +- .../intern/eval/deg_eval_runtime_backup_sequence.h | 6 +- .../eval/deg_eval_runtime_backup_sequencer.h | 6 +- .../intern/eval/deg_eval_runtime_backup_sound.h | 6 +- .../intern/eval/deg_eval_runtime_backup_volume.h | 6 +- .../blender/depsgraph/intern/eval/deg_eval_stats.h | 6 +- .../depsgraph/intern/eval/deg_eval_visibility.cc | 232 + .../depsgraph/intern/eval/deg_eval_visibility.h | 29 + source/blender/depsgraph/intern/node/deg_node.cc | 4 + source/blender/depsgraph/intern/node/deg_node.h | 10 +- .../depsgraph/intern/node/deg_node_component.cc | 23 +- .../depsgraph/intern/node/deg_node_component.h | 55 +- .../depsgraph/intern/node/deg_node_factory.h | 7 +- .../depsgraph/intern/node/deg_node_factory_impl.h | 15 +- .../blender/depsgraph/intern/node/deg_node_id.cc | 9 +- source/blender/depsgraph/intern/node/deg_node_id.h | 23 +- .../depsgraph/intern/node/deg_node_operation.cc | 24 +- .../depsgraph/intern/node/deg_node_operation.h | 25 +- .../blender/depsgraph/intern/node/deg_node_time.h | 6 +- source/blender/draw/CMakeLists.txt | 167 +- .../draw/engines/compositor/compositor_engine.cc | 203 + .../draw/engines/compositor/compositor_engine.h | 13 + .../blender/draw/engines/eevee/eevee_cryptomatte.c | 15 +- source/blender/draw/engines/eevee/eevee_engine.c | 4 +- .../blender/draw/engines/eevee/eevee_lightcache.c | 2 +- .../blender/draw/engines/eevee/eevee_materials.c | 2 +- source/blender/draw/engines/eevee/eevee_private.h | 4 +- source/blender/draw/engines/eevee/eevee_render.c | 2 +- source/blender/draw/engines/eevee/eevee_sampling.c | 5 +- source/blender/draw/engines/eevee/eevee_shaders.c | 14 +- .../draw/engines/eevee/eevee_shadows_cascade.c | 2 +- source/blender/draw/engines/eevee/eevee_volumes.c | 11 +- .../engines/eevee/shaders/closure_type_lib.glsl | 4 +- .../engines/eevee/shaders/cryptomatte_lib.glsl | 19 + .../engines/eevee/shaders/cryptomatte_vert.glsl | 1 + .../eevee/shaders/effect_dof_resolve_frag.glsl | 2 +- .../eevee/shaders/effect_dof_scatter_frag.glsl | 2 +- .../shaders/lightprobe_filter_diffuse_frag.glsl | 2 +- .../draw/engines/eevee/shaders/prepass_frag.glsl | 14 + .../draw/engines/eevee/shaders/raytrace_lib.glsl | 3 +- .../draw/engines/eevee/shaders/shadow_vert.glsl | 4 + .../draw/engines/eevee/shaders/surface_frag.glsl | 6 +- .../draw/engines/eevee/shaders/surface_lib.glsl | 8 +- .../draw/engines/eevee/shaders/surface_vert.glsl | 4 + .../engines/eevee/shaders/volumetric_frag.glsl | 4 + .../engines/eevee/shaders/volumetric_vert.glsl | 5 + .../draw/engines/eevee_next/eevee_camera.cc | 18 +- .../draw/engines/eevee_next/eevee_camera.hh | 23 +- .../draw/engines/eevee_next/eevee_cryptomatte.cc | 132 + .../draw/engines/eevee_next/eevee_cryptomatte.hh | 68 + .../draw/engines/eevee_next/eevee_defines.hh | 80 +- .../engines/eevee_next/eevee_depth_of_field.cc | 761 +++ .../engines/eevee_next/eevee_depth_of_field.hh | 200 + .../draw/engines/eevee_next/eevee_engine.cc | 22 +- .../blender/draw/engines/eevee_next/eevee_film.cc | 362 +- .../blender/draw/engines/eevee_next/eevee_film.hh | 148 +- .../draw/engines/eevee_next/eevee_hizbuffer.cc | 99 + .../draw/engines/eevee_next/eevee_hizbuffer.hh | 88 + .../draw/engines/eevee_next/eevee_instance.cc | 190 +- .../draw/engines/eevee_next/eevee_instance.hh | 27 +- .../blender/draw/engines/eevee_next/eevee_light.cc | 488 ++ .../blender/draw/engines/eevee_next/eevee_light.hh | 169 + .../draw/engines/eevee_next/eevee_material.cc | 93 +- .../draw/engines/eevee_next/eevee_material.hh | 15 +- .../draw/engines/eevee_next/eevee_motion_blur.cc | 256 + .../draw/engines/eevee_next/eevee_motion_blur.hh | 130 + .../draw/engines/eevee_next/eevee_pipeline.cc | 304 +- .../draw/engines/eevee_next/eevee_pipeline.hh | 84 +- .../draw/engines/eevee_next/eevee_renderbuffers.cc | 84 +- .../draw/engines/eevee_next/eevee_renderbuffers.hh | 8 +- .../draw/engines/eevee_next/eevee_sampling.cc | 38 +- .../draw/engines/eevee_next/eevee_sampling.hh | 37 +- .../draw/engines/eevee_next/eevee_shader.cc | 94 +- .../draw/engines/eevee_next/eevee_shader.hh | 35 +- .../draw/engines/eevee_next/eevee_shader_shared.hh | 425 +- .../blender/draw/engines/eevee_next/eevee_sync.cc | 87 +- .../blender/draw/engines/eevee_next/eevee_sync.hh | 12 +- .../draw/engines/eevee_next/eevee_velocity.cc | 124 +- .../draw/engines/eevee_next/eevee_velocity.hh | 86 +- .../blender/draw/engines/eevee_next/eevee_view.cc | 69 +- .../blender/draw/engines/eevee_next/eevee_view.hh | 11 +- .../blender/draw/engines/eevee_next/eevee_world.cc | 4 +- .../eevee_next/shaders/eevee_attributes_lib.glsl | 42 +- .../eevee_next/shaders/eevee_camera_lib.glsl | 20 +- .../eevee_next/shaders/eevee_colorspace_lib.glsl | 37 + .../eevee_next/shaders/eevee_cryptomatte_lib.glsl | 70 + .../eevee_depth_of_field_accumulator_lib.glsl | 680 +++ .../eevee_depth_of_field_bokeh_lut_comp.glsl | 55 + .../eevee_depth_of_field_downsample_comp.glsl | 32 + .../shaders/eevee_depth_of_field_filter_comp.glsl | 163 + .../shaders/eevee_depth_of_field_gather_comp.glsl | 99 + .../eevee_depth_of_field_hole_fill_comp.glsl | 70 + .../shaders/eevee_depth_of_field_lib.glsl | 327 ++ .../shaders/eevee_depth_of_field_reduce_comp.glsl | 247 + .../shaders/eevee_depth_of_field_resolve_comp.glsl | 178 + .../shaders/eevee_depth_of_field_scatter_frag.glsl | 62 + .../shaders/eevee_depth_of_field_scatter_vert.glsl | 45 + .../shaders/eevee_depth_of_field_setup_comp.glsl | 46 + .../eevee_depth_of_field_stabilize_comp.glsl | 367 ++ .../eevee_depth_of_field_tiles_dilate_comp.glsl | 97 + .../eevee_depth_of_field_tiles_flatten_comp.glsl | 78 + .../shaders/eevee_film_cryptomatte_post_comp.glsl | 77 + .../eevee_next/shaders/eevee_film_frag.glsl | 14 +- .../engines/eevee_next/shaders/eevee_film_lib.glsl | 480 +- .../eevee_next/shaders/eevee_hiz_debug_frag.glsl | 24 + .../eevee_next/shaders/eevee_hiz_update_comp.glsl | 121 + .../shaders/eevee_light_culling_debug_frag.glsl | 54 + .../shaders/eevee_light_culling_select_comp.glsl | 62 + .../shaders/eevee_light_culling_sort_comp.glsl | 57 + .../shaders/eevee_light_culling_tile_comp.glsl | 188 + .../shaders/eevee_light_culling_zbin_comp.glsl | 56 + .../eevee_next/shaders/eevee_light_eval_lib.glsl | 129 + .../eevee_next/shaders/eevee_light_iter_lib.glsl | 72 + .../eevee_next/shaders/eevee_light_lib.glsl | 209 + .../engines/eevee_next/shaders/eevee_ltc_lib.glsl | 299 ++ .../shaders/eevee_motion_blur_dilate_comp.glsl | 115 + .../shaders/eevee_motion_blur_flatten_comp.glsl | 103 + .../shaders/eevee_motion_blur_gather_comp.glsl | 221 + .../eevee_next/shaders/eevee_motion_blur_lib.glsl | 48 + .../eevee_next/shaders/eevee_nodetree_lib.glsl | 86 +- .../eevee_next/shaders/eevee_sampling_lib.glsl | 104 + .../eevee_next/shaders/eevee_surf_depth_frag.glsl | 27 +- .../shaders/eevee_surf_forward_frag.glsl | 74 +- .../engines/eevee_next/shaders/eevee_surf_lib.glsl | 6 +- .../eevee_next/shaders/eevee_surf_world_frag.glsl | 10 +- .../eevee_next/shaders/eevee_velocity_lib.glsl | 107 +- .../shaders/eevee_velocity_resolve_comp.glsl | 58 - .../shaders/infos/eevee_depth_of_field_info.hh | 247 + .../eevee_next/shaders/infos/eevee_film_info.hh | 40 +- .../eevee_next/shaders/infos/eevee_hiz_info.hh | 31 + .../shaders/infos/eevee_light_culling_info.hh | 76 + .../shaders/infos/eevee_material_info.hh | 72 +- .../shaders/infos/eevee_motion_blur_info.hh | 46 + .../shaders/infos/eevee_velocity_info.hh | 43 +- .../draw/engines/external/external_engine.c | 12 +- .../draw/engines/gpencil/gpencil_draw_data.c | 2 +- .../blender/draw/engines/gpencil/gpencil_engine.c | 2 +- .../blender/draw/engines/gpencil/gpencil_engine.h | 2 + .../draw/engines/gpencil/gpencil_shader_shared.h | 26 +- .../draw/engines/gpencil/shaders/gpencil_vert.glsl | 2 +- .../engines/gpencil/shaders/infos/gpencil_info.hh | 4 +- .../draw/engines/overlay/overlay_antialiasing.c | 255 - .../draw/engines/overlay/overlay_antialiasing.cc | 256 + .../draw/engines/overlay/overlay_armature.c | 2635 ---------- .../draw/engines/overlay/overlay_armature.cc | 2646 ++++++++++ .../draw/engines/overlay/overlay_background.c | 114 - .../draw/engines/overlay/overlay_background.cc | 114 + .../draw/engines/overlay/overlay_edit_curve.c | 121 - .../draw/engines/overlay/overlay_edit_curve.cc | 121 + .../draw/engines/overlay/overlay_edit_curves.cc | 2 +- .../draw/engines/overlay/overlay_edit_mesh.c | 381 -- .../draw/engines/overlay/overlay_edit_mesh.cc | 382 ++ .../draw/engines/overlay/overlay_edit_text.c | 203 - .../draw/engines/overlay/overlay_edit_text.cc | 219 + .../blender/draw/engines/overlay/overlay_edit_uv.c | 590 --- .../draw/engines/overlay/overlay_edit_uv.cc | 596 +++ .../blender/draw/engines/overlay/overlay_engine.c | 748 --- .../blender/draw/engines/overlay/overlay_engine.cc | 751 +++ .../blender/draw/engines/overlay/overlay_engine.h | 8 + .../blender/draw/engines/overlay/overlay_extra.c | 1615 ------ .../blender/draw/engines/overlay/overlay_extra.cc | 1623 ++++++ .../blender/draw/engines/overlay/overlay_facing.c | 73 - .../blender/draw/engines/overlay/overlay_facing.cc | 73 + source/blender/draw/engines/overlay/overlay_fade.c | 83 - .../blender/draw/engines/overlay/overlay_fade.cc | 83 + .../blender/draw/engines/overlay/overlay_gpencil.c | 471 -- .../draw/engines/overlay/overlay_gpencil.cc | 474 ++ source/blender/draw/engines/overlay/overlay_grid.c | 306 -- .../blender/draw/engines/overlay/overlay_grid.cc | 312 ++ .../blender/draw/engines/overlay/overlay_image.c | 498 -- .../blender/draw/engines/overlay/overlay_image.cc | 500 ++ .../blender/draw/engines/overlay/overlay_lattice.c | 68 - .../draw/engines/overlay/overlay_lattice.cc | 68 + .../draw/engines/overlay/overlay_metaball.c | 132 - .../draw/engines/overlay/overlay_metaball.cc | 133 + .../draw/engines/overlay/overlay_mode_transfer.c | 144 - .../draw/engines/overlay/overlay_mode_transfer.cc | 144 + .../draw/engines/overlay/overlay_motion_path.c | 220 - .../draw/engines/overlay/overlay_motion_path.cc | 220 + .../blender/draw/engines/overlay/overlay_outline.c | 379 -- .../draw/engines/overlay/overlay_outline.cc | 378 ++ .../blender/draw/engines/overlay/overlay_paint.c | 274 - .../blender/draw/engines/overlay/overlay_paint.cc | 275 ++ .../draw/engines/overlay/overlay_particle.c | 207 - .../draw/engines/overlay/overlay_particle.cc | 207 + .../blender/draw/engines/overlay/overlay_private.h | 774 --- .../draw/engines/overlay/overlay_private.hh | 776 +++ .../blender/draw/engines/overlay/overlay_sculpt.c | 77 - .../blender/draw/engines/overlay/overlay_sculpt.cc | 77 + .../draw/engines/overlay/overlay_sculpt_curves.cc | 2 +- .../blender/draw/engines/overlay/overlay_shader.c | 1082 ---- .../blender/draw/engines/overlay/overlay_shader.cc | 1082 ++++ .../draw/engines/overlay/overlay_shader_shared.h | 3 + .../blender/draw/engines/overlay/overlay_volume.c | 53 - .../blender/draw/engines/overlay/overlay_volume.cc | 53 + .../draw/engines/overlay/overlay_wireframe.c | 360 -- .../draw/engines/overlay/overlay_wireframe.cc | 362 ++ .../shaders/infos/overlay_edit_mode_info.hh | 1 - .../overlay/shaders/overlay_antialiasing_frag.glsl | 2 +- .../overlay_armature_envelope_outline_vert.glsl | 2 +- .../shaders/overlay_edit_uv_stretching_vert.glsl | 6 +- .../engines/overlay/shaders/overlay_grid_frag.glsl | 99 +- source/blender/draw/engines/select/select_engine.c | 2 +- .../draw/engines/workbench/workbench_engine.c | 2 +- source/blender/draw/intern/DRW_gpu_wrapper.hh | 294 +- source/blender/draw/intern/DRW_render.h | 93 +- source/blender/draw/intern/draw_attributes.cc | 3 +- source/blender/draw/intern/draw_cache.c | 168 +- source/blender/draw/intern/draw_cache.h | 9 - source/blender/draw/intern/draw_cache_extract.hh | 2 - .../blender/draw/intern/draw_cache_extract_mesh.cc | 2 - .../intern/draw_cache_extract_mesh_render_data.cc | 138 +- source/blender/draw/intern/draw_cache_impl.h | 38 - .../blender/draw/intern/draw_cache_impl_curves.cc | 31 +- .../blender/draw/intern/draw_cache_impl_displist.c | 354 -- .../blender/draw/intern/draw_cache_impl_lattice.c | 6 - source/blender/draw/intern/draw_cache_impl_mesh.cc | 499 +- .../blender/draw/intern/draw_cache_impl_metaball.c | 294 -- .../draw/intern/draw_cache_impl_particles.c | 76 +- .../draw/intern/draw_cache_impl_pointcloud.c | 275 -- .../draw/intern/draw_cache_impl_pointcloud.cc | 282 ++ .../draw/intern/draw_cache_impl_subdivision.cc | 140 +- .../blender/draw/intern/draw_color_management.cc | 2 +- source/blender/draw/intern/draw_command.cc | 600 +++ source/blender/draw/intern/draw_command.hh | 534 ++ source/blender/draw/intern/draw_command_shared.hh | 87 + source/blender/draw/intern/draw_common.c | 10 +- .../draw/intern/draw_common_shader_shared.h | 2 +- source/blender/draw/intern/draw_curves.cc | 236 +- source/blender/draw/intern/draw_debug.c | 196 - source/blender/draw/intern/draw_debug.cc | 739 +++ source/blender/draw/intern/draw_debug.h | 19 +- source/blender/draw/intern/draw_debug.hh | 198 + source/blender/draw/intern/draw_defines.h | 27 + source/blender/draw/intern/draw_hair.cc | 243 +- source/blender/draw/intern/draw_hair_private.h | 6 +- source/blender/draw/intern/draw_handle.hh | 59 + source/blender/draw/intern/draw_instance_data.c | 24 +- source/blender/draw/intern/draw_instance_data.h | 2 +- source/blender/draw/intern/draw_manager.c | 101 +- source/blender/draw/intern/draw_manager.cc | 214 + source/blender/draw/intern/draw_manager.h | 51 +- source/blender/draw/intern/draw_manager.hh | 237 + source/blender/draw/intern/draw_manager_data.c | 117 +- source/blender/draw/intern/draw_manager_exec.c | 31 +- source/blender/draw/intern/draw_pass.hh | 1005 ++++ source/blender/draw/intern/draw_resource.cc | 109 + source/blender/draw/intern/draw_resource.hh | 206 + source/blender/draw/intern/draw_shader.cc | 54 +- source/blender/draw/intern/draw_shader.h | 6 + source/blender/draw/intern/draw_shader_shared.h | 249 +- source/blender/draw/intern/draw_state.h | 225 + source/blender/draw/intern/draw_subdivision.h | 1 + source/blender/draw/intern/draw_texture_pool.cc | 13 + source/blender/draw/intern/draw_texture_pool.h | 17 + source/blender/draw/intern/draw_view.c | 4 +- source/blender/draw/intern/draw_view.cc | 334 ++ source/blender/draw/intern/draw_view.hh | 94 + source/blender/draw/intern/draw_view_data.cc | 56 + source/blender/draw/intern/draw_view_data.h | 1 + source/blender/draw/intern/draw_volume.cc | 4 + .../draw/intern/mesh_extractors/extract_mesh.hh | 84 +- .../mesh_extractors/extract_mesh_ibo_edituv.cc | 105 +- .../mesh_extractors/extract_mesh_ibo_fdots.cc | 6 +- .../mesh_extractors/extract_mesh_ibo_lines.cc | 61 +- .../extract_mesh_ibo_lines_adjacency.cc | 19 +- .../extract_mesh_ibo_lines_paint_mask.cc | 12 +- .../mesh_extractors/extract_mesh_ibo_points.cc | 10 +- .../mesh_extractors/extract_mesh_ibo_tris.cc | 8 +- .../mesh_extractors/extract_mesh_vbo_attributes.cc | 39 +- .../mesh_extractors/extract_mesh_vbo_edge_fac.cc | 6 +- .../extract_mesh_vbo_edituv_stretch_angle.cc | 2 +- .../extract_mesh_vbo_edituv_stretch_area.cc | 25 +- .../mesh_extractors/extract_mesh_vbo_fdots_nor.cc | 12 +- .../mesh_extractors/extract_mesh_vbo_lnor.cc | 16 +- .../extract_mesh_vbo_mesh_analysis.cc | 19 +- .../mesh_extractors/extract_mesh_vbo_pos_nor.cc | 17 +- .../extract_mesh_vbo_sculpt_data.cc | 8 +- .../mesh_extractors/extract_mesh_vbo_vcol.cc | 387 -- .../mesh_extractors/extract_mesh_vbo_weights.cc | 8 +- .../draw/intern/shaders/common_aabb_lib.glsl | 59 + .../draw/intern/shaders/common_attribute_lib.glsl | 1 + .../draw/intern/shaders/common_debug_draw_lib.glsl | 215 + .../intern/shaders/common_debug_print_lib.glsl | 388 ++ .../intern/shaders/common_debug_shape_lib.glsl | 57 + .../draw/intern/shaders/common_intersect_lib.glsl | 466 ++ .../draw/intern/shaders/common_math_geom_lib.glsl | 144 +- .../draw/intern/shaders/common_math_lib.glsl | 14 +- .../draw/intern/shaders/common_shape_lib.glsl | 202 + .../draw/intern/shaders/common_view_lib.glsl | 17 +- .../intern/shaders/draw_command_generate_comp.glsl | 84 + .../shaders/draw_debug_draw_display_frag.glsl | 9 + .../shaders/draw_debug_draw_display_vert.glsl | 15 + .../blender/draw/intern/shaders/draw_debug_info.hh | 56 + .../shaders/draw_debug_print_display_frag.glsl | 133 + .../shaders/draw_debug_print_display_vert.glsl | 29 + .../draw/intern/shaders/draw_object_infos_info.hh | 20 + .../shaders/draw_resource_finalize_comp.glsl | 64 + .../blender/draw/intern/shaders/draw_view_info.hh | 81 +- .../draw/intern/shaders/draw_visibility_comp.glsl | 46 + source/blender/draw/tests/draw_pass_test.cc | 441 ++ source/blender/draw/tests/shaders_test.cc | 2 +- source/blender/editors/animation/CMakeLists.txt | 1 - .../editors/animation/anim_channels_defines.c | 15 +- .../blender/editors/animation/anim_channels_edit.c | 26 +- source/blender/editors/animation/anim_deps.c | 3 +- source/blender/editors/animation/anim_draw.c | 9 +- source/blender/editors/animation/anim_filter.c | 54 +- source/blender/editors/animation/anim_ipo_utils.c | 1 + source/blender/editors/animation/anim_markers.c | 135 +- source/blender/editors/animation/drivers.c | 3 +- source/blender/editors/animation/fmodifier_ui.c | 2 +- source/blender/editors/animation/keyframes_draw.c | 2 +- source/blender/editors/animation/keyframes_edit.c | 13 +- .../blender/editors/animation/keyframes_general.c | 84 +- .../blender/editors/animation/keyframes_keylist.cc | 7 +- source/blender/editors/animation/keyframing.c | 20 +- source/blender/editors/animation/keyingsets.c | 5 +- source/blender/editors/animation/time_scrub_ui.c | 6 +- source/blender/editors/armature/CMakeLists.txt | 1 - source/blender/editors/armature/armature_add.c | 11 +- source/blender/editors/armature/armature_edit.c | 27 +- source/blender/editors/armature/armature_naming.c | 18 +- .../blender/editors/armature/armature_relations.c | 5 +- source/blender/editors/armature/armature_select.c | 55 +- .../blender/editors/armature/armature_skinning.c | 8 +- .../blender/editors/armature/editarmature_undo.c | 7 +- source/blender/editors/armature/meshlaplacian.c | 30 +- source/blender/editors/armature/pose_edit.c | 16 +- source/blender/editors/armature/pose_lib.c | 8 +- source/blender/editors/armature/pose_select.c | 43 +- source/blender/editors/armature/pose_slide.c | 16 +- source/blender/editors/armature/pose_transform.c | 9 +- source/blender/editors/armature/pose_utils.c | 5 +- source/blender/editors/asset/ED_asset_handle.h | 13 + source/blender/editors/asset/ED_asset_list.h | 2 +- .../blender/editors/asset/intern/asset_handle.cc | 30 + .../blender/editors/asset/intern/asset_indexer.cc | 16 +- .../asset/intern/asset_library_reference_enum.cc | 6 +- source/blender/editors/asset/intern/asset_list.cc | 7 +- source/blender/editors/asset/intern/asset_ops.cc | 3 +- source/blender/editors/curve/CMakeLists.txt | 1 - source/blender/editors/curve/editcurve.c | 498 +- source/blender/editors/curve/editcurve_add.c | 6 +- source/blender/editors/curve/editcurve_paint.c | 2 +- source/blender/editors/curve/editcurve_pen.c | 4 +- source/blender/editors/curve/editcurve_query.c | 2 +- source/blender/editors/curve/editcurve_select.c | 98 +- source/blender/editors/curve/editcurve_undo.c | 7 +- source/blender/editors/curve/editfont.c | 83 +- source/blender/editors/curve/editfont_undo.c | 5 +- source/blender/editors/curves/CMakeLists.txt | 1 + source/blender/editors/curves/intern/curves_add.cc | 15 +- source/blender/editors/curves/intern/curves_ops.cc | 171 +- source/blender/editors/geometry/CMakeLists.txt | 1 + .../editors/geometry/geometry_attributes.cc | 18 +- .../blender/editors/gizmo_library/CMakeLists.txt | 1 - .../gizmo_library/gizmo_types/button2d_gizmo.c | 2 +- .../gizmo_library/gizmo_types/cage2d_gizmo.c | 6 +- source/blender/editors/gpencil/CMakeLists.txt | 2 +- source/blender/editors/gpencil/annotate_paint.c | 12 +- source/blender/editors/gpencil/gpencil_add_blank.c | 6 +- .../blender/editors/gpencil/gpencil_add_lineart.c | 6 +- .../blender/editors/gpencil/gpencil_add_monkey.c | 18 +- .../blender/editors/gpencil/gpencil_add_stroke.c | 18 +- source/blender/editors/gpencil/gpencil_armature.c | 13 +- .../editors/gpencil/gpencil_bake_animation.cc | 2 +- source/blender/editors/gpencil/gpencil_convert.c | 3 +- source/blender/editors/gpencil/gpencil_data.c | 7 +- source/blender/editors/gpencil/gpencil_edit.c | 565 ++- source/blender/editors/gpencil/gpencil_fill.c | 390 +- source/blender/editors/gpencil/gpencil_intern.h | 2 + .../blender/editors/gpencil/gpencil_interpolate.c | 13 +- source/blender/editors/gpencil/gpencil_mesh.cc | 4 +- source/blender/editors/gpencil/gpencil_ops.c | 2 + .../editors/gpencil/gpencil_ops_versioning.c | 2 +- source/blender/editors/gpencil/gpencil_paint.c | 103 +- source/blender/editors/gpencil/gpencil_primitive.c | 8 +- .../blender/editors/gpencil/gpencil_sculpt_paint.c | 14 +- source/blender/editors/gpencil/gpencil_select.c | 2 +- source/blender/editors/gpencil/gpencil_trace_ops.c | 31 +- source/blender/editors/gpencil/gpencil_utils.c | 19 +- .../blender/editors/gpencil/gpencil_vertex_ops.c | 2 +- source/blender/editors/include/ED_anim_api.h | 21 +- source/blender/editors/include/ED_armature.h | 9 +- source/blender/editors/include/ED_curves.h | 6 +- source/blender/editors/include/ED_curves_sculpt.h | 25 + source/blender/editors/include/ED_fileselect.h | 8 + source/blender/editors/include/ED_gpencil.h | 8 +- source/blender/editors/include/ED_image.h | 5 +- source/blender/editors/include/ED_keyframes_edit.h | 5 +- source/blender/editors/include/ED_mesh.h | 45 +- source/blender/editors/include/ED_object.h | 16 +- source/blender/editors/include/ED_paint.h | 7 +- source/blender/editors/include/ED_screen.h | 6 +- source/blender/editors/include/ED_screen_types.h | 2 +- source/blender/editors/include/ED_sculpt.h | 11 +- source/blender/editors/include/ED_space_api.h | 2 +- source/blender/editors/include/ED_transform.h | 3 +- .../include/ED_transform_snap_object_context.h | 6 +- source/blender/editors/include/ED_types.h | 27 - source/blender/editors/include/ED_undo.h | 8 +- source/blender/editors/include/ED_uvedit.h | 45 +- source/blender/editors/include/ED_view3d.h | 36 +- source/blender/editors/include/UI_abstract_view.hh | 217 +- source/blender/editors/include/UI_grid_view.hh | 35 +- source/blender/editors/include/UI_interface.h | 85 +- source/blender/editors/include/UI_interface.hh | 13 +- source/blender/editors/include/UI_resources.h | 2 + source/blender/editors/include/UI_tree_view.hh | 151 +- source/blender/editors/include/UI_view2d.h | 14 +- source/blender/editors/interface/CMakeLists.txt | 42 +- source/blender/editors/interface/abstract_view.cc | 102 - .../interface/eyedroppers/eyedropper_color.c | 554 +++ .../interface/eyedroppers/eyedropper_colorband.c | 367 ++ .../interface/eyedroppers/eyedropper_datablock.c | 378 ++ .../interface/eyedroppers/eyedropper_depth.c | 381 ++ .../interface/eyedroppers/eyedropper_driver.c | 221 + .../eyedroppers/eyedropper_gpencil_color.c | 373 ++ .../interface/eyedroppers/eyedropper_intern.h | 57 + .../interface/eyedroppers/interface_eyedropper.c | 161 + source/blender/editors/interface/grid_view.cc | 496 -- source/blender/editors/interface/interface.cc | 102 +- source/blender/editors/interface/interface_anim.c | 348 -- source/blender/editors/interface/interface_anim.cc | 355 ++ .../editors/interface/interface_context_menu.c | 18 +- source/blender/editors/interface/interface_drag.cc | 10 +- source/blender/editors/interface/interface_draw.c | 50 +- .../editors/interface/interface_dropboxes.cc | 32 +- .../editors/interface/interface_eyedropper.c | 161 - .../editors/interface/interface_eyedropper_color.c | 554 --- .../interface/interface_eyedropper_colorband.c | 367 -- .../interface/interface_eyedropper_datablock.c | 378 -- .../editors/interface/interface_eyedropper_depth.c | 381 -- .../interface/interface_eyedropper_driver.c | 220 - .../interface/interface_eyedropper_gpencil_color.c | 373 -- .../interface/interface_eyedropper_intern.h | 57 - .../blender/editors/interface/interface_handlers.c | 152 +- source/blender/editors/interface/interface_icons.c | 10 +- .../editors/interface/interface_icons_event.c | 100 +- .../blender/editors/interface/interface_intern.h | 35 +- .../blender/editors/interface/interface_layout.c | 9 +- source/blender/editors/interface/interface_ops.c | 2259 --------- source/blender/editors/interface/interface_ops.cc | 2572 ++++++++++ source/blender/editors/interface/interface_panel.c | 2602 ---------- .../blender/editors/interface/interface_panel.cc | 2581 ++++++++++ .../blender/editors/interface/interface_query.cc | 32 +- .../interface/interface_region_color_picker.cc | 4 +- .../editors/interface/interface_region_menu_pie.cc | 5 +- .../interface/interface_region_menu_popup.cc | 4 +- .../editors/interface/interface_region_popover.cc | 4 +- .../editors/interface/interface_region_popup.cc | 4 +- .../editors/interface/interface_region_search.cc | 26 +- .../editors/interface/interface_region_tooltip.c | 1558 ------ .../editors/interface/interface_region_tooltip.cc | 1476 ++++++ .../blender/editors/interface/interface_regions.cc | 4 +- .../editors/interface/interface_regions_intern.h | 25 - .../editors/interface/interface_regions_intern.hh | 18 + .../interface/interface_template_asset_view.cc | 4 +- .../interface_template_attribute_search.cc | 6 +- .../editors/interface/interface_template_list.cc | 22 +- .../interface/interface_template_search_menu.cc | 1 + .../interface/interface_template_search_operator.c | 129 - .../interface_template_search_operator.cc | 131 + .../editors/interface/interface_templates.c | 209 +- source/blender/editors/interface/interface_undo.c | 112 - source/blender/editors/interface/interface_undo.cc | 113 + .../blender/editors/interface/interface_utils.cc | 2 +- source/blender/editors/interface/interface_view.cc | 191 - .../blender/editors/interface/interface_widgets.c | 96 +- source/blender/editors/interface/resources.c | 6 + source/blender/editors/interface/tree_view.cc | 838 ---- source/blender/editors/interface/view2d.cc | 91 +- source/blender/editors/interface/view2d_draw.cc | 2 +- source/blender/editors/interface/view2d_ops.cc | 6 +- .../editors/interface/views/abstract_view.cc | 109 + .../editors/interface/views/abstract_view_item.cc | 373 ++ .../blender/editors/interface/views/grid_view.cc | 463 ++ .../editors/interface/views/interface_view.cc | 196 + .../blender/editors/interface/views/tree_view.cc | 555 +++ source/blender/editors/io/CMakeLists.txt | 6 +- source/blender/editors/io/io_alembic.c | 40 +- source/blender/editors/io/io_collada.c | 32 +- source/blender/editors/io/io_gpencil_export.c | 32 +- source/blender/editors/io/io_gpencil_import.c | 45 +- source/blender/editors/io/io_obj.c | 176 +- source/blender/editors/io/io_stl_ops.c | 4 +- source/blender/editors/io/io_usd.c | 48 +- .../blender/editors/lattice/editlattice_select.c | 24 +- source/blender/editors/lattice/editlattice_tools.c | 6 +- source/blender/editors/lattice/editlattice_undo.c | 9 +- source/blender/editors/mask/CMakeLists.txt | 1 - source/blender/editors/mask/mask_draw.c | 10 +- source/blender/editors/mask/mask_query.c | 3 +- source/blender/editors/mask/mask_shapekey.c | 4 +- source/blender/editors/mesh/CMakeLists.txt | 1 - source/blender/editors/mesh/editface.cc | 287 +- source/blender/editors/mesh/editmesh_bevel.c | 4 +- source/blender/editors/mesh/editmesh_bisect.c | 5 +- source/blender/editors/mesh/editmesh_extrude.c | 24 +- .../blender/editors/mesh/editmesh_extrude_screw.c | 9 +- .../blender/editors/mesh/editmesh_extrude_spin.c | 3 +- source/blender/editors/mesh/editmesh_inset.c | 2 +- source/blender/editors/mesh/editmesh_intersect.c | 9 +- source/blender/editors/mesh/editmesh_knife.c | 23 +- .../blender/editors/mesh/editmesh_knife_project.c | 2 +- source/blender/editors/mesh/editmesh_loopcut.c | 4 +- .../blender/editors/mesh/editmesh_mask_extract.c | 8 +- source/blender/editors/mesh/editmesh_path.c | 16 +- source/blender/editors/mesh/editmesh_polybuild.c | 40 +- source/blender/editors/mesh/editmesh_rip.c | 3 +- source/blender/editors/mesh/editmesh_rip_edge.c | 3 +- source/blender/editors/mesh/editmesh_select.c | 80 +- .../blender/editors/mesh/editmesh_select_similar.c | 12 +- source/blender/editors/mesh/editmesh_tools.c | 194 +- source/blender/editors/mesh/editmesh_undo.c | 16 +- source/blender/editors/mesh/editmesh_utils.c | 704 ++- source/blender/editors/mesh/mesh_data.cc | 547 +- source/blender/editors/mesh/mesh_intern.h | 14 +- source/blender/editors/mesh/mesh_mirror.c | 13 +- source/blender/editors/mesh/mesh_ops.c | 8 +- source/blender/editors/mesh/meshtools.cc | 139 +- source/blender/editors/metaball/editmball_undo.c | 7 +- source/blender/editors/metaball/mball_edit.c | 27 +- source/blender/editors/object/CMakeLists.txt | 4 +- source/blender/editors/object/object_add.cc | 133 +- source/blender/editors/object/object_bake.c | 5 +- source/blender/editors/object/object_bake_api.c | 133 +- source/blender/editors/object/object_collection.c | 4 +- source/blender/editors/object/object_constraint.c | 17 +- .../blender/editors/object/object_data_transfer.c | 8 +- .../blender/editors/object/object_data_transform.c | 2 +- source/blender/editors/object/object_edit.c | 73 +- source/blender/editors/object/object_facemap_ops.c | 2 +- .../editors/object/object_gpencil_modifier.c | 3 +- source/blender/editors/object/object_hook.c | 18 +- source/blender/editors/object/object_intern.h | 8 +- source/blender/editors/object/object_modes.c | 10 +- source/blender/editors/object/object_modifier.cc | 88 +- source/blender/editors/object/object_ops.c | 5 +- source/blender/editors/object/object_random.c | 4 +- source/blender/editors/object/object_relations.c | 200 +- source/blender/editors/object/object_remesh.cc | 53 +- source/blender/editors/object/object_select.c | 72 +- source/blender/editors/object/object_shader_fx.c | 9 +- source/blender/editors/object/object_shapekey.c | 46 +- source/blender/editors/object/object_transform.cc | 7 +- source/blender/editors/object/object_utils.c | 9 +- source/blender/editors/object/object_vgroup.c | 4564 ----------------- source/blender/editors/object/object_vgroup.cc | 4614 +++++++++++++++++ source/blender/editors/physics/CMakeLists.txt | 1 - source/blender/editors/physics/dynamicpaint_ops.c | 3 +- source/blender/editors/physics/particle_edit.c | 31 +- .../blender/editors/physics/particle_edit_undo.c | 7 +- source/blender/editors/physics/particle_object.c | 12 +- source/blender/editors/physics/physics_fluid.c | 1 + .../blender/editors/physics/rigidbody_constraint.c | 9 +- source/blender/editors/render/CMakeLists.txt | 1 - source/blender/editors/render/render_internal.cc | 8 +- source/blender/editors/render/render_opengl.cc | 58 +- source/blender/editors/render/render_preview.cc | 78 +- source/blender/editors/render/render_shading.cc | 26 +- source/blender/editors/render/render_update.cc | 26 +- source/blender/editors/render/render_view.cc | 12 +- source/blender/editors/scene/scene_edit.c | 2 +- source/blender/editors/screen/CMakeLists.txt | 1 - source/blender/editors/screen/area.c | 43 +- source/blender/editors/screen/glutil.c | 7 +- source/blender/editors/screen/screen_context.c | 175 +- source/blender/editors/screen/screen_draw.c | 6 +- source/blender/editors/screen/screen_edit.c | 17 +- source/blender/editors/screen/screen_geometry.c | 2 +- source/blender/editors/screen/screen_intern.h | 2 +- source/blender/editors/screen/screen_ops.c | 27 +- source/blender/editors/screen/screen_user_menu.c | 10 +- source/blender/editors/screen/screendump.c | 3 +- source/blender/editors/screen/workspace_edit.c | 23 +- source/blender/editors/sculpt_paint/CMakeLists.txt | 5 +- .../editors/sculpt_paint/curves_sculpt_add.cc | 277 +- .../editors/sculpt_paint/curves_sculpt_brush.cc | 93 +- .../editors/sculpt_paint/curves_sculpt_comb.cc | 107 +- .../editors/sculpt_paint/curves_sculpt_delete.cc | 39 +- .../editors/sculpt_paint/curves_sculpt_density.cc | 422 +- .../sculpt_paint/curves_sculpt_grow_shrink.cc | 102 +- .../editors/sculpt_paint/curves_sculpt_intern.hh | 35 +- .../editors/sculpt_paint/curves_sculpt_ops.cc | 91 +- .../editors/sculpt_paint/curves_sculpt_pinch.cc | 46 +- .../editors/sculpt_paint/curves_sculpt_puff.cc | 54 +- .../sculpt_paint/curves_sculpt_selection.cc | 54 +- .../sculpt_paint/curves_sculpt_selection_paint.cc | 25 +- .../editors/sculpt_paint/curves_sculpt_slide.cc | 485 +- .../editors/sculpt_paint/curves_sculpt_smooth.cc | 106 +- .../sculpt_paint/curves_sculpt_snake_hook.cc | 48 +- source/blender/editors/sculpt_paint/paint_cursor.c | 27 +- source/blender/editors/sculpt_paint/paint_hide.c | 22 +- source/blender/editors/sculpt_paint/paint_image.cc | 23 +- .../blender/editors/sculpt_paint/paint_image_2d.c | 2 +- .../editors/sculpt_paint/paint_image_ops_paint.cc | 6 +- .../editors/sculpt_paint/paint_image_proj.c | 45 +- source/blender/editors/sculpt_paint/paint_intern.h | 9 +- source/blender/editors/sculpt_paint/paint_mask.c | 69 +- source/blender/editors/sculpt_paint/paint_ops.c | 30 +- source/blender/editors/sculpt_paint/paint_stroke.c | 54 +- source/blender/editors/sculpt_paint/paint_utils.c | 86 +- .../blender/editors/sculpt_paint/paint_vertex.cc | 143 +- .../editors/sculpt_paint/paint_vertex_color_ops.cc | 24 +- .../editors/sculpt_paint/paint_vertex_weight_ops.c | 69 +- .../sculpt_paint/paint_vertex_weight_utils.c | 2 +- source/blender/editors/sculpt_paint/sculpt.c | 706 +-- .../editors/sculpt_paint/sculpt_automasking.cc | 95 +- .../blender/editors/sculpt_paint/sculpt_boundary.c | 321 +- .../editors/sculpt_paint/sculpt_brush_types.c | 146 +- source/blender/editors/sculpt_paint/sculpt_cloth.c | 73 +- .../blender/editors/sculpt_paint/sculpt_detail.c | 12 +- .../blender/editors/sculpt_paint/sculpt_dyntopo.c | 60 +- .../blender/editors/sculpt_paint/sculpt_expand.c | 377 +- .../blender/editors/sculpt_paint/sculpt_face_set.c | 1448 ------ .../editors/sculpt_paint/sculpt_face_set.cc | 1463 ++++++ .../editors/sculpt_paint/sculpt_filter_color.c | 21 +- .../editors/sculpt_paint/sculpt_filter_mask.c | 29 +- .../editors/sculpt_paint/sculpt_filter_mesh.c | 42 +- .../blender/editors/sculpt_paint/sculpt_geodesic.c | 80 +- .../blender/editors/sculpt_paint/sculpt_intern.h | 169 +- .../editors/sculpt_paint/sculpt_mask_expand.c | 58 +- .../editors/sculpt_paint/sculpt_mask_init.c | 4 +- .../sculpt_paint/sculpt_multiplane_scrape.c | 6 +- source/blender/editors/sculpt_paint/sculpt_ops.c | 299 +- .../editors/sculpt_paint/sculpt_paint_color.c | 73 +- .../editors/sculpt_paint/sculpt_paint_image.cc | 12 +- source/blender/editors/sculpt_paint/sculpt_pose.c | 108 +- .../blender/editors/sculpt_paint/sculpt_smooth.c | 87 +- .../editors/sculpt_paint/sculpt_transform.c | 18 +- source/blender/editors/sculpt_paint/sculpt_undo.c | 212 +- source/blender/editors/sculpt_paint/sculpt_uv.c | 438 +- source/blender/editors/sound/sound_ops.c | 15 +- source/blender/editors/space_action/CMakeLists.txt | 1 - source/blender/editors/space_action/action_data.c | 5 +- source/blender/editors/space_action/action_draw.c | 4 +- source/blender/editors/space_action/action_edit.c | 18 +- .../blender/editors/space_action/action_select.c | 11 +- source/blender/editors/space_action/space_action.c | 16 +- source/blender/editors/space_api/spacetypes.c | 1 + .../blender/editors/space_buttons/CMakeLists.txt | 3 +- .../editors/space_buttons/buttons_context.c | 19 +- .../blender/editors/space_buttons/buttons_intern.h | 2 +- source/blender/editors/space_buttons/buttons_ops.c | 6 + .../editors/space_buttons/buttons_texture.c | 3 +- .../blender/editors/space_buttons/space_buttons.c | 11 +- source/blender/editors/space_clip/CMakeLists.txt | 1 - source/blender/editors/space_clip/clip_buttons.c | 2 +- .../editors/space_clip/clip_dopesheet_draw.c | 4 +- source/blender/editors/space_clip/clip_draw.c | 26 +- .../blender/editors/space_clip/clip_graph_draw.c | 2 +- source/blender/editors/space_clip/clip_utils.c | 2 +- source/blender/editors/space_clip/space_clip.c | 16 +- source/blender/editors/space_clip/tracking_ops.c | 1 + .../editors/space_clip/tracking_ops_orient.c | 7 +- .../blender/editors/space_console/CMakeLists.txt | 1 - .../blender/editors/space_console/console_draw.c | 2 +- .../blender/editors/space_console/console_intern.h | 2 +- source/blender/editors/space_console/console_ops.c | 14 +- .../blender/editors/space_console/space_console.c | 11 +- source/blender/editors/space_file/CMakeLists.txt | 4 +- .../editors/space_file/asset_catalog_tree_view.cc | 58 +- source/blender/editors/space_file/file_draw.c | 14 +- source/blender/editors/space_file/file_intern.h | 23 +- source/blender/editors/space_file/file_ops.c | 176 +- source/blender/editors/space_file/filelist.c | 4121 ---------------- source/blender/editors/space_file/filelist.cc | 3956 +++++++++++++++ source/blender/editors/space_file/filelist.h | 11 - source/blender/editors/space_file/filesel.c | 30 +- .../blender/editors/space_file/folder_history.cc | 191 + source/blender/editors/space_file/fsmenu.c | 21 +- source/blender/editors/space_file/fsmenu.h | 7 +- source/blender/editors/space_file/space_file.c | 8 +- source/blender/editors/space_graph/CMakeLists.txt | 1 - source/blender/editors/space_graph/graph_buttons.c | 11 +- source/blender/editors/space_graph/graph_draw.c | 16 +- source/blender/editors/space_graph/graph_edit.c | 20 +- source/blender/editors/space_graph/graph_select.c | 9 +- source/blender/editors/space_graph/space_graph.c | 11 +- source/blender/editors/space_image/CMakeLists.txt | 3 +- source/blender/editors/space_image/image_buttons.c | 10 +- source/blender/editors/space_image/image_draw.c | 37 +- source/blender/editors/space_image/image_edit.c | 14 +- source/blender/editors/space_image/image_ops.c | 58 +- source/blender/editors/space_image/image_undo.c | 1093 ---- source/blender/editors/space_image/image_undo.cc | 1137 +++++ source/blender/editors/space_image/space_image.c | 24 +- source/blender/editors/space_info/CMakeLists.txt | 1 - source/blender/editors/space_info/info_stats.cc | 83 +- source/blender/editors/space_info/space_info.c | 6 +- source/blender/editors/space_info/textview.c | 4 +- source/blender/editors/space_nla/CMakeLists.txt | 1 - source/blender/editors/space_nla/nla_buttons.c | 11 +- source/blender/editors/space_nla/nla_channels.c | 6 +- source/blender/editors/space_nla/nla_draw.c | 42 +- source/blender/editors/space_nla/nla_edit.c | 66 +- source/blender/editors/space_nla/nla_ops.c | 24 + source/blender/editors/space_nla/nla_select.c | 2 +- source/blender/editors/space_nla/space_nla.c | 14 +- source/blender/editors/space_node/CMakeLists.txt | 6 +- .../blender/editors/space_node/add_node_search.cc | 312 ++ source/blender/editors/space_node/drawnode.cc | 582 ++- .../blender/editors/space_node/link_drag_search.cc | 177 +- source/blender/editors/space_node/node_add.cc | 348 +- .../editors/space_node/node_context_path.cc | 31 +- source/blender/editors/space_node/node_draw.cc | 586 +-- source/blender/editors/space_node/node_edit.cc | 235 +- .../space_node/node_geometry_attribute_search.cc | 65 +- source/blender/editors/space_node/node_gizmo.cc | 45 +- source/blender/editors/space_node/node_group.cc | 43 +- source/blender/editors/space_node/node_intern.hh | 53 +- source/blender/editors/space_node/node_ops.cc | 5 +- .../editors/space_node/node_relationships.cc | 533 +- source/blender/editors/space_node/node_select.cc | 248 +- .../blender/editors/space_node/node_templates.cc | 67 +- source/blender/editors/space_node/node_view.cc | 22 +- source/blender/editors/space_node/space_node.cc | 16 +- .../blender/editors/space_outliner/CMakeLists.txt | 3 +- .../editors/space_outliner/outliner_collections.cc | 158 +- .../editors/space_outliner/outliner_context.cc | 4 +- .../editors/space_outliner/outliner_dragdrop.cc | 77 +- .../editors/space_outliner/outliner_draw.cc | 189 +- .../editors/space_outliner/outliner_edit.cc | 164 +- .../editors/space_outliner/outliner_intern.hh | 115 +- .../blender/editors/space_outliner/outliner_ops.cc | 5 + .../editors/space_outliner/outliner_query.cc | 4 +- .../editors/space_outliner/outliner_select.cc | 177 +- .../editors/space_outliner/outliner_sync.cc | 42 +- .../editors/space_outliner/outliner_tools.cc | 1403 ++++-- .../editors/space_outliner/outliner_tree.cc | 152 +- .../editors/space_outliner/outliner_utils.cc | 39 +- .../editors/space_outliner/space_outliner.cc | 37 +- .../blender/editors/space_outliner/tree/common.cc | 6 +- .../blender/editors/space_outliner/tree/common.hh | 4 + .../editors/space_outliner/tree/tree_display.cc | 5 + .../editors/space_outliner/tree/tree_display.hh | 16 +- .../space_outliner/tree/tree_display_data.cc | 5 + .../tree_display_override_library_hierarchies.cc | 89 +- .../space_outliner/tree/tree_display_view_layer.cc | 5 +- .../editors/space_outliner/tree/tree_element.cc | 43 +- .../editors/space_outliner/tree/tree_element.hh | 24 +- .../space_outliner/tree/tree_element_anim_data.hh | 2 - .../space_outliner/tree/tree_element_driver.hh | 2 - .../space_outliner/tree/tree_element_label.cc | 36 + .../space_outliner/tree/tree_element_label.hh | 36 + .../space_outliner/tree/tree_element_overrides.cc | 393 +- .../space_outliner/tree/tree_element_overrides.hh | 40 +- .../space_outliner/tree/tree_element_rna.cc | 15 +- .../editors/space_outliner/tree/tree_iterator.hh | 4 +- source/blender/editors/space_script/CMakeLists.txt | 1 - source/blender/editors/space_script/script_edit.c | 2 +- source/blender/editors/space_script/space_script.c | 2 +- .../blender/editors/space_sequencer/CMakeLists.txt | 1 - .../editors/space_sequencer/sequencer_drag_drop.c | 295 +- .../editors/space_sequencer/sequencer_draw.c | 547 +- .../editors/space_sequencer/sequencer_edit.c | 28 +- .../editors/space_sequencer/sequencer_scopes.c | 2 +- .../editors/space_sequencer/sequencer_view.c | 12 +- .../editors/space_sequencer/space_sequencer.c | 12 +- .../editors/space_spreadsheet/CMakeLists.txt | 1 - .../editors/space_spreadsheet/space_spreadsheet.cc | 8 +- .../spreadsheet_data_source_geometry.cc | 233 +- .../spreadsheet_data_source_geometry.hh | 4 - .../space_spreadsheet/spreadsheet_dataset_draw.cc | 2 +- .../editors/space_spreadsheet/spreadsheet_draw.cc | 2 +- .../space_spreadsheet/spreadsheet_layout.cc | 4 +- .../space_spreadsheet/spreadsheet_row_filter.cc | 9 +- .../blender/editors/space_statusbar/CMakeLists.txt | 1 - .../editors/space_statusbar/space_statusbar.c | 4 +- source/blender/editors/space_text/CMakeLists.txt | 1 - source/blender/editors/space_text/space_text.c | 7 +- .../blender/editors/space_text/text_autocomplete.c | 2 +- source/blender/editors/space_text/text_draw.c | 24 +- source/blender/editors/space_text/text_format_py.c | 2 +- source/blender/editors/space_text/text_ops.c | 19 +- source/blender/editors/space_topbar/CMakeLists.txt | 1 - source/blender/editors/space_topbar/space_topbar.c | 6 +- .../editors/space_userpref/space_userpref.c | 2 +- source/blender/editors/space_view3d/CMakeLists.txt | 5 +- source/blender/editors/space_view3d/drawobject.c | 25 +- source/blender/editors/space_view3d/space_view3d.c | 40 +- .../blender/editors/space_view3d/view3d_buttons.c | 32 +- .../editors/space_view3d/view3d_cursor_snap.c | 151 +- source/blender/editors/space_view3d/view3d_draw.c | 29 +- source/blender/editors/space_view3d/view3d_edit.c | 6 +- .../editors/space_view3d/view3d_gizmo_armature.c | 14 +- .../editors/space_view3d/view3d_gizmo_camera.c | 20 +- .../editors/space_view3d/view3d_gizmo_empty.c | 8 +- .../editors/space_view3d/view3d_gizmo_forcefield.c | 8 +- .../editors/space_view3d/view3d_gizmo_light.c | 24 +- .../space_view3d/view3d_gizmo_preselect_type.c | 17 +- .../editors/space_view3d/view3d_gizmo_ruler.c | 8 +- .../blender/editors/space_view3d/view3d_header.c | 9 +- .../blender/editors/space_view3d/view3d_intern.h | 10 +- .../editors/space_view3d/view3d_iterators.c | 27 +- .../blender/editors/space_view3d/view3d_navigate.c | 112 +- .../blender/editors/space_view3d/view3d_navigate.h | 24 + .../editors/space_view3d/view3d_navigate_dolly.c | 3 +- .../editors/space_view3d/view3d_navigate_fly.c | 8 +- .../editors/space_view3d/view3d_navigate_move.c | 3 +- .../editors/space_view3d/view3d_navigate_ndof.c | 7 +- .../editors/space_view3d/view3d_navigate_roll.c | 13 +- .../editors/space_view3d/view3d_navigate_rotate.c | 3 +- .../space_view3d/view3d_navigate_smoothview.c | 278 +- .../editors/space_view3d/view3d_navigate_walk.c | 10 +- .../editors/space_view3d/view3d_navigate_zoom.c | 4 +- .../space_view3d/view3d_navigate_zoom_border.c | 11 +- .../blender/editors/space_view3d/view3d_select.c | 4763 ------------------ .../blender/editors/space_view3d/view3d_select.cc | 4798 ++++++++++++++++++ source/blender/editors/space_view3d/view3d_snap.c | 14 +- source/blender/editors/space_view3d/view3d_utils.c | 60 + source/blender/editors/space_view3d/view3d_view.c | 54 +- source/blender/editors/transform/CMakeLists.txt | 2 +- source/blender/editors/transform/transform.c | 26 +- source/blender/editors/transform/transform.h | 47 +- .../editors/transform/transform_constraints.c | 44 +- .../editors/transform/transform_constraints.h | 2 +- .../blender/editors/transform/transform_convert.c | 750 +-- .../blender/editors/transform/transform_convert.h | 137 +- .../editors/transform/transform_convert_action.c | 24 +- .../editors/transform/transform_convert_armature.c | 26 +- .../editors/transform/transform_convert_cursor.c | 33 +- .../editors/transform/transform_convert_curve.c | 11 +- .../editors/transform/transform_convert_gpencil.c | 15 +- .../editors/transform/transform_convert_graph.c | 31 +- .../editors/transform/transform_convert_lattice.c | 11 +- .../editors/transform/transform_convert_mask.c | 13 +- .../editors/transform/transform_convert_mball.c | 11 +- .../editors/transform/transform_convert_mesh.c | 42 +- .../transform/transform_convert_mesh_edge.c | 19 +- .../transform/transform_convert_mesh_skin.c | 11 +- .../editors/transform/transform_convert_mesh_uv.c | 54 +- .../transform/transform_convert_mesh_vert_cdata.c | 298 ++ .../editors/transform/transform_convert_nla.c | 13 +- .../editors/transform/transform_convert_node.c | 49 +- .../editors/transform/transform_convert_object.c | 70 +- .../transform/transform_convert_object_texspace.c | 15 +- .../transform/transform_convert_paintcurve.c | 11 +- .../editors/transform/transform_convert_particle.c | 20 +- .../editors/transform/transform_convert_sculpt.c | 25 +- .../transform/transform_convert_sequencer.c | 17 +- .../transform/transform_convert_sequencer_image.c | 13 +- .../editors/transform/transform_convert_tracking.c | 13 +- .../editors/transform/transform_draw_cursors.c | 2 +- .../blender/editors/transform/transform_generics.c | 97 +- .../blender/editors/transform/transform_gizmo_2d.c | 2 +- .../blender/editors/transform/transform_gizmo_3d.c | 17 +- .../editors/transform/transform_gizmo_extrude_3d.c | 4 +- source/blender/editors/transform/transform_input.c | 69 +- source/blender/editors/transform/transform_mode.c | 13 +- source/blender/editors/transform/transform_mode.h | 2 + .../editors/transform/transform_mode_bend.c | 2 +- .../transform/transform_mode_curveshrinkfatten.c | 10 +- .../transform/transform_mode_edge_bevelweight.c | 11 +- .../editors/transform/transform_mode_edge_crease.c | 11 +- .../editors/transform/transform_mode_edge_slide.c | 160 +- .../editors/transform/transform_mode_gpopacity.c | 2 +- .../transform/transform_mode_gpshrinkfatten.c | 2 +- .../transform/transform_mode_maskshrinkfatten.c | 2 +- .../editors/transform/transform_mode_resize.c | 95 +- .../editors/transform/transform_mode_rotate.c | 83 +- .../editors/transform/transform_mode_translate.c | 45 +- .../editors/transform/transform_mode_vert_slide.c | 26 +- source/blender/editors/transform/transform_ops.c | 62 +- .../editors/transform/transform_orientations.c | 19 +- .../editors/transform/transform_orientations.h | 3 +- source/blender/editors/transform/transform_snap.c | 244 +- .../editors/transform/transform_snap_object.cc | 43 +- .../editors/transform/transform_snap_sequencer.c | 5 +- source/blender/editors/undo/CMakeLists.txt | 1 + source/blender/editors/undo/ed_undo.c | 54 +- source/blender/editors/util/CMakeLists.txt | 2 - source/blender/editors/util/ed_draw.c | 24 +- source/blender/editors/util/ed_util.c | 21 +- source/blender/editors/util/ed_util_imbuf.c | 4 +- source/blender/editors/util/numinput.c | 13 +- source/blender/editors/util/select_utils.c | 16 +- source/blender/editors/uvedit/CMakeLists.txt | 3 +- source/blender/editors/uvedit/uvedit_buttons.c | 4 +- source/blender/editors/uvedit/uvedit_draw.c | 2 +- source/blender/editors/uvedit/uvedit_intern.h | 3 - source/blender/editors/uvedit/uvedit_islands.c | 653 --- source/blender/editors/uvedit/uvedit_islands.cc | 640 +++ source/blender/editors/uvedit/uvedit_ops.c | 67 +- source/blender/editors/uvedit/uvedit_path.c | 313 +- source/blender/editors/uvedit/uvedit_rip.c | 6 +- source/blender/editors/uvedit/uvedit_select.c | 432 +- .../blender/editors/uvedit/uvedit_smart_stitch.c | 193 +- source/blender/editors/uvedit/uvedit_unwrap_ops.c | 146 +- source/blender/freestyle/CMakeLists.txt | 5 +- .../freestyle/intern/application/Controller.h | 4 + .../intern/blender_interface/BlenderFileLoader.cpp | 46 +- .../blender_interface/BlenderStrokeRenderer.cpp | 82 +- .../blender/freestyle/intern/geometry/FastGrid.h | 4 +- source/blender/freestyle/intern/geometry/Grid.h | 2 +- .../blender/freestyle/intern/geometry/HashGrid.h | 2 +- .../blender/freestyle/intern/view_map/ViewMap.cpp | 2 +- source/blender/functions/CMakeLists.txt | 9 + source/blender/functions/FN_field.hh | 22 + source/blender/functions/FN_field_cpp_type.hh | 2 +- source/blender/functions/FN_lazy_function.hh | 442 ++ .../blender/functions/FN_lazy_function_execute.hh | 125 + source/blender/functions/FN_lazy_function_graph.hh | 421 ++ .../functions/FN_lazy_function_graph_executor.hh | 98 + source/blender/functions/FN_multi_function.hh | 1 + .../blender/functions/FN_multi_function_builder.hh | 5 + source/blender/functions/intern/cpp_types.cc | 3 + source/blender/functions/intern/field.cc | 48 +- source/blender/functions/intern/lazy_function.cc | 71 + .../functions/intern/lazy_function_execute.cc | 70 + .../functions/intern/lazy_function_graph.cc | 181 + .../intern/lazy_function_graph_executor.cc | 1240 +++++ .../functions/tests/FN_lazy_function_test.cc | 115 + source/blender/geometry/CMakeLists.txt | 6 +- source/blender/geometry/GEO_add_curves_on_mesh.hh | 29 +- source/blender/geometry/GEO_fillet_curves.hh | 23 + source/blender/geometry/GEO_mesh_to_curve.hh | 17 +- .../geometry/GEO_point_merge_by_distance.hh | 2 +- source/blender/geometry/GEO_resample_curves.hh | 31 +- source/blender/geometry/GEO_set_curve_type.hh | 18 +- source/blender/geometry/GEO_subdivide_curves.hh | 10 +- source/blender/geometry/GEO_trim_curves.hh | 36 + source/blender/geometry/GEO_uv_parametrizer.h | 9 +- .../blender/geometry/intern/add_curves_on_mesh.cc | 216 +- source/blender/geometry/intern/fillet_curves.cc | 561 +++ .../geometry/intern/mesh_merge_by_distance.cc | 73 +- .../geometry/intern/mesh_primitive_cuboid.cc | 11 +- .../geometry/intern/mesh_to_curve_convert.cc | 47 +- source/blender/geometry/intern/mesh_to_volume.cc | 9 +- .../geometry/intern/point_merge_by_distance.cc | 22 +- .../blender/geometry/intern/realize_instances.cc | 240 +- source/blender/geometry/intern/resample_curves.cc | 274 +- .../blender/geometry/intern/reverse_uv_sampler.cc | 20 +- source/blender/geometry/intern/set_curve_type.cc | 200 +- source/blender/geometry/intern/subdivide_curves.cc | 99 +- source/blender/geometry/intern/trim_curves.cc | 1299 +++++ source/blender/geometry/intern/uv_parametrizer.c | 4357 ---------------- source/blender/geometry/intern/uv_parametrizer.cc | 4453 +++++++++++++++++ source/blender/gpencil_modifiers/CMakeLists.txt | 2 - .../gpencil_modifiers/intern/MOD_gpencilarmature.c | 2 +- .../gpencil_modifiers/intern/MOD_gpencilarray.c | 2 +- .../gpencil_modifiers/intern/MOD_gpencilbuild.c | 2 +- .../gpencil_modifiers/intern/MOD_gpencilcolor.c | 4 +- .../gpencil_modifiers/intern/MOD_gpencildash.c | 4 +- .../gpencil_modifiers/intern/MOD_gpencilenvelope.c | 2 +- .../gpencil_modifiers/intern/MOD_gpencilhook.c | 2 +- .../gpencil_modifiers/intern/MOD_gpencillattice.c | 2 +- .../gpencil_modifiers/intern/MOD_gpencillength.c | 2 +- .../gpencil_modifiers/intern/MOD_gpencillineart.c | 24 +- .../gpencil_modifiers/intern/MOD_gpencilmirror.c | 14 +- .../gpencil_modifiers/intern/MOD_gpencilmultiply.c | 4 +- .../gpencil_modifiers/intern/MOD_gpencilnoise.c | 2 +- .../gpencil_modifiers/intern/MOD_gpenciloffset.c | 2 +- .../gpencil_modifiers/intern/MOD_gpencilopacity.c | 2 +- .../intern/MOD_gpencilshrinkwrap.c | 7 +- .../gpencil_modifiers/intern/MOD_gpencilsimplify.c | 4 +- .../gpencil_modifiers/intern/MOD_gpencilsmooth.c | 2 +- .../gpencil_modifiers/intern/MOD_gpencilsubdiv.c | 2 +- .../gpencil_modifiers/intern/MOD_gpenciltexture.c | 2 +- .../gpencil_modifiers/intern/MOD_gpencilthick.c | 4 +- .../gpencil_modifiers/intern/MOD_gpenciltime.c | 2 +- .../gpencil_modifiers/intern/MOD_gpenciltint.c | 4 +- .../intern/MOD_gpencilweight_angle.c | 4 +- .../intern/MOD_gpencilweight_proximity.c | 4 +- .../gpencil_modifiers/intern/lineart/MOD_lineart.h | 20 +- .../intern/lineart/lineart_chain.c | 32 + .../gpencil_modifiers/intern/lineart/lineart_cpu.c | 174 +- .../intern/lineart/lineart_intern.h | 1 + .../intern/lineart/lineart_shadow.c | 9 +- source/blender/gpu/CMakeLists.txt | 200 +- source/blender/gpu/GPU_batch.h | 31 +- source/blender/gpu/GPU_buffers.h | 12 +- source/blender/gpu/GPU_capabilities.h | 6 + source/blender/gpu/GPU_compute.h | 2 +- source/blender/gpu/GPU_context.h | 8 +- source/blender/gpu/GPU_framebuffer.h | 6 + source/blender/gpu/GPU_glew.h | 15 - source/blender/gpu/GPU_index_buffer.h | 7 +- source/blender/gpu/GPU_legacy_stubs.h | 497 -- source/blender/gpu/GPU_material.h | 60 +- source/blender/gpu/GPU_primitive.h | 74 + source/blender/gpu/GPU_shader.h | 40 +- source/blender/gpu/GPU_shader_shared_utils.h | 29 +- source/blender/gpu/GPU_storage_buffer.h | 7 + source/blender/gpu/GPU_texture.h | 8 +- source/blender/gpu/GPU_vertex_buffer.h | 16 +- source/blender/gpu/intern/gpu_backend.hh | 1 + source/blender/gpu/intern/gpu_batch.cc | 50 + source/blender/gpu/intern/gpu_batch_private.hh | 5 + source/blender/gpu/intern/gpu_buffers.c | 106 +- source/blender/gpu/intern/gpu_capabilities.cc | 25 + .../blender/gpu/intern/gpu_capabilities_private.hh | 1 + source/blender/gpu/intern/gpu_codegen.cc | 96 +- source/blender/gpu/intern/gpu_context.cc | 53 +- source/blender/gpu/intern/gpu_context_private.hh | 28 +- source/blender/gpu/intern/gpu_framebuffer.cc | 5 + .../blender/gpu/intern/gpu_framebuffer_private.hh | 4 +- source/blender/gpu/intern/gpu_immediate.cc | 3 - source/blender/gpu/intern/gpu_immediate_private.hh | 6 +- source/blender/gpu/intern/gpu_immediate_util.c | 6 +- source/blender/gpu/intern/gpu_index_buffer.cc | 105 +- .../blender/gpu/intern/gpu_index_buffer_private.hh | 21 +- source/blender/gpu/intern/gpu_init_exit.c | 2 + source/blender/gpu/intern/gpu_material.c | 135 +- source/blender/gpu/intern/gpu_node_graph.c | 165 +- source/blender/gpu/intern/gpu_node_graph.h | 10 + source/blender/gpu/intern/gpu_platform.cc | 14 +- source/blender/gpu/intern/gpu_private.h | 4 + source/blender/gpu/intern/gpu_shader.cc | 48 + source/blender/gpu/intern/gpu_shader_builder.cc | 1 - .../blender/gpu/intern/gpu_shader_builder_stubs.cc | 80 +- source/blender/gpu/intern/gpu_shader_builtin.c | 39 +- .../blender/gpu/intern/gpu_shader_create_info.cc | 18 +- .../blender/gpu/intern/gpu_shader_create_info.hh | 128 +- source/blender/gpu/intern/gpu_shader_dependency.cc | 268 +- source/blender/gpu/intern/gpu_shader_interface.hh | 47 +- source/blender/gpu/intern/gpu_shader_log.cc | 1 + source/blender/gpu/intern/gpu_shader_private.hh | 2 - source/blender/gpu/intern/gpu_storage_buffer.cc | 5 + .../gpu/intern/gpu_storage_buffer_private.hh | 3 +- source/blender/gpu/intern/gpu_texture.cc | 149 +- source/blender/gpu/intern/gpu_texture_private.hh | 8 +- .../gpu/intern/gpu_uniform_buffer_private.hh | 2 +- source/blender/gpu/intern/gpu_vertex_buffer.cc | 22 +- .../gpu/intern/gpu_vertex_buffer_private.hh | 12 +- source/blender/gpu/intern/gpu_vertex_format.cc | 119 +- .../blender/gpu/intern/gpu_vertex_format_private.h | 1 + .../gpu/metal/kernels/compute_texture_read.msl | 2 +- .../gpu/metal/kernels/compute_texture_update.msl | 16 - .../metal/kernels/depth_2d_update_float_frag.glsl | 5 - .../gpu/metal/kernels/depth_2d_update_info.hh | 35 + .../metal/kernels/depth_2d_update_int24_frag.glsl | 4 - .../metal/kernels/depth_2d_update_int32_frag.glsl | 5 - .../gpu/metal/kernels/depth_2d_update_vert.glsl | 6 - .../kernels/gpu_shader_fullscreen_blit_frag.glsl | 5 - .../kernels/gpu_shader_fullscreen_blit_info.hh | 23 + .../kernels/gpu_shader_fullscreen_blit_vert.glsl | 8 - source/blender/gpu/metal/mtl_backend.hh | 6 +- source/blender/gpu/metal/mtl_backend.mm | 25 +- source/blender/gpu/metal/mtl_capabilities.hh | 2 + source/blender/gpu/metal/mtl_command_buffer.mm | 16 +- source/blender/gpu/metal/mtl_common.hh | 2 + source/blender/gpu/metal/mtl_context.hh | 53 +- source/blender/gpu/metal/mtl_context.mm | 209 +- source/blender/gpu/metal/mtl_framebuffer.hh | 36 +- source/blender/gpu/metal/mtl_framebuffer.mm | 20 +- source/blender/gpu/metal/mtl_index_buffer.hh | 80 + source/blender/gpu/metal/mtl_index_buffer.mm | 517 ++ source/blender/gpu/metal/mtl_memory.hh | 24 +- source/blender/gpu/metal/mtl_memory.mm | 61 +- source/blender/gpu/metal/mtl_primitive.hh | 100 + .../blender/gpu/metal/mtl_pso_descriptor_state.hh | 250 + source/blender/gpu/metal/mtl_query.hh | 41 + source/blender/gpu/metal/mtl_query.mm | 122 + source/blender/gpu/metal/mtl_shader.hh | 1165 +++++ source/blender/gpu/metal/mtl_shader.mm | 1266 +++++ source/blender/gpu/metal/mtl_shader_generator.hh | 727 +++ source/blender/gpu/metal/mtl_shader_generator.mm | 2980 +++++++++++ source/blender/gpu/metal/mtl_shader_interface.hh | 267 + source/blender/gpu/metal/mtl_shader_interface.mm | 604 +++ .../blender/gpu/metal/mtl_shader_interface_type.hh | 251 + source/blender/gpu/metal/mtl_shader_shared.h | 32 + source/blender/gpu/metal/mtl_state.hh | 14 +- source/blender/gpu/metal/mtl_state.mm | 3 +- source/blender/gpu/metal/mtl_texture.hh | 46 +- source/blender/gpu/metal/mtl_texture.mm | 22 +- source/blender/gpu/metal/mtl_texture_util.mm | 116 +- source/blender/gpu/metal/mtl_uniform_buffer.hh | 50 + source/blender/gpu/metal/mtl_uniform_buffer.mm | 162 + source/blender/gpu/opengl/gl_backend.cc | 112 +- source/blender/gpu/opengl/gl_backend.hh | 12 +- source/blender/gpu/opengl/gl_batch.cc | 52 + source/blender/gpu/opengl/gl_batch.hh | 11 +- source/blender/gpu/opengl/gl_context.cc | 6 +- source/blender/gpu/opengl/gl_context.hh | 3 - source/blender/gpu/opengl/gl_debug.cc | 19 +- source/blender/gpu/opengl/gl_debug.hh | 2 - source/blender/gpu/opengl/gl_framebuffer.cc | 5 + source/blender/gpu/opengl/gl_framebuffer.hh | 8 +- source/blender/gpu/opengl/gl_immediate.hh | 2 - source/blender/gpu/opengl/gl_index_buffer.hh | 12 +- source/blender/gpu/opengl/gl_primitive.hh | 2 - source/blender/gpu/opengl/gl_query.hh | 2 +- source/blender/gpu/opengl/gl_shader.cc | 116 +- source/blender/gpu/opengl/gl_shader.hh | 4 +- source/blender/gpu/opengl/gl_shader_interface.cc | 68 + source/blender/gpu/opengl/gl_shader_interface.hh | 2 - source/blender/gpu/opengl/gl_state.cc | 6 +- source/blender/gpu/opengl/gl_state.hh | 2 +- source/blender/gpu/opengl/gl_storage_buffer.cc | 19 +- source/blender/gpu/opengl/gl_storage_buffer.hh | 3 +- source/blender/gpu/opengl/gl_texture.cc | 9 +- source/blender/gpu/opengl/gl_texture.hh | 6 +- source/blender/gpu/opengl/gl_uniform_buffer.cc | 11 +- source/blender/gpu/opengl/gl_uniform_buffer.hh | 2 - source/blender/gpu/opengl/gl_vertex_array.cc | 15 +- source/blender/gpu/opengl/gl_vertex_array.hh | 2 - source/blender/gpu/opengl/gl_vertex_buffer.hh | 2 - .../common/gpu_shader_common_color_utils.glsl | 119 +- .../shaders/common/gpu_shader_common_curves.glsl | 114 +- .../common/gpu_shader_common_math_utils.glsl | 27 + .../shaders/common/gpu_shader_common_mix_rgb.glsl | 46 +- .../shaders/compositor/compositor_alpha_crop.glsl | 11 + .../compositor/compositor_bilateral_blur.glsl | 31 + .../gpu/shaders/compositor/compositor_blur.glsl | 55 + .../shaders/compositor/compositor_bokeh_image.glsl | 118 + .../shaders/compositor/compositor_box_mask.glsl | 27 + .../gpu/shaders/compositor/compositor_convert.glsl | 8 + .../shaders/compositor/compositor_despeckle.glsl | 70 + .../compositor/compositor_directional_blur.glsl | 21 + .../shaders/compositor/compositor_edge_filter.glsl | 31 + .../compositor/compositor_ellipse_mask.glsl | 27 + .../gpu/shaders/compositor/compositor_filter.glsl | 20 + .../gpu/shaders/compositor/compositor_flip.glsl | 15 + .../shaders/compositor/compositor_image_crop.glsl | 7 + .../compositor_morphological_distance.glsl | 24 + .../compositor_morphological_distance_feather.glsl | 101 + ...ompositor_morphological_distance_threshold.glsl | 88 + .../compositor/compositor_morphological_step.glsl | 19 + .../compositor_projector_lens_distortion.glsl | 16 + .../compositor/compositor_realize_on_domain.glsl | 29 + .../compositor_screen_lens_distortion.glsl | 151 + .../shaders/compositor/compositor_set_alpha.glsl | 8 + .../compositor/compositor_split_viewer.glsl | 14 + .../compositor/compositor_symmetric_blur.glsl | 77 + .../compositor_symmetric_separable_blur.glsl | 53 + .../compositor/infos/compositor_alpha_crop_info.hh | 12 + .../infos/compositor_bilateral_blur_info.hh | 13 + .../compositor/infos/compositor_blur_info.hh | 14 + .../infos/compositor_bokeh_image_info.hh | 14 + .../compositor/infos/compositor_box_mask_info.hh | 35 + .../compositor/infos/compositor_convert_info.hh | 69 + .../compositor/infos/compositor_despeckle_info.hh | 13 + .../infos/compositor_directional_blur_info.hh | 12 + .../infos/compositor_edge_filter_info.hh | 12 + .../infos/compositor_ellipse_mask_info.hh | 35 + .../compositor/infos/compositor_filter_info.hh | 12 + .../compositor/infos/compositor_flip_info.hh | 12 + .../compositor/infos/compositor_image_crop_info.hh | 11 + ...mpositor_morphological_distance_feather_info.hh | 21 + .../compositor_morphological_distance_info.hh | 22 + ...ositor_morphological_distance_threshold_info.hh | 13 + .../infos/compositor_morphological_step_info.hh | 22 + .../compositor_projector_lens_distortion_info.hh | 11 + .../infos/compositor_realize_on_domain_info.hh | 24 + .../compositor_screen_lens_distortion_info.hh | 20 + .../compositor/infos/compositor_set_alpha_info.hh | 11 + .../infos/compositor_split_viewer_info.hh | 22 + .../infos/compositor_symmetric_blur_info.hh | 13 + .../compositor_symmetric_separable_blur_info.hh | 14 + .../library/gpu_shader_compositor_alpha_over.glsl | 48 + .../library/gpu_shader_compositor_blur_common.glsl | 32 + .../gpu_shader_compositor_bright_contrast.glsl | 38 + .../gpu_shader_compositor_channel_matte.glsl | 52 + .../gpu_shader_compositor_chroma_matte.glsl | 43 + .../gpu_shader_compositor_color_balance.glsl | 34 + .../gpu_shader_compositor_color_correction.glsl | 87 + .../library/gpu_shader_compositor_color_matte.glsl | 27 + .../library/gpu_shader_compositor_color_spill.glsl | 13 + .../gpu_shader_compositor_color_to_luminance.glsl | 6 + .../gpu_shader_compositor_difference_matte.glsl | 10 + .../gpu_shader_compositor_distance_matte.glsl | 26 + .../library/gpu_shader_compositor_exposure.glsl | 6 + .../library/gpu_shader_compositor_gamma.glsl | 7 + .../library/gpu_shader_compositor_hue_correct.glsl | 39 + ...gpu_shader_compositor_hue_saturation_value.glsl | 16 + .../library/gpu_shader_compositor_invert.glsl | 13 + .../gpu_shader_compositor_luminance_matte.glsl | 14 + .../library/gpu_shader_compositor_main.glsl | 7 + .../library/gpu_shader_compositor_map_value.glsl | 56 + .../library/gpu_shader_compositor_normal.glsl | 9 + .../library/gpu_shader_compositor_posterize.glsl | 6 + .../gpu_shader_compositor_separate_combine.glsl | 132 + .../library/gpu_shader_compositor_set_alpha.glsl | 9 + .../gpu_shader_compositor_store_output.glsl | 26 + .../gpu_shader_compositor_texture_utilities.glsl | 35 + .../gpu_shader_compositor_type_conversion.glsl | 29 + .../gpu/shaders/gpu_shader_2D_flat_color_vert.glsl | 6 - .../gpu/shaders/gpu_shader_2D_image_vert.glsl | 1 - ...u_shader_2D_line_dashed_uniform_color_vert.glsl | 13 - .../gpu/shaders/gpu_shader_2D_nodelink_frag.glsl | 2 +- .../gpu/shaders/gpu_shader_2D_nodelink_vert.glsl | 58 +- .../shaders/gpu_shader_2D_smooth_color_frag.glsl | 7 - .../shaders/gpu_shader_2D_smooth_color_vert.glsl | 6 - .../gpu/shaders/gpu_shader_codegen_lib.glsl | 4 +- .../gpu_shader_image_modulate_alpha_frag.glsl | 6 - .../shaders/infos/gpu_shader_2D_flat_color_info.hh | 21 - .../infos/gpu_shader_2D_image_color_info.hh | 14 - .../gpu/shaders/infos/gpu_shader_2D_image_info.hh | 5 - .../shaders/infos/gpu_shader_2D_nodelink_info.hh | 1 + .../infos/gpu_shader_2D_smooth_color_info.hh | 20 - .../infos/gpu_shader_2D_uniform_color_info.hh | 18 - .../gpu/shaders/infos/gpu_shader_3D_image_info.hh | 13 +- .../gpu_shader_3D_image_modulate_alpha_info.hh | 21 - ...gpu_shader_3D_line_dashed_uniform_color_info.hh | 18 - .../gpu_shader_line_dashed_uniform_color_info.hh | 15 +- .../material/gpu_shader_material_attribute.glsl | 17 + .../material/gpu_shader_material_displacement.glsl | 2 +- .../material/gpu_shader_material_mix_color.glsl | 537 ++ .../material/gpu_shader_material_noise.glsl | 1 - .../material/gpu_shader_material_normal_map.glsl | 6 +- .../material/gpu_shader_material_principled.glsl | 12 + .../material/gpu_shader_material_tex_musgrave.glsl | 68 +- .../material/gpu_shader_material_tex_sky.glsl | 44 +- .../material/gpu_shader_material_tex_voronoi.glsl | 1 - .../gpu_shader_material_texture_coordinates.glsl | 5 + .../gpu/shaders/metal/mtl_shader_common.msl | 109 + .../gpu/shaders/metal/mtl_shader_defines.msl | 1065 ++++ .../blender/gpu/tests/gpu_shader_builtin_test.cc | 7 +- source/blender/gpu/tests/gpu_shader_test.cc | 6 +- source/blender/gpu/tests/gpu_testing.cc | 1 - source/blender/imbuf/IMB_colormanagement.h | 2 + source/blender/imbuf/IMB_imbuf.h | 78 +- source/blender/imbuf/IMB_imbuf_types.h | 10 +- source/blender/imbuf/intern/IMB_filetype.h | 6 + source/blender/imbuf/intern/allocimbuf.c | 37 +- source/blender/imbuf/intern/anim_movie.c | 11 +- source/blender/imbuf/intern/bmp.c | 3 +- source/blender/imbuf/intern/cache.c | 32 +- source/blender/imbuf/intern/cineon/cineon_dpx.c | 25 +- source/blender/imbuf/intern/cineon/cineonlib.c | 23 +- source/blender/imbuf/intern/cineon/cineonlib.h | 30 +- source/blender/imbuf/intern/cineon/dpxlib.c | 19 +- source/blender/imbuf/intern/cineon/logImageCore.c | 119 +- source/blender/imbuf/intern/cineon/logmemfile.c | 30 +- source/blender/imbuf/intern/colormanagement.c | 128 +- .../blender/imbuf/intern/colormanagement_inline.c | 7 +- source/blender/imbuf/intern/dds/BlockDXT.cpp | 4 +- .../blender/imbuf/intern/dds/DirectDrawSurface.cpp | 8 +- source/blender/imbuf/intern/dds/FlipDXT.cpp | 44 +- source/blender/imbuf/intern/dds/Stream.cpp | 16 +- source/blender/imbuf/intern/dds/dds_api.cpp | 23 +- source/blender/imbuf/intern/divers.c | 42 +- source/blender/imbuf/intern/filetype.c | 2 +- source/blender/imbuf/intern/filter.c | 34 +- source/blender/imbuf/intern/imageprocess.c | 61 +- source/blender/imbuf/intern/indexer.c | 6 +- source/blender/imbuf/intern/iris.c | 2 +- source/blender/imbuf/intern/jp2.c | 46 +- source/blender/imbuf/intern/jpeg.c | 21 +- source/blender/imbuf/intern/moviecache.cc | 2 +- .../blender/imbuf/intern/oiio/openimageio_api.cpp | 6 +- .../blender/imbuf/intern/openexr/openexr_api.cpp | 58 +- source/blender/imbuf/intern/png.c | 60 +- source/blender/imbuf/intern/radiance_hdr.c | 53 +- source/blender/imbuf/intern/readimage.c | 19 +- source/blender/imbuf/intern/rectop.c | 86 +- source/blender/imbuf/intern/rotate.c | 4 +- source/blender/imbuf/intern/scaling.c | 102 +- source/blender/imbuf/intern/stereoimbuf.c | 36 +- source/blender/imbuf/intern/targa.c | 57 +- source/blender/imbuf/intern/thumbs.c | 8 +- source/blender/imbuf/intern/thumbs_font.c | 6 +- source/blender/imbuf/intern/tiff.c | 59 +- source/blender/imbuf/intern/transform.cc | 28 +- source/blender/imbuf/intern/util.c | 11 +- source/blender/imbuf/intern/util_gpu.c | 110 +- source/blender/imbuf/intern/webp.c | 107 +- source/blender/io/alembic/ABC_alembic.h | 30 +- .../io/alembic/exporter/abc_subdiv_disabler.cc | 4 +- .../blender/io/alembic/exporter/abc_writer_hair.cc | 13 +- .../blender/io/alembic/exporter/abc_writer_mesh.cc | 52 +- source/blender/io/alembic/intern/abc_customdata.cc | 13 +- .../blender/io/alembic/intern/abc_reader_mesh.cc | 92 +- source/blender/io/alembic/intern/abc_reader_mesh.h | 9 +- .../blender/io/alembic/intern/abc_reader_object.cc | 6 +- .../blender/io/alembic/intern/abc_reader_points.cc | 2 +- source/blender/io/alembic/intern/alembic_capi.cc | 26 +- source/blender/io/collada/AnimationImporter.cpp | 4 +- source/blender/io/collada/ArmatureExporter.cpp | 2 +- source/blender/io/collada/BCAnimationCurve.cpp | 4 +- source/blender/io/collada/BlenderContext.cpp | 28 +- source/blender/io/collada/BlenderContext.h | 8 +- source/blender/io/collada/ControllerExporter.cpp | 7 +- source/blender/io/collada/DocumentImporter.cpp | 11 +- source/blender/io/collada/EffectExporter.cpp | 6 +- source/blender/io/collada/ExportSettings.h | 7 +- source/blender/io/collada/GeometryExporter.cpp | 89 +- source/blender/io/collada/ImportSettings.h | 1 + source/blender/io/collada/Materials.cpp | 2 +- source/blender/io/collada/MeshImporter.cpp | 135 +- source/blender/io/collada/MeshImporter.h | 7 +- source/blender/io/collada/SceneExporter.cpp | 8 +- source/blender/io/collada/collada.cpp | 4 +- source/blender/io/collada/collada_utils.cpp | 3 +- source/blender/io/common/CMakeLists.txt | 4 +- .../io/common/IO_abstract_hierarchy_iterator.h | 6 +- source/blender/io/gpencil/gpencil_io.h | 2 + .../blender/io/gpencil/intern/gpencil_io_base.cc | 10 +- .../blender/io/gpencil/intern/gpencil_io_base.hh | 6 +- .../io/gpencil/intern/gpencil_io_export_pdf.cc | 2 +- .../io/gpencil/intern/gpencil_io_export_svg.cc | 2 +- .../io/gpencil/intern/gpencil_io_import_base.cc | 17 +- source/blender/io/stl/CMakeLists.txt | 8 +- source/blender/io/stl/importer/stl_import.cc | 3 +- source/blender/io/stl/importer/stl_import_mesh.cc | 25 +- source/blender/io/usd/intern/usd_capi_import.cc | 28 +- .../blender/io/usd/intern/usd_reader_material.cc | 86 +- source/blender/io/usd/intern/usd_reader_mesh.cc | 132 +- source/blender/io/usd/intern/usd_reader_mesh.h | 11 +- source/blender/io/usd/intern/usd_reader_stage.cc | 15 +- source/blender/io/usd/intern/usd_reader_stage.h | 2 + source/blender/io/usd/intern/usd_writer_mesh.cc | 64 +- source/blender/io/usd/intern/usd_writer_volume.cc | 2 +- source/blender/io/usd/usd.h | 2 +- .../blender/io/wavefront_obj/IO_wavefront_obj.cc | 17 +- source/blender/io/wavefront_obj/IO_wavefront_obj.h | 13 +- .../exporter/obj_export_file_writer.cc | 349 +- .../exporter/obj_export_file_writer.hh | 34 +- .../io/wavefront_obj/exporter/obj_export_io.hh | 404 +- .../io/wavefront_obj/exporter/obj_export_mesh.cc | 116 +- .../io/wavefront_obj/exporter/obj_export_mesh.hh | 5 - .../io/wavefront_obj/exporter/obj_export_mtl.cc | 214 +- .../io/wavefront_obj/exporter/obj_export_mtl.hh | 99 +- .../io/wavefront_obj/exporter/obj_exporter.cc | 11 +- .../importer/obj_import_file_reader.cc | 222 +- .../io/wavefront_obj/importer/obj_import_mesh.cc | 145 +- .../io/wavefront_obj/importer/obj_import_mesh.hh | 3 +- .../io/wavefront_obj/importer/obj_import_mtl.cc | 335 +- .../io/wavefront_obj/importer/obj_import_mtl.hh | 88 +- .../wavefront_obj/importer/obj_import_objects.hh | 71 +- .../importer/obj_import_string_utils.cc | 28 +- .../importer/obj_import_string_utils.hh | 14 +- .../io/wavefront_obj/importer/obj_importer.cc | 16 +- .../io/wavefront_obj/tests/obj_exporter_tests.cc | 47 +- .../io/wavefront_obj/tests/obj_exporter_tests.hh | 1 + .../tests/obj_import_string_utils_tests.cc | 23 +- .../io/wavefront_obj/tests/obj_importer_tests.cc | 450 +- .../io/wavefront_obj/tests/obj_mtl_parser_tests.cc | 259 +- source/blender/makesdna/DNA_ID.h | 217 +- source/blender/makesdna/DNA_anim_types.h | 3 + source/blender/makesdna/DNA_asset_types.h | 2 +- source/blender/makesdna/DNA_brush_enums.h | 10 +- source/blender/makesdna/DNA_brush_types.h | 13 +- source/blender/makesdna/DNA_camera_types.h | 1 + source/blender/makesdna/DNA_collection_types.h | 4 + source/blender/makesdna/DNA_color_types.h | 3 + source/blender/makesdna/DNA_constraint_types.h | 2 +- source/blender/makesdna/DNA_curve_types.h | 4 +- source/blender/makesdna/DNA_curves_types.h | 18 +- source/blender/makesdna/DNA_customdata_types.h | 1 - source/blender/makesdna/DNA_fileglobal_types.h | 4 +- .../blender/makesdna/DNA_gpencil_modifier_types.h | 7 +- source/blender/makesdna/DNA_gpencil_types.h | 6 +- source/blender/makesdna/DNA_image_types.h | 19 +- source/blender/makesdna/DNA_layer_types.h | 59 +- source/blender/makesdna/DNA_lightprobe_types.h | 1 - source/blender/makesdna/DNA_lineart_types.h | 1 - source/blender/makesdna/DNA_mesh_types.h | 136 +- source/blender/makesdna/DNA_meshdata_types.h | 28 +- source/blender/makesdna/DNA_meta_types.h | 2 - source/blender/makesdna/DNA_modifier_types.h | 10 +- source/blender/makesdna/DNA_node_types.h | 304 +- source/blender/makesdna/DNA_object_types.h | 9 +- source/blender/makesdna/DNA_outliner_types.h | 7 +- source/blender/makesdna/DNA_particle_types.h | 4 +- source/blender/makesdna/DNA_pointcloud_types.h | 16 +- source/blender/makesdna/DNA_scene_types.h | 27 +- source/blender/makesdna/DNA_screen_types.h | 14 +- source/blender/makesdna/DNA_sequence_types.h | 9 +- source/blender/makesdna/DNA_sound_types.h | 2 +- source/blender/makesdna/DNA_space_types.h | 43 +- source/blender/makesdna/DNA_texture_types.h | 2 +- source/blender/makesdna/DNA_userdef_types.h | 10 +- source/blender/makesdna/DNA_view2d_types.h | 18 +- source/blender/makesdna/DNA_view3d_types.h | 12 +- source/blender/makesdna/DNA_windowmanager_types.h | 35 +- source/blender/makesdna/DNA_workspace_types.h | 2 +- source/blender/makesdna/intern/CMakeLists.txt | 43 +- source/blender/makesdna/intern/dna_genfile.c | 29 +- source/blender/makesdna/intern/dna_rename_defs.h | 4 + source/blender/makesdna/intern/makesdna.c | 68 +- source/blender/makesrna/RNA_access.h | 249 +- source/blender/makesrna/RNA_path.h | 258 + source/blender/makesrna/RNA_types.h | 14 +- source/blender/makesrna/intern/CMakeLists.txt | 72 +- source/blender/makesrna/intern/makesrna.c | 88 +- source/blender/makesrna/intern/rna_ID.c | 43 +- source/blender/makesrna/intern/rna_access.c | 1362 +---- .../makesrna/intern/rna_access_compare_override.c | 5 +- .../blender/makesrna/intern/rna_access_internal.h | 10 + source/blender/makesrna/intern/rna_action.c | 7 +- source/blender/makesrna/intern/rna_armature.c | 4 + source/blender/makesrna/intern/rna_attribute.c | 54 +- source/blender/makesrna/intern/rna_brush.c | 61 +- source/blender/makesrna/intern/rna_camera.c | 11 +- source/blender/makesrna/intern/rna_collection.c | 91 +- source/blender/makesrna/intern/rna_color.c | 4 +- source/blender/makesrna/intern/rna_constraint.c | 3 +- source/blender/makesrna/intern/rna_curve.c | 16 +- source/blender/makesrna/intern/rna_curveprofile.c | 3 + source/blender/makesrna/intern/rna_curves.c | 117 +- source/blender/makesrna/intern/rna_depsgraph.c | 120 +- source/blender/makesrna/intern/rna_fcurve.c | 21 +- source/blender/makesrna/intern/rna_fluid.c | 72 +- source/blender/makesrna/intern/rna_gpencil.c | 1 + .../blender/makesrna/intern/rna_gpencil_modifier.c | 35 +- source/blender/makesrna/intern/rna_image.c | 156 +- source/blender/makesrna/intern/rna_internal.h | 12 +- source/blender/makesrna/intern/rna_layer.c | 36 +- source/blender/makesrna/intern/rna_light.c | 2 + source/blender/makesrna/intern/rna_main.c | 7 +- source/blender/makesrna/intern/rna_main_api.c | 6 - source/blender/makesrna/intern/rna_material.c | 4 +- source/blender/makesrna/intern/rna_mesh.c | 552 ++- source/blender/makesrna/intern/rna_mesh_api.c | 21 +- source/blender/makesrna/intern/rna_mesh_utils.h | 4 +- source/blender/makesrna/intern/rna_meta_api.c | 2 +- source/blender/makesrna/intern/rna_modifier.c | 8 +- source/blender/makesrna/intern/rna_nodetree.c | 146 +- source/blender/makesrna/intern/rna_object.c | 24 +- source/blender/makesrna/intern/rna_object_api.c | 99 +- source/blender/makesrna/intern/rna_object_force.c | 2 +- source/blender/makesrna/intern/rna_particle.c | 34 +- source/blender/makesrna/intern/rna_path.cc | 1364 +++++ source/blender/makesrna/intern/rna_pointcloud.c | 72 +- source/blender/makesrna/intern/rna_pose.c | 4 +- source/blender/makesrna/intern/rna_render.c | 2 +- source/blender/makesrna/intern/rna_rigidbody.c | 4 +- source/blender/makesrna/intern/rna_rna.c | 17 +- source/blender/makesrna/intern/rna_scene.c | 110 +- source/blender/makesrna/intern/rna_sculpt_paint.c | 31 +- source/blender/makesrna/intern/rna_sequencer.c | 9 +- source/blender/makesrna/intern/rna_sequencer_api.c | 2 +- source/blender/makesrna/intern/rna_space.c | 117 +- source/blender/makesrna/intern/rna_texture.c | 2 +- source/blender/makesrna/intern/rna_ui.c | 8 +- source/blender/makesrna/intern/rna_ui_api.c | 3 +- source/blender/makesrna/intern/rna_userdef.c | 115 +- source/blender/makesrna/intern/rna_wm.c | 37 +- source/blender/makesrna/intern/rna_wm_api.c | 8 +- source/blender/makesrna/intern/rna_wm_gizmo.c | 7 +- source/blender/makesrna/intern/rna_wm_gizmo_api.c | 40 +- source/blender/makesrna/intern/rna_workspace.c | 2 +- source/blender/modifiers/CMakeLists.txt | 3 - source/blender/modifiers/intern/MOD_armature.c | 11 +- source/blender/modifiers/intern/MOD_array.c | 83 +- source/blender/modifiers/intern/MOD_bevel.c | 12 +- source/blender/modifiers/intern/MOD_boolean.cc | 26 +- source/blender/modifiers/intern/MOD_build.c | 37 +- source/blender/modifiers/intern/MOD_cast.c | 19 +- source/blender/modifiers/intern/MOD_cloth.c | 14 +- source/blender/modifiers/intern/MOD_collision.c | 12 +- .../modifiers/intern/MOD_correctivesmooth.c | 255 +- source/blender/modifiers/intern/MOD_curve.c | 12 +- source/blender/modifiers/intern/MOD_datatransfer.c | 18 +- source/blender/modifiers/intern/MOD_decimate.c | 12 +- source/blender/modifiers/intern/MOD_displace.c | 26 +- source/blender/modifiers/intern/MOD_dynamicpaint.c | 8 +- source/blender/modifiers/intern/MOD_edgesplit.c | 2 +- source/blender/modifiers/intern/MOD_explode.c | 51 +- source/blender/modifiers/intern/MOD_fluid.c | 8 +- source/blender/modifiers/intern/MOD_hook.c | 13 +- .../blender/modifiers/intern/MOD_laplaciandeform.c | 30 +- .../blender/modifiers/intern/MOD_laplaciansmooth.c | 24 +- source/blender/modifiers/intern/MOD_lattice.c | 10 +- source/blender/modifiers/intern/MOD_mask.cc | 190 +- .../blender/modifiers/intern/MOD_mesh_to_volume.cc | 17 +- source/blender/modifiers/intern/MOD_meshcache.c | 17 +- .../blender/modifiers/intern/MOD_meshcache_util.h | 8 +- source/blender/modifiers/intern/MOD_meshdeform.c | 20 +- .../modifiers/intern/MOD_meshsequencecache.cc | 19 +- source/blender/modifiers/intern/MOD_mirror.c | 4 +- source/blender/modifiers/intern/MOD_multires.c | 6 +- source/blender/modifiers/intern/MOD_nodes.cc | 578 ++- .../modifiers/intern/MOD_nodes_evaluator.cc | 1928 -------- .../modifiers/intern/MOD_nodes_evaluator.hh | 44 - source/blender/modifiers/intern/MOD_normal_edit.c | 74 +- source/blender/modifiers/intern/MOD_ocean.c | 147 +- .../modifiers/intern/MOD_particleinstance.c | 32 +- .../blender/modifiers/intern/MOD_particlesystem.cc | 9 +- source/blender/modifiers/intern/MOD_remesh.c | 27 +- source/blender/modifiers/intern/MOD_screw.c | 123 +- source/blender/modifiers/intern/MOD_shapekey.c | 13 +- source/blender/modifiers/intern/MOD_shrinkwrap.c | 19 +- source/blender/modifiers/intern/MOD_simpledeform.c | 17 +- source/blender/modifiers/intern/MOD_skin.c | 37 +- source/blender/modifiers/intern/MOD_smooth.c | 18 +- source/blender/modifiers/intern/MOD_softbody.c | 4 +- source/blender/modifiers/intern/MOD_solidify.c | 6 +- .../modifiers/intern/MOD_solidify_extrude.c | 223 +- .../modifiers/intern/MOD_solidify_nonmanifold.c | 218 +- source/blender/modifiers/intern/MOD_subsurf.c | 6 +- source/blender/modifiers/intern/MOD_surface.c | 8 +- .../blender/modifiers/intern/MOD_surfacedeform.c | 42 +- source/blender/modifiers/intern/MOD_triangulate.c | 4 +- source/blender/modifiers/intern/MOD_util.c | 21 +- source/blender/modifiers/intern/MOD_util.h | 3 +- source/blender/modifiers/intern/MOD_uvproject.c | 31 +- source/blender/modifiers/intern/MOD_uvwarp.c | 29 +- .../modifiers/intern/MOD_volume_displace.cc | 4 +- .../blender/modifiers/intern/MOD_volume_to_mesh.cc | 8 +- source/blender/modifiers/intern/MOD_warp.c | 16 +- source/blender/modifiers/intern/MOD_wave.c | 43 +- .../blender/modifiers/intern/MOD_weighted_normal.c | 96 +- source/blender/modifiers/intern/MOD_weightvgedit.c | 26 +- source/blender/modifiers/intern/MOD_weightvgmix.c | 22 +- .../modifiers/intern/MOD_weightvgproximity.c | 16 +- source/blender/modifiers/intern/MOD_weld.cc | 6 +- source/blender/modifiers/intern/MOD_wireframe.c | 6 +- source/blender/nodes/CMakeLists.txt | 22 +- source/blender/nodes/NOD_composite.h | 2 +- source/blender/nodes/NOD_derived_node_tree.hh | 172 +- source/blender/nodes/NOD_geometry.h | 17 +- source/blender/nodes/NOD_geometry_exec.hh | 213 +- .../blender/nodes/NOD_geometry_nodes_eval_log.hh | 406 -- .../nodes/NOD_geometry_nodes_lazy_function.hh | 181 + source/blender/nodes/NOD_geometry_nodes_log.hh | 340 ++ source/blender/nodes/NOD_multi_function.hh | 26 +- source/blender/nodes/NOD_node_declaration.hh | 55 + source/blender/nodes/NOD_node_tree_ref.hh | 760 --- source/blender/nodes/NOD_shader.h | 1 + source/blender/nodes/NOD_static_types.h | 266 +- source/blender/nodes/composite/CMakeLists.txt | 11 +- .../blender/nodes/composite/node_composite_tree.cc | 5 +- .../composite/nodes/node_composite_alpha_over.cc | 68 +- .../composite/nodes/node_composite_antialiasing.cc | 20 + .../nodes/node_composite_bilateralblur.cc | 73 +- .../nodes/composite/nodes/node_composite_blur.cc | 419 ++ .../composite/nodes/node_composite_bokehblur.cc | 121 +- .../composite/nodes/node_composite_bokehimage.cc | 66 + .../composite/nodes/node_composite_boxmask.cc | 99 + .../composite/nodes/node_composite_brightness.cc | 45 +- .../nodes/node_composite_channel_matte.cc | 96 +- .../composite/nodes/node_composite_chroma_matte.cc | 63 +- .../composite/nodes/node_composite_color_matte.cc | 62 +- .../composite/nodes/node_composite_color_spill.cc | 112 +- .../composite/nodes/node_composite_colorbalance.cc | 69 +- .../nodes/node_composite_colorcorrection.cc | 81 +- .../composite/nodes/node_composite_composite.cc | 129 + .../nodes/node_composite_convert_color_space.cc | 20 + .../composite/nodes/node_composite_cornerpin.cc | 21 + .../nodes/composite/nodes/node_composite_crop.cc | 168 +- .../composite/nodes/node_composite_cryptomatte.cc | 55 +- .../nodes/composite/nodes/node_composite_curves.cc | 246 +- .../composite/nodes/node_composite_defocus.cc | 20 + .../composite/nodes/node_composite_denoise.cc | 20 + .../composite/nodes/node_composite_despeckle.cc | 72 +- .../composite/nodes/node_composite_diff_matte.cc | 56 +- .../nodes/composite/nodes/node_composite_dilate.cc | 486 +- .../nodes/node_composite_directionalblur.cc | 140 +- .../composite/nodes/node_composite_displace.cc | 20 + .../nodes/node_composite_distance_matte.cc | 72 +- .../nodes/node_composite_double_edge_mask.cc | 20 + .../composite/nodes/node_composite_ellipsemask.cc | 99 + .../composite/nodes/node_composite_exposure.cc | 31 +- .../nodes/composite/nodes/node_composite_filter.cc | 130 +- .../nodes/composite/nodes/node_composite_flip.cc | 64 +- .../nodes/composite/nodes/node_composite_gamma.cc | 32 +- .../nodes/composite/nodes/node_composite_glare.cc | 20 + .../composite/nodes/node_composite_hue_sat_val.cc | 49 +- .../composite/nodes/node_composite_huecorrect.cc | 65 +- .../composite/nodes/node_composite_id_mask.cc | 20 + .../nodes/composite/nodes/node_composite_image.cc | 275 +- .../composite/nodes/node_composite_inpaint.cc | 20 + .../nodes/composite/nodes/node_composite_invert.cc | 55 +- .../nodes/composite/nodes/node_composite_keying.cc | 22 + .../composite/nodes/node_composite_keyingscreen.cc | 20 + .../composite/nodes/node_composite_lensdist.cc | 204 +- .../nodes/composite/nodes/node_composite_levels.cc | 21 + .../composite/nodes/node_composite_luma_matte.cc | 55 +- .../composite/nodes/node_composite_map_range.cc | 67 +- .../nodes/composite/nodes/node_composite_map_uv.cc | 20 + .../composite/nodes/node_composite_map_value.cc | 58 +- .../nodes/composite/nodes/node_composite_mask.cc | 20 + .../nodes/composite/nodes/node_composite_math.cc | 67 +- .../nodes/composite/nodes/node_composite_mixrgb.cc | 125 +- .../composite/nodes/node_composite_movieclip.cc | 190 + .../nodes/node_composite_moviedistortion.cc | 20 + .../nodes/composite/nodes/node_composite_normal.cc | 42 +- .../composite/nodes/node_composite_normalize.cc | 20 + .../composite/nodes/node_composite_output_file.cc | 21 +- .../composite/nodes/node_composite_pixelate.cc | 49 + .../nodes/node_composite_planetrackdeform.cc | 21 + .../composite/nodes/node_composite_posterize.cc | 35 +- .../composite/nodes/node_composite_premulkey.cc | 39 +- .../nodes/composite/nodes/node_composite_rgb.cc | 30 + .../nodes/composite/nodes/node_composite_rotate.cc | 54 +- .../nodes/composite/nodes/node_composite_scale.cc | 167 +- .../composite/nodes/node_composite_scene_time.cc | 36 + .../nodes/node_composite_sepcomb_color.cc | 124 +- .../composite/nodes/node_composite_sepcomb_hsva.cc | 77 +- .../composite/nodes/node_composite_sepcomb_rgba.cc | 77 +- .../composite/nodes/node_composite_sepcomb_xyz.cc | 63 +- .../composite/nodes/node_composite_sepcomb_ycca.cc | 126 +- .../composite/nodes/node_composite_sepcomb_yuva.cc | 76 +- .../composite/nodes/node_composite_setalpha.cc | 42 +- .../composite/nodes/node_composite_split_viewer.cc | 71 + .../composite/nodes/node_composite_stabilize2d.cc | 20 + .../composite/nodes/node_composite_sunbeams.cc | 20 + .../nodes/composite/nodes/node_composite_switch.cc | 28 + .../composite/nodes/node_composite_switchview.cc | 22 + .../composite/nodes/node_composite_texture.cc | 21 + .../composite/nodes/node_composite_tonemap.cc | 20 + .../composite/nodes/node_composite_trackpos.cc | 22 + .../composite/nodes/node_composite_transform.cc | 77 +- .../composite/nodes/node_composite_translate.cc | 72 +- .../composite/nodes/node_composite_val_to_rgb.cc | 150 +- .../nodes/composite/nodes/node_composite_value.cc | 26 + .../composite/nodes/node_composite_vec_blur.cc | 20 + .../nodes/composite/nodes/node_composite_viewer.cc | 129 + .../composite/nodes/node_composite_zcombine.cc | 21 + .../blender/nodes/function/node_function_util.hh | 2 + .../nodes/node_fn_align_euler_to_vector.cc | 4 +- .../nodes/function/nodes/node_fn_boolean_math.cc | 2 +- .../nodes/function/nodes/node_fn_combine_color.cc | 2 +- .../nodes/function/nodes/node_fn_compare.cc | 2 +- .../nodes/function/nodes/node_fn_float_to_int.cc | 2 +- .../nodes/function/nodes/node_fn_input_bool.cc | 2 +- .../nodes/function/nodes/node_fn_input_color.cc | 2 +- .../nodes/function/nodes/node_fn_input_int.cc | 2 +- .../nodes/function/nodes/node_fn_input_string.cc | 2 +- .../nodes/function/nodes/node_fn_input_vector.cc | 2 +- .../nodes/function/nodes/node_fn_rotate_euler.cc | 2 +- source/blender/nodes/geometry/CMakeLists.txt | 21 +- .../blender/nodes/geometry/node_geometry_exec.cc | 1 + .../blender/nodes/geometry/node_geometry_tree.cc | 6 +- .../blender/nodes/geometry/node_geometry_util.hh | 5 +- .../geometry/nodes/node_geo_accumulate_field.cc | 40 +- .../geometry/nodes/node_geo_attribute_capture.cc | 2 +- .../nodes/node_geo_attribute_domain_size.cc | 28 +- .../geometry/nodes/node_geo_attribute_statistic.cc | 4 +- .../nodes/geometry/nodes/node_geo_boolean.cc | 4 +- .../nodes/geometry/nodes/node_geo_bounding_box.cc | 4 +- .../nodes/geometry/nodes/node_geo_convex_hull.cc | 84 +- .../nodes/node_geo_curve_endpoint_selection.cc | 28 +- .../nodes/geometry/nodes/node_geo_curve_fill.cc | 8 +- .../nodes/geometry/nodes/node_geo_curve_fillet.cc | 595 +-- .../nodes/node_geo_curve_handle_type_selection.cc | 17 +- .../geometry/nodes/node_geo_curve_resample.cc | 37 +- .../nodes/geometry/nodes/node_geo_curve_reverse.cc | 12 +- .../nodes/geometry/nodes/node_geo_curve_sample.cc | 318 +- .../nodes/node_geo_curve_set_handle_type.cc | 37 +- .../nodes/node_geo_curve_spline_parameter.cc | 53 +- .../geometry/nodes/node_geo_curve_spline_type.cc | 18 +- .../geometry/nodes/node_geo_curve_subdivide.cc | 16 +- .../nodes/geometry/nodes/node_geo_curve_to_mesh.cc | 5 +- .../geometry/nodes/node_geo_curve_to_points.cc | 391 +- .../nodes/geometry/nodes/node_geo_curve_trim.cc | 485 +- .../nodes/node_geo_deform_curves_on_surface.cc | 158 +- .../geometry/nodes/node_geo_delete_geometry.cc | 445 +- .../nodes/node_geo_distribute_points_in_volume.cc | 285 ++ .../nodes/node_geo_distribute_points_on_faces.cc | 122 +- .../nodes/geometry/nodes/node_geo_dual_mesh.cc | 200 +- .../geometry/nodes/node_geo_duplicate_elements.cc | 279 +- .../nodes/node_geo_edge_paths_to_curves.cc | 110 + .../nodes/node_geo_edge_paths_to_selection.cc | 134 + .../nodes/geometry/nodes/node_geo_edge_split.cc | 23 +- .../nodes/geometry/nodes/node_geo_extrude_mesh.cc | 204 +- .../geometry/nodes/node_geo_field_at_index.cc | 47 +- .../geometry/nodes/node_geo_field_on_domain.cc | 146 - .../nodes/geometry/nodes/node_geo_flip_faces.cc | 25 +- .../nodes/node_geo_geometry_to_instance.cc | 2 +- .../geometry/nodes/node_geo_input_curve_handles.cc | 20 +- .../nodes/node_geo_input_instance_rotation.cc | 22 +- .../nodes/node_geo_input_instance_scale.cc | 22 +- .../nodes/node_geo_input_mesh_edge_angle.cc | 78 +- .../nodes/node_geo_input_mesh_edge_neighbors.cc | 28 +- .../nodes/node_geo_input_mesh_edge_vertices.cc | 64 +- .../nodes/node_geo_input_mesh_face_area.cc | 32 +- .../nodes/node_geo_input_mesh_face_is_planar.cc | 48 +- .../nodes/node_geo_input_mesh_face_neighbors.cc | 66 +- .../geometry/nodes/node_geo_input_mesh_island.cc | 53 +- .../nodes/node_geo_input_mesh_vertex_neighbors.cc | 58 +- .../nodes/node_geo_input_named_attribute.cc | 2 +- .../nodes/node_geo_input_shortest_edge_paths.cc | 236 + .../geometry/nodes/node_geo_input_spline_length.cc | 23 +- .../nodes/geometry/nodes/node_geo_input_tangent.cc | 25 +- .../geometry/nodes/node_geo_instance_on_points.cc | 7 +- .../geometry/nodes/node_geo_instances_to_points.cc | 42 +- .../geometry/nodes/node_geo_interpolate_domain.cc | 167 + .../nodes/geometry/nodes/node_geo_join_geometry.cc | 3 +- .../geometry/nodes/node_geo_material_selection.cc | 38 +- .../geometry/nodes/node_geo_merge_by_distance.cc | 40 +- .../nodes/node_geo_mesh_face_set_boundaries.cc | 89 + .../nodes/node_geo_mesh_primitive_circle.cc | 8 +- .../geometry/nodes/node_geo_mesh_primitive_cone.cc | 24 +- .../geometry/nodes/node_geo_mesh_primitive_grid.cc | 10 +- .../geometry/nodes/node_geo_mesh_primitive_line.cc | 5 +- .../nodes/node_geo_mesh_primitive_uv_sphere.cc | 154 +- .../nodes/geometry/nodes/node_geo_mesh_to_curve.cc | 19 +- .../geometry/nodes/node_geo_mesh_to_points.cc | 52 +- .../geometry/nodes/node_geo_mesh_to_volume.cc | 5 +- .../nodes/geometry/nodes/node_geo_object_info.cc | 2 +- .../nodes/geometry/nodes/node_geo_points.cc | 9 +- .../geometry/nodes/node_geo_points_to_vertices.cc | 46 +- .../geometry/nodes/node_geo_points_to_volume.cc | 9 +- .../nodes/geometry/nodes/node_geo_raycast.cc | 8 +- .../geometry/nodes/node_geo_remove_attribute.cc | 2 +- .../geometry/nodes/node_geo_rotate_instances.cc | 6 +- .../geometry/nodes/node_geo_scale_elements.cc | 158 +- .../geometry/nodes/node_geo_scale_instances.cc | 5 +- .../geometry/nodes/node_geo_set_curve_handles.cc | 37 +- .../geometry/nodes/node_geo_set_curve_radius.cc | 23 +- .../geometry/nodes/node_geo_set_curve_tilt.cc | 24 +- .../nodes/geometry/nodes/node_geo_set_id.cc | 2 +- .../nodes/geometry/nodes/node_geo_set_material.cc | 13 +- .../geometry/nodes/node_geo_set_material_index.cc | 20 +- .../geometry/nodes/node_geo_set_point_radius.cc | 20 +- .../nodes/geometry/nodes/node_geo_set_position.cc | 9 +- .../geometry/nodes/node_geo_set_shade_smooth.cc | 29 +- .../geometry/nodes/node_geo_set_spline_cyclic.cc | 24 +- .../nodes/node_geo_set_spline_resolution.cc | 28 +- .../nodes/node_geo_store_named_attribute.cc | 75 +- .../nodes/geometry/nodes/node_geo_string_join.cc | 5 +- .../geometry/nodes/node_geo_subdivision_surface.cc | 22 +- .../geometry/nodes/node_geo_transfer_attribute.cc | 60 +- .../nodes/geometry/nodes/node_geo_transform.cc | 129 +- .../geometry/nodes/node_geo_translate_instances.cc | 5 +- .../nodes/geometry/nodes/node_geo_triangulate.cc | 9 +- .../geometry/nodes/node_geo_uv_pack_islands.cc | 44 +- .../nodes/geometry/nodes/node_geo_uv_unwrap.cc | 49 +- .../nodes/geometry/nodes/node_geo_volume_cube.cc | 21 +- .../geometry/nodes/node_geo_volume_to_mesh.cc | 15 +- source/blender/nodes/intern/derived_node_tree.cc | 139 +- .../nodes/intern/geometry_nodes_eval_log.cc | 508 -- .../nodes/intern/geometry_nodes_lazy_function.cc | 1423 ++++++ source/blender/nodes/intern/geometry_nodes_log.cc | 610 +++ source/blender/nodes/intern/node_common.cc | 28 +- source/blender/nodes/intern/node_geometry_exec.cc | 88 +- source/blender/nodes/intern/node_multi_function.cc | 26 +- source/blender/nodes/intern/node_tree_ref.cc | 678 --- source/blender/nodes/intern/node_util.c | 2 +- source/blender/nodes/shader/CMakeLists.txt | 27 +- source/blender/nodes/shader/node_shader_tree.cc | 31 +- source/blender/nodes/shader/node_shader_util.hh | 2 + .../nodes/shader/nodes/node_shader_attribute.cc | 16 +- .../nodes/node_shader_bsdf_hair_principled.cc | 3 +- .../shader/nodes/node_shader_bsdf_principled.cc | 21 + .../nodes/shader/nodes/node_shader_color_ramp.cc | 2 +- .../nodes/shader/nodes/node_shader_curves.cc | 6 +- .../nodes/shader/nodes/node_shader_geometry.cc | 5 +- .../nodes/shader/nodes/node_shader_hair_info.cc | 4 +- .../blender/nodes/shader/nodes/node_shader_math.cc | 7 +- .../blender/nodes/shader/nodes/node_shader_mix.cc | 443 ++ .../nodes/shader/nodes/node_shader_mix_rgb.cc | 33 +- .../blender/nodes/shader/nodes/node_shader_rgb.cc | 7 +- .../nodes/shader/nodes/node_shader_sepcomb_hsv.cc | 2 + .../nodes/shader/nodes/node_shader_sepcomb_rgb.cc | 2 + .../nodes/shader/nodes/node_shader_tex_brick.cc | 2 +- .../nodes/shader/nodes/node_shader_tex_coord.cc | 8 +- .../shader/nodes/node_shader_tex_environment.cc | 6 +- .../nodes/shader/nodes/node_shader_tex_gradient.cc | 2 +- .../nodes/shader/nodes/node_shader_tex_magic.cc | 2 +- .../nodes/shader/nodes/node_shader_tex_musgrave.cc | 2 +- .../nodes/shader/nodes/node_shader_tex_sky.cc | 52 +- .../nodes/shader/nodes/node_shader_tex_wave.cc | 2 +- .../shader/nodes/node_shader_tex_white_noise.cc | 2 +- .../nodes/shader/nodes/node_shader_value.cc | 7 +- .../nodes/shader/nodes/node_shader_vector_math.cc | 2 +- .../shader/nodes/node_shader_vector_rotate.cc | 2 +- .../shader/nodes/node_shader_vector_transform.cc | 2 +- .../nodes/shader/nodes/node_shader_vertex_color.cc | 7 +- .../nodes/shader/nodes/node_shader_volume_info.cc | 2 + source/blender/nodes/texture/CMakeLists.txt | 16 +- source/blender/nodes/texture/node_texture_tree.c | 7 +- source/blender/python/BPY_extern.h | 2 +- source/blender/python/CMakeLists.txt | 4 + source/blender/python/bmesh/bmesh_py_api.c | 2 +- source/blender/python/bmesh/bmesh_py_geometry.c | 2 +- source/blender/python/bmesh/bmesh_py_ops.c | 2 +- source/blender/python/bmesh/bmesh_py_types.c | 6 +- .../blender/python/bmesh/bmesh_py_types_meshdata.c | 4 +- source/blender/python/bmesh/bmesh_py_utils.c | 2 +- source/blender/python/generic/CMakeLists.txt | 7 +- source/blender/python/generic/bgl.c | 70 +- source/blender/python/generic/bl_math_py_api.c | 2 +- source/blender/python/generic/blf_py_api.c | 2 +- source/blender/python/generic/idprop_py_api.c | 4 +- source/blender/python/generic/imbuf_py_api.c | 4 +- source/blender/python/generic/py_capi_utils.c | 43 +- source/blender/python/generic/py_capi_utils.h | 1 - source/blender/python/gpu/CMakeLists.txt | 6 +- source/blender/python/gpu/gpu_py_batch.c | 6 +- source/blender/python/gpu/gpu_py_buffer.c | 4 +- source/blender/python/gpu/gpu_py_element.c | 2 +- source/blender/python/gpu/gpu_py_framebuffer.c | 28 +- source/blender/python/gpu/gpu_py_matrix.c | 12 +- source/blender/python/gpu/gpu_py_platform.c | 32 + source/blender/python/gpu/gpu_py_select.c | 2 +- source/blender/python/gpu/gpu_py_shader.c | 231 +- source/blender/python/gpu/gpu_py_shader.h | 5 + .../python/gpu/gpu_py_shader_create_info.cc | 85 +- source/blender/python/gpu/gpu_py_state.c | 27 +- source/blender/python/gpu/gpu_py_texture.c | 4 +- source/blender/python/gpu/gpu_py_vertex_buffer.c | 16 +- source/blender/python/gpu/gpu_py_vertex_format.c | 8 +- source/blender/python/intern/CMakeLists.txt | 4 +- source/blender/python/intern/bpy.c | 171 +- source/blender/python/intern/bpy.h | 2 +- source/blender/python/intern/bpy_app.c | 41 +- .../blender/python/intern/bpy_app_build_options.c | 4 +- source/blender/python/intern/bpy_app_icons.c | 2 +- source/blender/python/intern/bpy_app_timers.c | 2 +- source/blender/python/intern/bpy_driver.c | 98 +- source/blender/python/intern/bpy_driver.h | 10 + source/blender/python/intern/bpy_gizmo_wrap.c | 2 +- source/blender/python/intern/bpy_interface.c | 59 +- .../blender/python/intern/bpy_interface_atexit.c | 2 +- source/blender/python/intern/bpy_library_load.c | 2 +- source/blender/python/intern/bpy_operator.c | 2 +- source/blender/python/intern/bpy_path.c | 2 +- source/blender/python/intern/bpy_rna.c | 121 +- source/blender/python/intern/bpy_rna.h | 6 +- source/blender/python/intern/bpy_rna_anim.c | 4 +- source/blender/python/intern/bpy_rna_array.c | 2 +- source/blender/python/intern/bpy_rna_data.c | 2 +- source/blender/python/intern/bpy_rna_gizmo.c | 2 + source/blender/python/intern/bpy_rna_operator.c | 2 +- source/blender/python/intern/bpy_rna_types_capi.c | 12 +- source/blender/python/intern/bpy_traceback.c | 2 +- source/blender/python/intern/bpy_utils_units.c | 4 +- source/blender/python/intern/stubs.c | 2 +- source/blender/python/mathutils/mathutils.c | 4 +- source/blender/python/mathutils/mathutils.h | 8 +- source/blender/python/mathutils/mathutils_Color.c | 15 +- source/blender/python/mathutils/mathutils_Euler.c | 4 +- source/blender/python/mathutils/mathutils_Matrix.c | 31 +- .../python/mathutils/mathutils_Quaternion.c | 5 +- source/blender/python/mathutils/mathutils_Vector.c | 2 +- .../blender/python/mathutils/mathutils_bvhtree.c | 10 +- .../blender/python/mathutils/mathutils_geometry.c | 2 +- .../python/mathutils/mathutils_interpolate.c | 2 +- source/blender/python/mathutils/mathutils_kdtree.c | 2 +- source/blender/python/mathutils/mathutils_noise.c | 2 +- source/blender/render/CMakeLists.txt | 9 +- source/blender/render/RE_bake.h | 5 + source/blender/render/RE_engine.h | 28 +- source/blender/render/RE_pipeline.h | 8 +- source/blender/render/intern/bake.c | 42 +- source/blender/render/intern/engine.c | 1207 ----- source/blender/render/intern/engine.cc | 1338 +++++ source/blender/render/intern/initrender.c | 221 - source/blender/render/intern/initrender.cc | 222 + source/blender/render/intern/multires_bake.c | 22 +- source/blender/render/intern/pipeline.c | 2683 ---------- source/blender/render/intern/pipeline.cc | 2664 ++++++++++ source/blender/render/intern/render_result.c | 1276 ----- source/blender/render/intern/render_result.cc | 1261 +++++ source/blender/render/intern/render_result.h | 3 +- source/blender/render/intern/render_types.h | 7 +- source/blender/render/intern/texture_common.h | 4 +- source/blender/render/intern/texture_image.c | 10 - source/blender/render/intern/texture_margin.cc | 14 +- .../blender/render/intern/texture_pointdensity.c | 8 +- source/blender/render/intern/texture_procedural.c | 1 - source/blender/sequencer/SEQ_edit.h | 8 +- source/blender/sequencer/SEQ_relations.h | 2 +- source/blender/sequencer/SEQ_select.h | 6 +- source/blender/sequencer/SEQ_transform.h | 2 +- source/blender/sequencer/intern/effects.c | 23 +- source/blender/sequencer/intern/modifier.c | 6 +- source/blender/sequencer/intern/proxy.c | 16 +- source/blender/sequencer/intern/render.c | 20 +- source/blender/sequencer/intern/sequencer.c | 14 +- source/blender/sequencer/intern/sound.c | 3 +- source/blender/sequencer/intern/strip_edit.c | 31 +- source/blender/sequencer/intern/strip_select.c | 6 +- source/blender/sequencer/intern/strip_time.c | 26 +- source/blender/sequencer/intern/strip_time.h | 1 + source/blender/sequencer/intern/strip_transform.c | 14 +- source/blender/sequencer/intern/utils.c | 7 +- source/blender/shader_fx/intern/FX_shader_blur.c | 2 +- .../blender/shader_fx/intern/FX_shader_colorize.c | 2 +- source/blender/shader_fx/intern/FX_shader_flip.c | 2 +- source/blender/shader_fx/intern/FX_shader_glow.c | 4 +- source/blender/shader_fx/intern/FX_shader_pixel.c | 2 +- source/blender/shader_fx/intern/FX_shader_rim.c | 2 +- source/blender/shader_fx/intern/FX_shader_shadow.c | 2 +- source/blender/shader_fx/intern/FX_shader_swirl.c | 4 +- source/blender/shader_fx/intern/FX_shader_wave.c | 4 +- source/blender/simulation/intern/hair_volume.cpp | 2 +- source/blender/windowmanager/CMakeLists.txt | 8 +- source/blender/windowmanager/WM_api.h | 38 +- source/blender/windowmanager/WM_toolsystem.h | 8 +- source/blender/windowmanager/WM_types.h | 14 +- .../blender/windowmanager/gizmo/WM_gizmo_types.h | 2 +- .../windowmanager/gizmo/intern/wm_gizmo_group.c | 4 +- .../windowmanager/gizmo/intern/wm_gizmo_map.c | 10 +- source/blender/windowmanager/intern/wm.c | 15 +- source/blender/windowmanager/intern/wm_dragdrop.c | 1008 ---- source/blender/windowmanager/intern/wm_dragdrop.cc | 1019 ++++ source/blender/windowmanager/intern/wm_draw.c | 57 +- .../blender/windowmanager/intern/wm_event_query.c | 21 +- .../windowmanager/intern/wm_event_system.cc | 283 +- source/blender/windowmanager/intern/wm_files.c | 78 +- .../blender/windowmanager/intern/wm_files_link.c | 2 +- source/blender/windowmanager/intern/wm_gesture.c | 16 +- source/blender/windowmanager/intern/wm_init_exit.c | 38 +- .../windowmanager/intern/wm_operator_props.c | 18 +- .../windowmanager/intern/wm_operator_utils.c | 3 +- source/blender/windowmanager/intern/wm_operators.c | 23 +- .../windowmanager/intern/wm_platform_support.c | 44 +- source/blender/windowmanager/intern/wm_playanim.c | 20 +- .../windowmanager/intern/wm_splash_screen.c | 81 +- source/blender/windowmanager/intern/wm_stereo.c | 4 +- .../blender/windowmanager/intern/wm_toolsystem.c | 64 +- source/blender/windowmanager/intern/wm_window.c | 357 +- .../message_bus/intern/wm_message_bus_rna.c | 1 + .../windowmanager/message_bus/wm_message_bus.h | 2 +- source/blender/windowmanager/wm_event_types.h | 37 +- source/blender/windowmanager/wm_window.h | 1 + source/creator/CMakeLists.txt | 630 +-- source/creator/blender.map | 173 - source/creator/creator.c | 50 +- source/creator/creator_intern.h | 12 +- source/creator/creator_signals.c | 7 +- source/creator/osx_locals.map | 71 - source/creator/symbols_apple.map | 71 + source/creator/symbols_unix.map | 44 + 2571 files changed, 179193 insertions(+), 120851 deletions(-) create mode 100644 source/blender/blenkernel/BKE_compute_contexts.hh create mode 100644 source/blender/blenkernel/BKE_crazyspace.hh delete mode 100644 source/blender/blenkernel/BKE_lib_principle_properties.h create mode 100644 source/blender/blenkernel/BKE_main_namemap.h delete mode 100644 source/blender/blenkernel/BKE_outliner_treehash.h create mode 100644 source/blender/blenkernel/BKE_outliner_treehash.hh delete mode 100644 source/blender/blenkernel/BKE_spline.hh create mode 100644 source/blender/blenkernel/intern/compute_contexts.cc delete mode 100644 source/blender/blenkernel/intern/crazyspace.c create mode 100644 source/blender/blenkernel/intern/crazyspace.cc delete mode 100644 source/blender/blenkernel/intern/curve_eval.cc delete mode 100644 source/blender/blenkernel/intern/editmesh_tangent.c create mode 100644 source/blender/blenkernel/intern/editmesh_tangent.cc delete mode 100644 source/blender/blenkernel/intern/geometry_component_curve.cc create mode 100644 source/blender/blenkernel/intern/geometry_component_edit_data.cc create mode 100644 source/blender/blenkernel/intern/geometry_fields.cc delete mode 100644 source/blender/blenkernel/intern/lib_principle_properties.c create mode 100644 source/blender/blenkernel/intern/main_namemap.cc delete mode 100644 source/blender/blenkernel/intern/mball.c create mode 100644 source/blender/blenkernel/intern/mball.cc delete mode 100644 source/blender/blenkernel/intern/mesh_mapping.c create mode 100644 source/blender/blenkernel/intern/mesh_mapping.cc delete mode 100644 source/blender/blenkernel/intern/mesh_tangent.c create mode 100644 source/blender/blenkernel/intern/mesh_tangent.cc delete mode 100644 source/blender/blenkernel/intern/mesh_tessellate.c create mode 100644 source/blender/blenkernel/intern/mesh_tessellate.cc create mode 100644 source/blender/blenkernel/intern/node_runtime.cc delete mode 100644 source/blender/blenkernel/intern/outliner_treehash.c create mode 100644 source/blender/blenkernel/intern/outliner_treehash.cc delete mode 100644 source/blender/blenkernel/intern/paint.c create mode 100644 source/blender/blenkernel/intern/paint.cc delete mode 100644 source/blender/blenkernel/intern/spline_base.cc delete mode 100644 source/blender/blenkernel/intern/spline_bezier.cc delete mode 100644 source/blender/blenkernel/intern/spline_nurbs.cc delete mode 100644 source/blender/blenkernel/intern/spline_poly.cc delete mode 100644 source/blender/blenkernel/intern/subdiv_mesh.c create mode 100644 source/blender/blenkernel/intern/subdiv_mesh.cc delete mode 100644 source/blender/blenkernel/intern/workspace.c create mode 100644 source/blender/blenkernel/intern/workspace.cc create mode 100644 source/blender/blenlib/BLI_array_utils.hh create mode 100644 source/blender/blenlib/BLI_bit_vector.hh create mode 100644 source/blender/blenlib/BLI_compute_context.hh create mode 100644 source/blender/blenlib/BLI_lazy_threading.hh create mode 100644 source/blender/blenlib/BLI_pool.hh delete mode 100644 source/blender/blenlib/BLI_vector_adaptor.hh create mode 100644 source/blender/blenlib/intern/array_utils.cc create mode 100644 source/blender/blenlib/intern/compute_context.cc create mode 100644 source/blender/blenlib/intern/lazy_threading.cc create mode 100644 source/blender/blenlib/tests/BLI_bit_vector_test.cc create mode 100644 source/blender/blenlib/tests/BLI_bitmap_test.cc create mode 100644 source/blender/blenlib/tests/BLI_pool_test.cc delete mode 100644 source/blender/blenloader/intern/blend_validate.c create mode 100644 source/blender/blenloader/intern/blend_validate.cc delete mode 100644 source/blender/blenloader/intern/readblenentry.c create mode 100644 source/blender/blenloader/intern/readblenentry.cc delete mode 100644 source/blender/blenloader/intern/readfile.c create mode 100644 source/blender/blenloader/intern/readfile.cc delete mode 100644 source/blender/blenloader/intern/readfile_tempload.c create mode 100644 source/blender/blenloader/intern/readfile_tempload.cc delete mode 100644 source/blender/blenloader/intern/undofile.c create mode 100644 source/blender/blenloader/intern/undofile.cc delete mode 100644 source/blender/blenloader/intern/versioning_300.c create mode 100644 source/blender/blenloader/intern/versioning_300.cc create mode 100644 source/blender/blenloader/intern/versioning_400.cc delete mode 100644 source/blender/blenloader/intern/writefile.c create mode 100644 source/blender/blenloader/intern/writefile.cc delete mode 100644 source/blender/bmesh/intern/bmesh_mesh.c create mode 100644 source/blender/bmesh/intern/bmesh_mesh.cc delete mode 100644 source/blender/bmesh/intern/bmesh_query_uv.c create mode 100644 source/blender/bmesh/intern/bmesh_query_uv.cc create mode 100644 source/blender/compositor/realtime_compositor/CMakeLists.txt create mode 100644 source/blender/compositor/realtime_compositor/COM_compile_state.hh create mode 100644 source/blender/compositor/realtime_compositor/COM_context.hh create mode 100644 source/blender/compositor/realtime_compositor/COM_conversion_operation.hh create mode 100644 source/blender/compositor/realtime_compositor/COM_domain.hh create mode 100644 source/blender/compositor/realtime_compositor/COM_evaluator.hh create mode 100644 source/blender/compositor/realtime_compositor/COM_input_descriptor.hh create mode 100644 source/blender/compositor/realtime_compositor/COM_input_single_value_operation.hh create mode 100644 source/blender/compositor/realtime_compositor/COM_node_operation.hh create mode 100644 source/blender/compositor/realtime_compositor/COM_operation.hh create mode 100644 source/blender/compositor/realtime_compositor/COM_realize_on_domain_operation.hh create mode 100644 source/blender/compositor/realtime_compositor/COM_reduce_to_single_value_operation.hh create mode 100644 source/blender/compositor/realtime_compositor/COM_result.hh create mode 100644 source/blender/compositor/realtime_compositor/COM_scheduler.hh create mode 100644 source/blender/compositor/realtime_compositor/COM_shader_node.hh create mode 100644 source/blender/compositor/realtime_compositor/COM_shader_operation.hh create mode 100644 source/blender/compositor/realtime_compositor/COM_simple_operation.hh create mode 100644 source/blender/compositor/realtime_compositor/COM_static_shader_manager.hh create mode 100644 source/blender/compositor/realtime_compositor/COM_texture_pool.hh create mode 100644 source/blender/compositor/realtime_compositor/COM_utilities.hh create mode 100644 source/blender/compositor/realtime_compositor/intern/compile_state.cc create mode 100644 source/blender/compositor/realtime_compositor/intern/context.cc create mode 100644 source/blender/compositor/realtime_compositor/intern/conversion_operation.cc create mode 100644 source/blender/compositor/realtime_compositor/intern/domain.cc create mode 100644 source/blender/compositor/realtime_compositor/intern/evaluator.cc create mode 100644 source/blender/compositor/realtime_compositor/intern/input_single_value_operation.cc create mode 100644 source/blender/compositor/realtime_compositor/intern/node_operation.cc create mode 100644 source/blender/compositor/realtime_compositor/intern/operation.cc create mode 100644 source/blender/compositor/realtime_compositor/intern/realize_on_domain_operation.cc create mode 100644 source/blender/compositor/realtime_compositor/intern/reduce_to_single_value_operation.cc create mode 100644 source/blender/compositor/realtime_compositor/intern/result.cc create mode 100644 source/blender/compositor/realtime_compositor/intern/scheduler.cc create mode 100644 source/blender/compositor/realtime_compositor/intern/shader_node.cc create mode 100644 source/blender/compositor/realtime_compositor/intern/shader_operation.cc create mode 100644 source/blender/compositor/realtime_compositor/intern/simple_operation.cc create mode 100644 source/blender/compositor/realtime_compositor/intern/static_shader_manager.cc create mode 100644 source/blender/compositor/realtime_compositor/intern/texture_pool.cc create mode 100644 source/blender/compositor/realtime_compositor/intern/utilities.cc create mode 100644 source/blender/depsgraph/intern/builder/deg_builder_key.cc create mode 100644 source/blender/depsgraph/intern/builder/deg_builder_key.h delete mode 100644 source/blender/depsgraph/intern/builder/deg_builder_relations_keys.cc create mode 100644 source/blender/depsgraph/intern/eval/deg_eval_visibility.cc create mode 100644 source/blender/depsgraph/intern/eval/deg_eval_visibility.h create mode 100644 source/blender/draw/engines/compositor/compositor_engine.cc create mode 100644 source/blender/draw/engines/compositor/compositor_engine.h create mode 100644 source/blender/draw/engines/eevee/shaders/cryptomatte_lib.glsl create mode 100644 source/blender/draw/engines/eevee_next/eevee_cryptomatte.cc create mode 100644 source/blender/draw/engines/eevee_next/eevee_cryptomatte.hh create mode 100644 source/blender/draw/engines/eevee_next/eevee_depth_of_field.cc create mode 100644 source/blender/draw/engines/eevee_next/eevee_depth_of_field.hh create mode 100644 source/blender/draw/engines/eevee_next/eevee_hizbuffer.cc create mode 100644 source/blender/draw/engines/eevee_next/eevee_hizbuffer.hh create mode 100644 source/blender/draw/engines/eevee_next/eevee_light.cc create mode 100644 source/blender/draw/engines/eevee_next/eevee_light.hh create mode 100644 source/blender/draw/engines/eevee_next/eevee_motion_blur.cc create mode 100644 source/blender/draw/engines/eevee_next/eevee_motion_blur.hh create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_colorspace_lib.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_cryptomatte_lib.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_accumulator_lib.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_bokeh_lut_comp.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_downsample_comp.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_filter_comp.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_gather_comp.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_hole_fill_comp.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_lib.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_reduce_comp.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_resolve_comp.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_scatter_frag.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_scatter_vert.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_setup_comp.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_stabilize_comp.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_tiles_dilate_comp.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_tiles_flatten_comp.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_film_cryptomatte_post_comp.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_hiz_debug_frag.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_hiz_update_comp.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_debug_frag.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_select_comp.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_sort_comp.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_tile_comp.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_zbin_comp.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_light_eval_lib.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_light_iter_lib.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_light_lib.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_ltc_lib.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_dilate_comp.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_flatten_comp.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_gather_comp.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_lib.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_sampling_lib.glsl delete mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_velocity_resolve_comp.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/infos/eevee_depth_of_field_info.hh create mode 100644 source/blender/draw/engines/eevee_next/shaders/infos/eevee_hiz_info.hh create mode 100644 source/blender/draw/engines/eevee_next/shaders/infos/eevee_light_culling_info.hh create mode 100644 source/blender/draw/engines/eevee_next/shaders/infos/eevee_motion_blur_info.hh delete mode 100644 source/blender/draw/engines/overlay/overlay_antialiasing.c create mode 100644 source/blender/draw/engines/overlay/overlay_antialiasing.cc delete mode 100644 source/blender/draw/engines/overlay/overlay_armature.c create mode 100644 source/blender/draw/engines/overlay/overlay_armature.cc delete mode 100644 source/blender/draw/engines/overlay/overlay_background.c create mode 100644 source/blender/draw/engines/overlay/overlay_background.cc delete mode 100644 source/blender/draw/engines/overlay/overlay_edit_curve.c create mode 100644 source/blender/draw/engines/overlay/overlay_edit_curve.cc delete mode 100644 source/blender/draw/engines/overlay/overlay_edit_mesh.c create mode 100644 source/blender/draw/engines/overlay/overlay_edit_mesh.cc delete mode 100644 source/blender/draw/engines/overlay/overlay_edit_text.c create mode 100644 source/blender/draw/engines/overlay/overlay_edit_text.cc delete mode 100644 source/blender/draw/engines/overlay/overlay_edit_uv.c create mode 100644 source/blender/draw/engines/overlay/overlay_edit_uv.cc delete mode 100644 source/blender/draw/engines/overlay/overlay_engine.c create mode 100644 source/blender/draw/engines/overlay/overlay_engine.cc delete mode 100644 source/blender/draw/engines/overlay/overlay_extra.c create mode 100644 source/blender/draw/engines/overlay/overlay_extra.cc delete mode 100644 source/blender/draw/engines/overlay/overlay_facing.c create mode 100644 source/blender/draw/engines/overlay/overlay_facing.cc delete mode 100644 source/blender/draw/engines/overlay/overlay_fade.c create mode 100644 source/blender/draw/engines/overlay/overlay_fade.cc delete mode 100644 source/blender/draw/engines/overlay/overlay_gpencil.c create mode 100644 source/blender/draw/engines/overlay/overlay_gpencil.cc delete mode 100644 source/blender/draw/engines/overlay/overlay_grid.c create mode 100644 source/blender/draw/engines/overlay/overlay_grid.cc delete mode 100644 source/blender/draw/engines/overlay/overlay_image.c create mode 100644 source/blender/draw/engines/overlay/overlay_image.cc delete mode 100644 source/blender/draw/engines/overlay/overlay_lattice.c create mode 100644 source/blender/draw/engines/overlay/overlay_lattice.cc delete mode 100644 source/blender/draw/engines/overlay/overlay_metaball.c create mode 100644 source/blender/draw/engines/overlay/overlay_metaball.cc delete mode 100644 source/blender/draw/engines/overlay/overlay_mode_transfer.c create mode 100644 source/blender/draw/engines/overlay/overlay_mode_transfer.cc delete mode 100644 source/blender/draw/engines/overlay/overlay_motion_path.c create mode 100644 source/blender/draw/engines/overlay/overlay_motion_path.cc delete mode 100644 source/blender/draw/engines/overlay/overlay_outline.c create mode 100644 source/blender/draw/engines/overlay/overlay_outline.cc delete mode 100644 source/blender/draw/engines/overlay/overlay_paint.c create mode 100644 source/blender/draw/engines/overlay/overlay_paint.cc delete mode 100644 source/blender/draw/engines/overlay/overlay_particle.c create mode 100644 source/blender/draw/engines/overlay/overlay_particle.cc delete mode 100644 source/blender/draw/engines/overlay/overlay_private.h create mode 100644 source/blender/draw/engines/overlay/overlay_private.hh delete mode 100644 source/blender/draw/engines/overlay/overlay_sculpt.c create mode 100644 source/blender/draw/engines/overlay/overlay_sculpt.cc delete mode 100644 source/blender/draw/engines/overlay/overlay_shader.c create mode 100644 source/blender/draw/engines/overlay/overlay_shader.cc delete mode 100644 source/blender/draw/engines/overlay/overlay_volume.c create mode 100644 source/blender/draw/engines/overlay/overlay_volume.cc delete mode 100644 source/blender/draw/engines/overlay/overlay_wireframe.c create mode 100644 source/blender/draw/engines/overlay/overlay_wireframe.cc delete mode 100644 source/blender/draw/intern/draw_cache_impl_displist.c delete mode 100644 source/blender/draw/intern/draw_cache_impl_metaball.c delete mode 100644 source/blender/draw/intern/draw_cache_impl_pointcloud.c create mode 100644 source/blender/draw/intern/draw_cache_impl_pointcloud.cc create mode 100644 source/blender/draw/intern/draw_command.cc create mode 100644 source/blender/draw/intern/draw_command.hh create mode 100644 source/blender/draw/intern/draw_command_shared.hh delete mode 100644 source/blender/draw/intern/draw_debug.c create mode 100644 source/blender/draw/intern/draw_debug.cc create mode 100644 source/blender/draw/intern/draw_debug.hh create mode 100644 source/blender/draw/intern/draw_defines.h create mode 100644 source/blender/draw/intern/draw_handle.hh create mode 100644 source/blender/draw/intern/draw_manager.cc create mode 100644 source/blender/draw/intern/draw_manager.hh create mode 100644 source/blender/draw/intern/draw_pass.hh create mode 100644 source/blender/draw/intern/draw_resource.cc create mode 100644 source/blender/draw/intern/draw_resource.hh create mode 100644 source/blender/draw/intern/draw_state.h create mode 100644 source/blender/draw/intern/draw_view.cc create mode 100644 source/blender/draw/intern/draw_view.hh delete mode 100644 source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc create mode 100644 source/blender/draw/intern/shaders/common_aabb_lib.glsl create mode 100644 source/blender/draw/intern/shaders/common_debug_draw_lib.glsl create mode 100644 source/blender/draw/intern/shaders/common_debug_print_lib.glsl create mode 100644 source/blender/draw/intern/shaders/common_debug_shape_lib.glsl create mode 100644 source/blender/draw/intern/shaders/common_intersect_lib.glsl create mode 100644 source/blender/draw/intern/shaders/common_shape_lib.glsl create mode 100644 source/blender/draw/intern/shaders/draw_command_generate_comp.glsl create mode 100644 source/blender/draw/intern/shaders/draw_debug_draw_display_frag.glsl create mode 100644 source/blender/draw/intern/shaders/draw_debug_draw_display_vert.glsl create mode 100644 source/blender/draw/intern/shaders/draw_debug_info.hh create mode 100644 source/blender/draw/intern/shaders/draw_debug_print_display_frag.glsl create mode 100644 source/blender/draw/intern/shaders/draw_debug_print_display_vert.glsl create mode 100644 source/blender/draw/intern/shaders/draw_resource_finalize_comp.glsl create mode 100644 source/blender/draw/intern/shaders/draw_visibility_comp.glsl create mode 100644 source/blender/draw/tests/draw_pass_test.cc delete mode 100644 source/blender/editors/include/ED_types.h delete mode 100644 source/blender/editors/interface/abstract_view.cc create mode 100644 source/blender/editors/interface/eyedroppers/eyedropper_color.c create mode 100644 source/blender/editors/interface/eyedroppers/eyedropper_colorband.c create mode 100644 source/blender/editors/interface/eyedroppers/eyedropper_datablock.c create mode 100644 source/blender/editors/interface/eyedroppers/eyedropper_depth.c create mode 100644 source/blender/editors/interface/eyedroppers/eyedropper_driver.c create mode 100644 source/blender/editors/interface/eyedroppers/eyedropper_gpencil_color.c create mode 100644 source/blender/editors/interface/eyedroppers/eyedropper_intern.h create mode 100644 source/blender/editors/interface/eyedroppers/interface_eyedropper.c delete mode 100644 source/blender/editors/interface/grid_view.cc delete mode 100644 source/blender/editors/interface/interface_anim.c create mode 100644 source/blender/editors/interface/interface_anim.cc delete mode 100644 source/blender/editors/interface/interface_eyedropper.c delete mode 100644 source/blender/editors/interface/interface_eyedropper_color.c delete mode 100644 source/blender/editors/interface/interface_eyedropper_colorband.c delete mode 100644 source/blender/editors/interface/interface_eyedropper_datablock.c delete mode 100644 source/blender/editors/interface/interface_eyedropper_depth.c delete mode 100644 source/blender/editors/interface/interface_eyedropper_driver.c delete mode 100644 source/blender/editors/interface/interface_eyedropper_gpencil_color.c delete mode 100644 source/blender/editors/interface/interface_eyedropper_intern.h delete mode 100644 source/blender/editors/interface/interface_ops.c create mode 100644 source/blender/editors/interface/interface_ops.cc delete mode 100644 source/blender/editors/interface/interface_panel.c create mode 100644 source/blender/editors/interface/interface_panel.cc delete mode 100644 source/blender/editors/interface/interface_region_tooltip.c create mode 100644 source/blender/editors/interface/interface_region_tooltip.cc delete mode 100644 source/blender/editors/interface/interface_regions_intern.h create mode 100644 source/blender/editors/interface/interface_regions_intern.hh delete mode 100644 source/blender/editors/interface/interface_template_search_operator.c create mode 100644 source/blender/editors/interface/interface_template_search_operator.cc delete mode 100644 source/blender/editors/interface/interface_undo.c create mode 100644 source/blender/editors/interface/interface_undo.cc delete mode 100644 source/blender/editors/interface/interface_view.cc delete mode 100644 source/blender/editors/interface/tree_view.cc create mode 100644 source/blender/editors/interface/views/abstract_view.cc create mode 100644 source/blender/editors/interface/views/abstract_view_item.cc create mode 100644 source/blender/editors/interface/views/grid_view.cc create mode 100644 source/blender/editors/interface/views/interface_view.cc create mode 100644 source/blender/editors/interface/views/tree_view.cc delete mode 100644 source/blender/editors/object/object_vgroup.c create mode 100644 source/blender/editors/object/object_vgroup.cc delete mode 100644 source/blender/editors/sculpt_paint/sculpt_face_set.c create mode 100644 source/blender/editors/sculpt_paint/sculpt_face_set.cc delete mode 100644 source/blender/editors/space_file/filelist.c create mode 100644 source/blender/editors/space_file/filelist.cc create mode 100644 source/blender/editors/space_file/folder_history.cc delete mode 100644 source/blender/editors/space_image/image_undo.c create mode 100644 source/blender/editors/space_image/image_undo.cc create mode 100644 source/blender/editors/space_node/add_node_search.cc create mode 100644 source/blender/editors/space_outliner/tree/tree_element_label.cc create mode 100644 source/blender/editors/space_outliner/tree/tree_element_label.hh delete mode 100644 source/blender/editors/space_view3d/view3d_select.c create mode 100644 source/blender/editors/space_view3d/view3d_select.cc create mode 100644 source/blender/editors/transform/transform_convert_mesh_vert_cdata.c delete mode 100644 source/blender/editors/uvedit/uvedit_islands.c create mode 100644 source/blender/editors/uvedit/uvedit_islands.cc create mode 100644 source/blender/functions/FN_lazy_function.hh create mode 100644 source/blender/functions/FN_lazy_function_execute.hh create mode 100644 source/blender/functions/FN_lazy_function_graph.hh create mode 100644 source/blender/functions/FN_lazy_function_graph_executor.hh create mode 100644 source/blender/functions/intern/lazy_function.cc create mode 100644 source/blender/functions/intern/lazy_function_execute.cc create mode 100644 source/blender/functions/intern/lazy_function_graph.cc create mode 100644 source/blender/functions/intern/lazy_function_graph_executor.cc create mode 100644 source/blender/functions/tests/FN_lazy_function_test.cc create mode 100644 source/blender/geometry/GEO_fillet_curves.hh create mode 100644 source/blender/geometry/GEO_trim_curves.hh create mode 100644 source/blender/geometry/intern/fillet_curves.cc create mode 100644 source/blender/geometry/intern/trim_curves.cc delete mode 100644 source/blender/geometry/intern/uv_parametrizer.c create mode 100644 source/blender/geometry/intern/uv_parametrizer.cc delete mode 100644 source/blender/gpu/GPU_glew.h delete mode 100644 source/blender/gpu/GPU_legacy_stubs.h create mode 100644 source/blender/gpu/metal/kernels/depth_2d_update_info.hh create mode 100644 source/blender/gpu/metal/kernels/gpu_shader_fullscreen_blit_info.hh create mode 100644 source/blender/gpu/metal/mtl_index_buffer.hh create mode 100644 source/blender/gpu/metal/mtl_index_buffer.mm create mode 100644 source/blender/gpu/metal/mtl_primitive.hh create mode 100644 source/blender/gpu/metal/mtl_pso_descriptor_state.hh create mode 100644 source/blender/gpu/metal/mtl_query.hh create mode 100644 source/blender/gpu/metal/mtl_query.mm create mode 100644 source/blender/gpu/metal/mtl_shader.hh create mode 100644 source/blender/gpu/metal/mtl_shader.mm create mode 100644 source/blender/gpu/metal/mtl_shader_generator.hh create mode 100644 source/blender/gpu/metal/mtl_shader_generator.mm create mode 100644 source/blender/gpu/metal/mtl_shader_interface.hh create mode 100644 source/blender/gpu/metal/mtl_shader_interface.mm create mode 100644 source/blender/gpu/metal/mtl_shader_interface_type.hh create mode 100644 source/blender/gpu/metal/mtl_shader_shared.h create mode 100644 source/blender/gpu/metal/mtl_uniform_buffer.hh create mode 100644 source/blender/gpu/metal/mtl_uniform_buffer.mm create mode 100644 source/blender/gpu/shaders/compositor/compositor_alpha_crop.glsl create mode 100644 source/blender/gpu/shaders/compositor/compositor_bilateral_blur.glsl create mode 100644 source/blender/gpu/shaders/compositor/compositor_blur.glsl create mode 100644 source/blender/gpu/shaders/compositor/compositor_bokeh_image.glsl create mode 100644 source/blender/gpu/shaders/compositor/compositor_box_mask.glsl create mode 100644 source/blender/gpu/shaders/compositor/compositor_convert.glsl create mode 100644 source/blender/gpu/shaders/compositor/compositor_despeckle.glsl create mode 100644 source/blender/gpu/shaders/compositor/compositor_directional_blur.glsl create mode 100644 source/blender/gpu/shaders/compositor/compositor_edge_filter.glsl create mode 100644 source/blender/gpu/shaders/compositor/compositor_ellipse_mask.glsl create mode 100644 source/blender/gpu/shaders/compositor/compositor_filter.glsl create mode 100644 source/blender/gpu/shaders/compositor/compositor_flip.glsl create mode 100644 source/blender/gpu/shaders/compositor/compositor_image_crop.glsl create mode 100644 source/blender/gpu/shaders/compositor/compositor_morphological_distance.glsl create mode 100644 source/blender/gpu/shaders/compositor/compositor_morphological_distance_feather.glsl create mode 100644 source/blender/gpu/shaders/compositor/compositor_morphological_distance_threshold.glsl create mode 100644 source/blender/gpu/shaders/compositor/compositor_morphological_step.glsl create mode 100644 source/blender/gpu/shaders/compositor/compositor_projector_lens_distortion.glsl create mode 100644 source/blender/gpu/shaders/compositor/compositor_realize_on_domain.glsl create mode 100644 source/blender/gpu/shaders/compositor/compositor_screen_lens_distortion.glsl create mode 100644 source/blender/gpu/shaders/compositor/compositor_set_alpha.glsl create mode 100644 source/blender/gpu/shaders/compositor/compositor_split_viewer.glsl create mode 100644 source/blender/gpu/shaders/compositor/compositor_symmetric_blur.glsl create mode 100644 source/blender/gpu/shaders/compositor/compositor_symmetric_separable_blur.glsl create mode 100644 source/blender/gpu/shaders/compositor/infos/compositor_alpha_crop_info.hh create mode 100644 source/blender/gpu/shaders/compositor/infos/compositor_bilateral_blur_info.hh create mode 100644 source/blender/gpu/shaders/compositor/infos/compositor_blur_info.hh create mode 100644 source/blender/gpu/shaders/compositor/infos/compositor_bokeh_image_info.hh create mode 100644 source/blender/gpu/shaders/compositor/infos/compositor_box_mask_info.hh create mode 100644 source/blender/gpu/shaders/compositor/infos/compositor_convert_info.hh create mode 100644 source/blender/gpu/shaders/compositor/infos/compositor_despeckle_info.hh create mode 100644 source/blender/gpu/shaders/compositor/infos/compositor_directional_blur_info.hh create mode 100644 source/blender/gpu/shaders/compositor/infos/compositor_edge_filter_info.hh create mode 100644 source/blender/gpu/shaders/compositor/infos/compositor_ellipse_mask_info.hh create mode 100644 source/blender/gpu/shaders/compositor/infos/compositor_filter_info.hh create mode 100644 source/blender/gpu/shaders/compositor/infos/compositor_flip_info.hh create mode 100644 source/blender/gpu/shaders/compositor/infos/compositor_image_crop_info.hh create mode 100644 source/blender/gpu/shaders/compositor/infos/compositor_morphological_distance_feather_info.hh create mode 100644 source/blender/gpu/shaders/compositor/infos/compositor_morphological_distance_info.hh create mode 100644 source/blender/gpu/shaders/compositor/infos/compositor_morphological_distance_threshold_info.hh create mode 100644 source/blender/gpu/shaders/compositor/infos/compositor_morphological_step_info.hh create mode 100644 source/blender/gpu/shaders/compositor/infos/compositor_projector_lens_distortion_info.hh create mode 100644 source/blender/gpu/shaders/compositor/infos/compositor_realize_on_domain_info.hh create mode 100644 source/blender/gpu/shaders/compositor/infos/compositor_screen_lens_distortion_info.hh create mode 100644 source/blender/gpu/shaders/compositor/infos/compositor_set_alpha_info.hh create mode 100644 source/blender/gpu/shaders/compositor/infos/compositor_split_viewer_info.hh create mode 100644 source/blender/gpu/shaders/compositor/infos/compositor_symmetric_blur_info.hh create mode 100644 source/blender/gpu/shaders/compositor/infos/compositor_symmetric_separable_blur_info.hh create mode 100644 source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_alpha_over.glsl create mode 100644 source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_blur_common.glsl create mode 100644 source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_bright_contrast.glsl create mode 100644 source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_channel_matte.glsl create mode 100644 source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_chroma_matte.glsl create mode 100644 source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_balance.glsl create mode 100644 source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_correction.glsl create mode 100644 source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_matte.glsl create mode 100644 source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_spill.glsl create mode 100644 source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_to_luminance.glsl create mode 100644 source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_difference_matte.glsl create mode 100644 source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_distance_matte.glsl create mode 100644 source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_exposure.glsl create mode 100644 source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_gamma.glsl create mode 100644 source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_hue_correct.glsl create mode 100644 source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_hue_saturation_value.glsl create mode 100644 source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_invert.glsl create mode 100644 source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_luminance_matte.glsl create mode 100644 source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_main.glsl create mode 100644 source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_map_value.glsl create mode 100644 source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_normal.glsl create mode 100644 source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_posterize.glsl create mode 100644 source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_separate_combine.glsl create mode 100644 source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_set_alpha.glsl create mode 100644 source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_store_output.glsl create mode 100644 source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_texture_utilities.glsl create mode 100644 source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_type_conversion.glsl delete mode 100644 source/blender/gpu/shaders/gpu_shader_2D_flat_color_vert.glsl delete mode 100644 source/blender/gpu/shaders/gpu_shader_2D_line_dashed_uniform_color_vert.glsl delete mode 100644 source/blender/gpu/shaders/gpu_shader_2D_smooth_color_frag.glsl delete mode 100644 source/blender/gpu/shaders/gpu_shader_2D_smooth_color_vert.glsl delete mode 100644 source/blender/gpu/shaders/gpu_shader_image_modulate_alpha_frag.glsl delete mode 100644 source/blender/gpu/shaders/infos/gpu_shader_2D_flat_color_info.hh delete mode 100644 source/blender/gpu/shaders/infos/gpu_shader_2D_image_color_info.hh delete mode 100644 source/blender/gpu/shaders/infos/gpu_shader_2D_smooth_color_info.hh delete mode 100644 source/blender/gpu/shaders/infos/gpu_shader_2D_uniform_color_info.hh delete mode 100644 source/blender/gpu/shaders/infos/gpu_shader_3D_image_modulate_alpha_info.hh delete mode 100644 source/blender/gpu/shaders/infos/gpu_shader_3D_line_dashed_uniform_color_info.hh create mode 100644 source/blender/gpu/shaders/material/gpu_shader_material_mix_color.glsl create mode 100644 source/blender/gpu/shaders/metal/mtl_shader_common.msl create mode 100644 source/blender/gpu/shaders/metal/mtl_shader_defines.msl create mode 100644 source/blender/makesrna/RNA_path.h create mode 100644 source/blender/makesrna/intern/rna_path.cc delete mode 100644 source/blender/modifiers/intern/MOD_nodes_evaluator.cc delete mode 100644 source/blender/modifiers/intern/MOD_nodes_evaluator.hh delete mode 100644 source/blender/nodes/NOD_geometry_nodes_eval_log.hh create mode 100644 source/blender/nodes/NOD_geometry_nodes_lazy_function.hh create mode 100644 source/blender/nodes/NOD_geometry_nodes_log.hh delete mode 100644 source/blender/nodes/NOD_node_tree_ref.hh create mode 100644 source/blender/nodes/geometry/nodes/node_geo_distribute_points_in_volume.cc create mode 100644 source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_curves.cc create mode 100644 source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_selection.cc delete mode 100644 source/blender/nodes/geometry/nodes/node_geo_field_on_domain.cc create mode 100644 source/blender/nodes/geometry/nodes/node_geo_input_shortest_edge_paths.cc create mode 100644 source/blender/nodes/geometry/nodes/node_geo_interpolate_domain.cc create mode 100644 source/blender/nodes/geometry/nodes/node_geo_mesh_face_set_boundaries.cc delete mode 100644 source/blender/nodes/intern/geometry_nodes_eval_log.cc create mode 100644 source/blender/nodes/intern/geometry_nodes_lazy_function.cc create mode 100644 source/blender/nodes/intern/geometry_nodes_log.cc delete mode 100644 source/blender/nodes/intern/node_tree_ref.cc create mode 100644 source/blender/nodes/shader/nodes/node_shader_mix.cc delete mode 100644 source/blender/render/intern/engine.c create mode 100644 source/blender/render/intern/engine.cc delete mode 100644 source/blender/render/intern/initrender.c create mode 100644 source/blender/render/intern/initrender.cc delete mode 100644 source/blender/render/intern/pipeline.c create mode 100644 source/blender/render/intern/pipeline.cc delete mode 100644 source/blender/render/intern/render_result.c create mode 100644 source/blender/render/intern/render_result.cc delete mode 100644 source/blender/windowmanager/intern/wm_dragdrop.c create mode 100644 source/blender/windowmanager/intern/wm_dragdrop.cc delete mode 100644 source/creator/blender.map delete mode 100644 source/creator/osx_locals.map create mode 100644 source/creator/symbols_apple.map create mode 100644 source/creator/symbols_unix.map (limited to 'source') 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(thumb->width) * + static_cast(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 #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; @@ -62,20 +68,93 @@ static void (*blf_draw_cache_flush)(void) = NULL; 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) @@ -253,11 +245,6 @@ void DM_copy_vert_data(struct DerivedMesh *source, int dest_index, 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. * 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 + #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 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 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 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. @@ -483,6 +526,14 @@ class AttributeAccessor { return VArray::ForSingle(default_value, this->domain_size(domain)); } + /** + * 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. */ @@ -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. @@ -554,6 +610,19 @@ class MutableAttributeAccessor : public AttributeAccessor { return attribute.typed(); } + /** + * Same as above, but returns a type that makes it easier to work with the attribute as a span. + */ + template + SpanAttributeWriter lookup_for_write_span(const AttributeIDRef &attribute_id) + { + AttributeWriter attribute = this->lookup_for_write(attribute_id); + if (attribute) { + return SpanAttributeWriter{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 @@ -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 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(); const eCustomDataType data_type = cpp_type_to_custom_data_type(cpp_type); @@ -610,7 +679,7 @@ class MutableAttributeAccessor : public AttributeAccessor { SpanAttributeWriter lookup_or_add_for_write_span( const AttributeIDRef &attribute_id, const eAttrDomain domain, - const AttributeInit &initializer = AttributeInitDefault()) + const AttributeInit &initializer = AttributeInitDefaultValue()) { AttributeWriter attribute = this->lookup_or_add_for_write( 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 lookup_or_add_for_write_only_span(const AttributeIDRef &attribute_id, const eAttrDomain domain) { - AttributeWriter attribute = this->lookup_or_add_for_write(attribute_id, domain); + AttributeWriter attribute = this->lookup_or_add_for_write( + attribute_id, domain, AttributeInitConstruct()); + if (attribute) { return SpanAttributeWriter{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 retrieve_attributes_for_transfer( + const bke::AttributeAccessor src_attributes, + bke::MutableAttributeAccessor dst_attributes, + eAttrDomainMask domain_mask, + const Set &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 &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 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 @@ -45,6 +45,58 @@ inline void convert_to_static_type(const eCustomDataType data_type, const Func & convert_to_static_type(cpp_type, func); } +/* -------------------------------------------------------------------- */ +/** \name Mix two values of the same type. + * + * This is just basic linear interpolation. + * \{ */ + +template 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(std::round((1.0f - factor) * a + factor * b)); +} + +template<> inline int mix2(const float factor, const int &a, const int &b) +{ + return static_cast(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. * @@ -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 T mix2(float factor, const T &a, const T &b); +template +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( + 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(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(std::round((1.0f - factor) * a + factor * b)); + return static_cast( + 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(mixed[0]), + static_cast(mixed[1]), + static_cast(mixed[2]), + static_cast(mixed[3])}; } /** \} */ @@ -188,10 +272,28 @@ template class SimpleMixer { * \param default_value: Output value for an element that has not been affected by a #mix_in. */ SimpleMixer(MutableSpan 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 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, ""); - 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 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 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 buffer) : buffer_(buffer) + BooleanPropagationMixer(MutableSpan 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 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 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 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(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 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 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 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 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 struct DefaultMixerStruct { @@ -381,12 +548,12 @@ template<> struct DefaultMixerStruct { using type = SimpleMixerWithAccumulationType; }; -template struct DefaultPropatationMixerStruct { +template struct DefaultPropagationMixerStruct { /* Use void by default. This can be checked for in `if constexpr` statements. */ using type = typename DefaultMixerStruct::type; }; -template<> struct DefaultPropatationMixerStruct { +template<> struct DefaultPropagationMixerStruct { using type = BooleanPropagationMixer; }; @@ -396,7 +563,7 @@ template<> struct DefaultPropatationMixerStruct { * (the default mixing for booleans). */ template -using DefaultPropatationMixer = typename DefaultPropatationMixerStruct::type; +using DefaultPropagationMixer = typename DefaultPropagationMixerStruct::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 positions; + /** + * Matrices that transform point translations on original data into corresponding translations in + * evaluated data. This may be empty if not available. + */ + Span 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 &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 +#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 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,11 +151,22 @@ 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 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> positions; + /** + * Matrices which transform point movement vectors from original data to corresponding movements + * of evaluated data. + */ + std::optional> 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 { /* -------------------------------------------------------------------- */ @@ -492,6 +537,16 @@ bool segment_is_vector(Span handle_types_left, Span handle_types_right, 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 handle_types_left, int resolution, MutableSpan 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. + * *
  *           handle_prev         handle_next
  *                x-----------------x
@@ -650,6 +709,29 @@ void interpolate_to_evaluated(const GSpan src,
                               const Span 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
+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) {
+    /* Save multiplications by adjusting weights after mix. */
+    return 0.5f * attribute_math::mix4(n, a, b, c, d);
+  }
+  else {
+    return attribute_math::mix4(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 calculate_type_counts(const VArray &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 handle_types_left,
                            const Span 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 ©)
+        : index_(copy.index_), begin_(copy.begin_), end_(copy.end_), cycles_(copy.cycles_)
+    {
+    }
+    ~CyclicIterator() = default;
+
+    constexpr CyclicIterator &operator=(const CyclicIterator ©)
+    {
+      if (this == ©) {
+        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
 void copy_point_data(const CurvesGeometry &src_curves,
                      const CurvesGeometry &dst_curves,
-                     const IndexMask src_curve_selection,
-                     const Span src,
+                     IndexMask src_curve_selection,
+                     Span src,
                      MutableSpan 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
 void fill_points(const CurvesGeometry &curves,
-                 const IndexMask curve_selection,
+                 IndexMask curve_selection,
                  const T &value,
                  MutableSpan dst)
 {
   fill_points(curves, curve_selection, &value, dst);
 }
 
+void fill_points(const CurvesGeometry &curves,
+                 Span curve_ranges,
+                 GPointer value,
+                 GMutableSpan dst);
+
+template
+void fill_points(const CurvesGeometry &curves,
+                 Span curve_ranges,
+                 const T &value,
+                 MutableSpan 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 &types,
                            FunctionRef bezier_fn,
                            FunctionRef 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);
@@ -429,6 +451,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 &layers_to_write);
+                                    blender::Vector &layers_to_write,
+                                    const blender::Set &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 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 curve_normals_varray(const CurveComponent &component, const eAttrDomain domain);
+VArray curve_normals_varray(const CurvesGeometry &curves, const eAttrDomain domain);
 
-VArray mesh_normals_varray(const MeshComponent &mesh_component,
-                                   const Mesh &mesh,
-                                   const IndexMask mask,
-                                   eAttrDomain domain);
+VArray 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{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 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 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 attributes() const final;
-  std::optional 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 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 create(StringRefNull prop_name, d
 std::unique_ptr create(StringRefNull prop_name,
                                                       const StringRefNull value);
 
+/** \brief Allocate a new IDProperty of type IDP_ID, set its name and value. */
+std::unique_ptr 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 create(StringRefNull prop_name,
                                                       Span 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 create(StringRefNull prop_name, Span 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,12 +400,9 @@ 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).
  *
@@ -428,14 +427,39 @@ 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,
                                      uint duplicate_flags,
                                      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
@@ -59,6 +59,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.
  */
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
@@ -54,19 +54,10 @@ 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,9 +91,17 @@ 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 Mesh::verts() const
+{
+  return {BKE_mesh_verts(this), this->totvert};
+}
+inline blender::MutableSpan Mesh::verts_for_write()
+{
+  return {BKE_mesh_verts_for_write(this), this->totvert};
+}
+
+inline blender::Span Mesh::edges() const
+{
+  return {BKE_mesh_edges(this), this->totedge};
+}
+inline blender::MutableSpan Mesh::edges_for_write()
+{
+  return {BKE_mesh_edges_for_write(this), this->totedge};
+}
+
+inline blender::Span Mesh::polys() const
+{
+  return {BKE_mesh_polys(this), this->totpoly};
+}
+inline blender::MutableSpan Mesh::polys_for_write()
+{
+  return {BKE_mesh_polys_for_write(this), this->totpoly};
+}
+
+inline blender::Span Mesh::loops() const
+{
+  return {BKE_mesh_loops(this), this->totloop};
+}
+inline blender::MutableSpan Mesh::loops_for_write()
+{
+  return {BKE_mesh_loops_for_write(this), this->totloop};
+}
+
+inline blender::Span Mesh::deform_verts() const
+{
+  return {BKE_mesh_deform_verts(this), this->totvert};
+}
+inline blender::MutableSpan 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
@@ -17,6 +17,35 @@ struct CustomData;
 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.
  *
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 looptri_indices,
                             Span 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 looptri_indices,
                              Span 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 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 positions_;
@@ -68,18 +66,14 @@ class MeshAttributeInterpolator {
 
  public:
   MeshAttributeInterpolator(const Mesh *mesh,
-                            const IndexMask mask,
-                            const Span positions,
-                            const Span looptri_indices);
+                            IndexMask mask,
+                            Span positions,
+                            Span 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 ensure_barycentric_coords();
@@ -133,8 +127,20 @@ int sample_surface_points_projected(
     Vector &r_looptri_indices,
     Vector &r_positions);
 
-float3 compute_bary_coord_in_triangle(const Mesh &mesh,
+float3 compute_bary_coord_in_triangle(Span verts,
+                                      Span loops,
                                       const MLoopTri &looptri,
                                       const float3 &position);
 
+template
+inline T sample_corner_attrribute_with_bary_coords(const float3 &bary_weights,
+                                                   const MLoopTri &looptri,
+                                                   const Span 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 ¶ms);
 
+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 
+#include 
 
-#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 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
+      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 allow_use_dirty_topology_cache = 0;
+
+  /** Only valid when #topology_cache_is_dirty is false. */
+  Vector nodes;
+  Vector links;
+  Vector sockets;
+  Vector input_sockets;
+  Vector output_sockets;
+  MultiValueMap nodes_by_type;
+  Vector toposort_left_to_right;
+  Vector toposort_right_to_left;
+  Vector 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 directly_linked_links;
+  Vector directly_linked_sockets;
+  Vector logically_linked_sockets;
+  Vector 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 inputs;
+  Vector outputs;
+  Vector internal_links;
+  Map inputs_by_identifier;
+  Map 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 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 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 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 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 bNodeTree::all_nodes() const
+{
+  BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+  return this->runtime->nodes;
+}
+
+inline blender::Span bNodeTree::all_nodes()
+{
+  BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+  return this->runtime->nodes;
+}
+
+inline blender::Span bNodeTree::group_nodes() const
+{
+  BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+  return this->runtime->group_nodes;
+}
+
+inline blender::Span 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 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 bNodeTree::all_input_sockets()
+{
+  BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+  return this->runtime->input_sockets;
+}
+
+inline blender::Span 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 bNodeTree::all_output_sockets()
+{
+  BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+  return this->runtime->output_sockets;
+}
+
+inline blender::Span bNodeTree::all_sockets() const
+{
+  BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+  return this->runtime->sockets;
+}
+
+inline blender::Span 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 bNode::input_sockets()
+{
+  BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+  return this->runtime->inputs;
+}
+
+inline blender::Span bNode::output_sockets()
+{
+  BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+  return this->runtime->outputs;
+}
+
+inline blender::Span bNode::input_sockets() const
+{
+  BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+  return this->runtime->inputs;
+}
+
+inline blender::Span 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 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 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 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 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 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::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 const T *bNodeSocket::default_value_typed() const
+{
+  return static_cast(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 
+
+#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> elem_groups_;
+
+ public:
+  ~TreeHash();
+
+  /** Create and fill hash-table with treestore elements */
+  static std::unique_ptr 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 
-
-#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;
-
-/**
- * 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 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 evaluated_normals_cache_;
-  mutable std::mutex normal_cache_mutex_;
-  mutable bool normal_cache_dirty_ = true;
-
-  /** Accumulated lengths along the evaluated points. */
-  mutable blender::Vector 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 positions() = 0;
-  virtual blender::Span positions() const = 0;
-  virtual blender::MutableSpan radii() = 0;
-  virtual blender::Span radii() const = 0;
-  virtual blender::MutableSpan tilts() = 0;
-  virtual blender::Span 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 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 evaluated_lengths() const;
-  /**
-   * Return non-owning access to the direction of the curve at each evaluated point.
-   */
-  blender::Span 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 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 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 index_factors,
-                                 blender::GMutableSpan dst) const;
-  template
-  void sample_with_index_factors(const blender::VArray &src,
-                                 blender::Span index_factors,
-                                 blender::MutableSpan dst) const
-  {
-    this->sample_with_index_factors(
-        blender::GVArray(src), index_factors, blender::GMutableSpan(dst));
-  }
-  template
-  void sample_with_index_factors(blender::Span src,
-                                 blender::Span index_factors,
-                                 blender::MutableSpan dst) const
-  {
-    this->sample_with_index_factors(blender::VArray::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 blender::VArray interpolate_to_evaluated(blender::Span data) const
-  {
-    return this->interpolate_to_evaluated(blender::GSpan(data)).typed();
-  }
-
- 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 positions_;
-  blender::Vector radii_;
-  blender::Vector tilts_;
-  int resolution_;
-
-  blender::Vector handle_types_left_;
-  blender::Vector handle_types_right_;
-
-  /* These are mutable to allow lazy recalculation of #Auto and #Vector handle positions. */
-  mutable blender::Vector handle_positions_left_;
-  mutable blender::Vector 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 offset_cache_;
-  mutable std::mutex offset_cache_mutex_;
-  mutable bool offset_cache_dirty_ = true;
-
-  /** Cache of evaluated positions. */
-  mutable blender::Vector 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 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 positions() final;
-  blender::Span positions() const final;
-  blender::MutableSpan radii() final;
-  blender::Span radii() const final;
-  blender::MutableSpan tilts() final;
-  blender::Span tilts() const final;
-  blender::Span handle_types_left() const;
-  blender::MutableSpan handle_types_left();
-  blender::Span 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 handle_positions_left(bool write_only = false);
-  blender::Span handle_types_right() const;
-  blender::MutableSpan handle_types_right();
-  blender::Span 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 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 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 evaluated_mappings() const;
-  blender::Span 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 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.
-   *
-   * 
-   *           handle_prev         handle_next
-   *                x----------------x
-   *               /                  \
-   *              /      x---O---x     \
-   *             /        result        \
-   *            /                        \
-   *           O                          O
-   *       point_prev                  point_next
-   * 
- */ - 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 weights; - /** - * An offset for the start of #weights: the first control point index with a non-zero weight. - */ - blender::Vector start_indices; - }; - - private: - blender::Vector positions_; - blender::Vector radii_; - blender::Vector tilts_; - blender::Vector 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 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 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 positions() final; - blender::Span positions() const final; - blender::MutableSpan radii() final; - blender::Span radii() const final; - blender::MutableSpan tilts() final; - blender::Span tilts() const final; - blender::Span knots() const; - - blender::MutableSpan weights(); - blender::Span weights() const; - - void mark_cache_invalid() final; - int evaluated_points_num() const final; - - blender::Span 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 positions_; - blender::Vector radii_; - blender::Vector 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 positions() final; - blender::Span positions() const final; - blender::MutableSpan radii() final; - blender::Span radii() const final; - blender::MutableSpan tilts() final; - blender::Span tilts() const final; - - void mark_cache_invalid() final; - int evaluated_points_num() const final; - - blender::Span 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 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 splines() const; - blender::MutableSpan 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 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 control_point_offsets() const; - /** - * Exactly like #control_point_offsets, but uses the number of evaluated points instead. - */ - blender::Array 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 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 curve_eval_from_dna_curve(const Curve &curve, - const ListBase &nurbs_list); -std::unique_ptr curve_eval_from_dna_curve(const Curve &dna_curve); -std::unique_ptr 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 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(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 rest_positions = + attributes.lookup_or_add_for_write_only_span("rest_position", ATTR_DOMAIN_POINT); + if (rest_positions) { + attributes.lookup("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 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 verts = mesh->verts(); + const Span polys = mesh->polys(); + const Span loops = mesh->loops(); + + const MPoly *mp = polys.data(); int i, j, k; blender::Vector 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 +#include #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 get_attribute_accessor_for_write( + ID &id) +{ + switch (GS(id.name)) { + case ID_ME: { + Mesh &mesh = reinterpret_cast(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(id); + return pointcloud.attributes_for_write(); + } + case ID_CV: { + Curves &curves_id = reinterpret_cast(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(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 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(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 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(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(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 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(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(initializer).data; + void *source_data = static_cast(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(initializer).varray; + varray.materialize_to_uninitialized(varray.index_range(), data); } - const GVArray &varray = static_cast(initializer).varray; - varray.materialize_to_uninitialized(varray.index_range(), data); - return true; + break; } case AttributeInit::Type::MoveArray: { - void *source_data = static_cast(initializer).data; + void *source_data = static_cast(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 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 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 new_order) -{ - BLI_assert(new_order.size() == data.totlayer); - - Map old_order; - old_order.reserve(data.totlayer); - Array 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(&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(&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(&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(&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 domain, const std::optional 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 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(); + 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 retrieve_attributes_for_transfer( + const bke::AttributeAccessor src_attributes, + bke::MutableAttributeAccessor dst_attributes, + const eAttrDomainMask domain_mask, + const Set &skip) +{ + Vector 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 &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; }; /** @@ -379,6 +392,21 @@ inline bool for_all(const void *owner, return true; } +template +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 inline bool contains(const void *owner, const blender::bke::AttributeIDRef &attribute_id) { @@ -490,6 +518,7 @@ inline AttributeAccessorFunctions accessor_functions_for_providers() lookup, nullptr, for_all, + lookup_validator, lookup_for_write, remove, add}; 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 output_buffer, +ColorGeometry4fMixer::ColorGeometry4fMixer(MutableSpan 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 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 buffer, - ColorGeometry4b default_color) + const ColorGeometry4b default_color) + : ColorGeometry4bMixer(buffer, buffer.index_range(), default_color) +{ +} + +ColorGeometry4bMixer::ColorGeometry4bMixer(MutableSpan 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 &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 verts = mesh->verts(); + const Span edges = mesh->edges(); + const Span 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 positions = attributes.lookup_or_default( + "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(¶ms->viewplane) / + BLI_rctf_size_y(¶ms->viewplane))) : + (dist_span_y * (BLI_rctf_size_y(¶ms->viewplane) / + BLI_rctf_size_x(¶ms->viewplane))); - if ((dists[0] + dists[2]) > (dists[1] + dists[3])) { - scale_diff = (dists[1] + dists[3]) * - (BLI_rctf_size_x(¶ms->viewplane) / BLI_rctf_size_y(¶ms->viewplane)); - } - else { - scale_diff = (dists[0] + dists[2]) * - (BLI_rctf_size_y(¶ms->viewplane) / BLI_rctf_size_x(¶ms->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 #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.c deleted file mode 100644 index 14e862c2377..00000000000 --- a/source/blender/blenkernel/intern/crazyspace.c +++ /dev/null @@ -1,584 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright 2005 Blender Foundation. All rights reserved. */ - -/** \file - * \ingroup bke - */ - -#include "MEM_guardedalloc.h" - -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" -#include "DNA_modifier_types.h" -#include "DNA_object_types.h" -#include "DNA_scene_types.h" - -#include "BLI_bitmap.h" -#include "BLI_linklist.h" -#include "BLI_utildefines.h" - -#include "BKE_DerivedMesh.h" -#include "BKE_crazyspace.h" -#include "BKE_editmesh.h" -#include "BKE_lib_id.h" -#include "BKE_mesh.h" -#include "BKE_mesh_wrapper.h" -#include "BKE_modifier.h" -#include "BKE_multires.h" -#include "BKE_report.h" - -#include "DEG_depsgraph_query.h" - -BLI_INLINE void tan_calc_quat_v3(float r_quat[4], - const float co_1[3], - const float co_2[3], - const float co_3[3]) -{ - float vec_u[3], vec_v[3]; - float nor[3]; - - sub_v3_v3v3(vec_u, co_1, co_2); - sub_v3_v3v3(vec_v, co_1, co_3); - - cross_v3_v3v3(nor, vec_u, vec_v); - - if (normalize_v3(nor) > FLT_EPSILON) { - const float zero_vec[3] = {0.0f}; - tri_to_quat_ex(r_quat, zero_vec, vec_u, vec_v, nor); - } - else { - unit_qt(r_quat); - } -} - -static void set_crazy_vertex_quat(float r_quat[4], - const float co_1[3], - const float co_2[3], - const float co_3[3], - const float vd_1[3], - const float vd_2[3], - const float vd_3[3]) -{ - float q1[4], q2[4]; - - tan_calc_quat_v3(q1, co_1, co_2, co_3); - tan_calc_quat_v3(q2, vd_1, vd_2, vd_3); - - sub_qt_qtqt(r_quat, q2, q1); -} - -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); - - ModifierData *md = ob->modifiers.first; - for (int i = 0; md && i <= cageIndex; i++, md = md->next) { - if (md->type == eModifierType_Subsurf) { - md->mode ^= eModifierMode_DisableTemporary; - disabled = true; - } - } - - return disabled; -} - -float (*BKE_crazyspace_get_mapped_editverts(struct Depsgraph *depsgraph, Object *obedit))[3] -{ - 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; - 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 */ - makeDerivedMesh(depsgraph, scene_eval, obedit_eval, &CD_MASK_BAREMESH); - } - - /* now get the cage */ - Mesh *mesh_eval_cage = editbmesh_get_eval_cage_from_orig( - depsgraph, scene, obedit, &CD_MASK_BAREMESH); - - const int nverts = editmesh_eval->bm->totvert; - float(*vertexcos)[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 */ - modifiers_disable_subsurf_temporary(scene_eval, obedit_eval); - - return vertexcos; -} - -void BKE_crazyspace_set_quats_editmesh(BMEditMesh *em, - float (*origcos)[3], - float (*mappedcos)[3], - float (*quats)[4], - const bool use_select) -{ - BMFace *f; - BMIter iter; - int index; - - { - BMVert *v; - BM_ITER_MESH_INDEX (v, &iter, em->bm, BM_VERTS_OF_MESH, index) { - BM_elem_flag_disable(v, BM_ELEM_TAG); - BM_elem_index_set(v, index); /* set_inline */ - } - em->bm->elem_index_dirty &= ~BM_VERT; - } - - BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) { - BMLoop *l_iter, *l_first; - - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - if (BM_elem_flag_test(l_iter->v, BM_ELEM_HIDDEN) || - BM_elem_flag_test(l_iter->v, BM_ELEM_TAG) || - (use_select && !BM_elem_flag_test(l_iter->v, BM_ELEM_SELECT))) { - continue; - } - - if (!BM_elem_flag_test(l_iter->v, BM_ELEM_TAG)) { - const float *co_prev, *co_curr, *co_next; /* orig */ - const float *vd_prev, *vd_curr, *vd_next; /* deform */ - - const int i_prev = BM_elem_index_get(l_iter->prev->v); - const int i_curr = BM_elem_index_get(l_iter->v); - const int i_next = BM_elem_index_get(l_iter->next->v); - - /* retrieve mapped coordinates */ - vd_prev = mappedcos[i_prev]; - vd_curr = mappedcos[i_curr]; - vd_next = mappedcos[i_next]; - - if (origcos) { - co_prev = origcos[i_prev]; - co_curr = origcos[i_curr]; - co_next = origcos[i_next]; - } - else { - co_prev = l_iter->prev->v->co; - co_curr = l_iter->v->co; - co_next = l_iter->next->v->co; - } - - set_crazy_vertex_quat(quats[i_curr], co_curr, co_next, co_prev, vd_curr, vd_next, vd_prev); - - BM_elem_flag_enable(l_iter->v, BM_ELEM_TAG); - } - } while ((l_iter = l_iter->next) != l_first); - } -} - -void BKE_crazyspace_set_quats_mesh(Mesh *me, - float (*origcos)[3], - float (*mappedcos)[3], - float (*quats)[4]) -{ - 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; - - 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 j = 0; j < mp->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 */ - - /* retrieve mapped coordinates */ - vd_prev = mappedcos[ml_prev->v]; - vd_curr = mappedcos[ml_curr->v]; - vd_next = mappedcos[ml_next->v]; - - if (origcos) { - co_prev = origcos[ml_prev->v]; - co_curr = origcos[ml_curr->v]; - 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; - } - - set_crazy_vertex_quat( - quats[ml_curr->v], co_curr, co_next, co_prev, vd_curr, vd_next, vd_prev); - - BLI_BITMAP_ENABLE(vert_tag, ml_curr->v); - } - - ml_prev = ml_curr; - ml_curr = ml_next; - ml_next++; - } - } - - MEM_freeN(vert_tag); -} - -int BKE_crazyspace_get_first_deform_matrices_editbmesh(struct Depsgraph *depsgraph, - Scene *scene, - Object *ob, - BMEditMesh *em, - float (**deformmats)[3][3], - float (**deformcos)[3]) -{ - ModifierData *md; - Mesh *me_input = ob->data; - Mesh *me = NULL; - 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; - VirtualModifierData virtualModifierData; - ModifierEvalContext mectx = {depsgraph, ob, 0}; - - BKE_modifiers_clear_errors(ob); - - md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData); - - /* compute the deformation matrices and coordinates for the first - * 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); - - if (!editbmesh_modifier_is_enabled(scene, ob, md, me != NULL)) { - continue; - } - - if (mti->type == eModifierTypeType_OnlyDeform && mti->deformMatricesEM) { - if (!defmats) { - 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); - cd_mask_extra = datamasks->mask; - BLI_linklist_free((LinkNode *)datamasks, NULL); - - me = BKE_mesh_wrapper_from_editmesh_with_coords(em, &cd_mask_extra, NULL, me_input); - deformedVerts = editbmesh_vert_coords_alloc(em, &verts_num); - defmats = MEM_mallocN(sizeof(*defmats) * verts_num, "defmats"); - - for (a = 0; a < verts_num; a++) { - unit_m3(defmats[a]); - } - } - mti->deformMatricesEM(md, &mectx, em, me, deformedVerts, defmats, verts_num); - } - else { - break; - } - } - - for (; md && i <= cageIndex; md = md->next, i++) { - if (editbmesh_modifier_is_enabled(scene, ob, md, me != NULL) && - BKE_modifier_is_correctable_deformed(md)) { - modifiers_left_num++; - } - } - - if (me) { - BKE_id_free(NULL, me); - } - - *deformmats = defmats; - *deformcos = deformedVerts; - - return modifiers_left_num; -} - -/** - * Crazy-space evaluation needs to have an object which has all the fields - * evaluated, but the mesh data being at undeformed state. This way it can - * re-apply modifiers and also have proper pointers to key data blocks. - * - * Similar to #BKE_object_eval_reset(), but does not modify the actual evaluated object. - */ -static void crazyspace_init_object_for_eval(struct Depsgraph *depsgraph, - Object *object, - 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->data = object_crazy->runtime.data_orig; - } -} - -static void crazyspace_init_verts_and_matrices(const Mesh *mesh, - float (**deformmats)[3][3], - float (**deformcos)[3]) -{ - int verts_num; - *deformcos = BKE_mesh_vert_coords_alloc(mesh, &verts_num); - *deformmats = MEM_callocN(sizeof(**deformmats) * verts_num, "defmats"); - for (int a = 0; a < verts_num; a++) { - unit_m3((*deformmats)[a]); - } - BLI_assert(verts_num == mesh->totvert); -} - -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); - return (mti->type == eModifierTypeType_OnlyDeform); -} - -static bool crazyspace_modifier_supports_deform(ModifierData *md) -{ - const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); - return (mti->type == eModifierTypeType_OnlyDeform); -} - -int BKE_sculpt_get_first_deform_matrices(struct Depsgraph *depsgraph, - Scene *scene, - Object *object, - float (**deformmats)[3][3], - float (**deformcos)[3]) -{ - ModifierData *md; - Mesh *me_eval = NULL; - float(*defmats)[3][3] = NULL, (*deformedVerts)[3] = NULL; - 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); - 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}; - - if (is_sculpt_mode && has_multires) { - *deformmats = NULL; - *deformcos = NULL; - return modifiers_left_num; - } - - md = BKE_modifiers_get_virtual_modifierlist(&object_eval, &virtualModifierData); - - for (; md; md = md->next) { - if (!BKE_modifier_is_enabled(scene, md, eModifierMode_Realtime)) { - continue; - } - - if (crazyspace_modifier_supports_deform_matrices(md)) { - const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); - if (defmats == NULL) { - /* NOTE: Evaluated object is re-set to its original un-deformed state. */ - Mesh *me = object_eval.data; - me_eval = BKE_mesh_copy_for_eval(me, true); - crazyspace_init_verts_and_matrices(me_eval, &defmats, &deformedVerts); - } - - if (mti->deformMatrices) { - mti->deformMatrices(md, &mectx, me_eval, deformedVerts, defmats, me_eval->totvert); - } - else { - /* More complex handling will continue in BKE_crazyspace_build_sculpt. - * Exiting the loop on a non-deform modifier causes issues - T71213. */ - BLI_assert(crazyspace_modifier_supports_deform(md)); - break; - } - } - } - - for (; md; md = md->next) { - if (!BKE_modifier_is_enabled(scene, md, eModifierMode_Realtime)) { - continue; - } - - if (crazyspace_modifier_supports_deform(md)) { - modifiers_left_num++; - } - } - - if (me_eval != NULL) { - BKE_id_free(NULL, me_eval); - } - - *deformmats = defmats; - *deformcos = deformedVerts; - - return modifiers_left_num; -} - -void BKE_crazyspace_build_sculpt(struct Depsgraph *depsgraph, - Scene *scene, - Object *object, - float (**deformmats)[3][3], - float (**deformcos)[3]) -{ - int totleft = BKE_sculpt_get_first_deform_matrices( - depsgraph, scene, object, deformmats, deformcos); - - if (totleft) { - /* There are deformation modifier which doesn't support deformation matrices calculation. - * Need additional crazy-space correction. */ - - Mesh *mesh = (Mesh *)object->data; - Mesh *mesh_eval = NULL; - - if (*deformcos == NULL) { - crazyspace_init_verts_and_matrices(mesh, deformmats, deformcos); - } - - float(*deformedVerts)[3] = *deformcos; - float(*origVerts)[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}; - - for (; md; md = md->next) { - if (!BKE_modifier_is_enabled(scene, md, eModifierMode_Realtime)) { - continue; - } - - if (crazyspace_modifier_supports_deform(md)) { - const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); - - /* skip leading modifiers which have been already - * handled in sculpt_get_first_deform_matrices */ - if (mti->deformMatrices && !deformed) { - continue; - } - - if (mesh_eval == NULL) { - mesh_eval = BKE_mesh_copy_for_eval(mesh, true); - } - - mti->deformVerts(md, &mectx, mesh_eval, deformedVerts, mesh_eval->totvert); - deformed = 1; - } - } - - quats = MEM_mallocN(mesh->totvert * sizeof(*quats), "crazy quats"); - - BKE_crazyspace_set_quats_mesh(mesh, origVerts, deformedVerts, quats); - - for (i = 0; i < mesh->totvert; i++) { - float qmat[3][3], tmat[3][3]; - - quat_to_mat3(qmat, quats[i]); - mul_m3_m3m3(tmat, qmat, (*deformmats)[i]); - copy_m3_m3((*deformmats)[i], tmat); - } - - MEM_freeN(origVerts); - MEM_freeN(quats); - - if (mesh_eval != NULL) { - BKE_id_free(NULL, mesh_eval); - } - } - - if (*deformmats == NULL) { - 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"); - - for (a = 0; a < verts_num; a++) { - unit_m3((*deformmats)[a]); - } - } -} - -/* -------------------------------------------------------------------- */ -/** \name Crazyspace API - * \{ */ - -void BKE_crazyspace_api_eval(Depsgraph *depsgraph, - Scene *scene, - Object *object, - struct ReportList *reports) -{ - if (object->runtime.crazyspace_deform_imats != NULL || - object->runtime.crazyspace_deform_cos != NULL) { - return; - } - - if (object->type != OB_MESH) { - BKE_report(reports, - RPT_ERROR, - "Crazyspace transformation is only available for Mesh type of objects"); - return; - } - - const Mesh *mesh = (const Mesh *)object->data; - object->runtime.crazyspace_verts_num = mesh->totvert; - BKE_crazyspace_build_sculpt(depsgraph, - scene, - object, - &object->runtime.crazyspace_deform_imats, - &object->runtime.crazyspace_deform_cos); -} - -void BKE_crazyspace_api_displacement_to_deformed(struct Object *object, - struct ReportList *reports, - int vertex_index, - float displacement[3], - float r_displacement_deformed[3]) -{ - if (vertex_index < 0 || vertex_index >= object->runtime.crazyspace_verts_num) { - BKE_reportf(reports, - RPT_ERROR, - "Invalid vertex index %d (expected to be within 0 to %d range)", - vertex_index, - object->runtime.crazyspace_verts_num); - return; - } - - mul_v3_m3v3(r_displacement_deformed, - object->runtime.crazyspace_deform_imats[vertex_index], - displacement); -} - -void BKE_crazyspace_api_displacement_to_original(struct Object *object, - struct ReportList *reports, - int vertex_index, - float displacement_deformed[3], - float r_displacement[3]) -{ - if (vertex_index < 0 || vertex_index >= object->runtime.crazyspace_verts_num) { - BKE_reportf(reports, - RPT_ERROR, - "Invalid vertex index %d (expected to be within 0 to %d range))", - vertex_index, - object->runtime.crazyspace_verts_num); - return; - } - - float mat[3][3]; - if (!invert_m3_m3(mat, object->runtime.crazyspace_deform_imats[vertex_index])) { - copy_v3_v3(r_displacement, displacement_deformed); - return; - } - - mul_v3_m3v3(r_displacement, mat, displacement_deformed); -} - -void BKE_crazyspace_api_eval_clear(Object *object) -{ - MEM_SAFE_FREE(object->runtime.crazyspace_deform_imats); - MEM_SAFE_FREE(object->runtime.crazyspace_deform_cos); -} - -/** \} */ diff --git a/source/blender/blenkernel/intern/crazyspace.cc b/source/blender/blenkernel/intern/crazyspace.cc new file mode 100644 index 00000000000..190e2d3bb7e --- /dev/null +++ b/source/blender/blenkernel/intern/crazyspace.cc @@ -0,0 +1,655 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2005 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup bke + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BLI_bitmap.h" +#include "BLI_linklist.h" +#include "BLI_utildefines.h" + +#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" +#include "BKE_modifier.h" +#include "BKE_multires.h" +#include "BKE_report.h" + +#include "DEG_depsgraph_query.h" + +BLI_INLINE void tan_calc_quat_v3(float r_quat[4], + const float co_1[3], + const float co_2[3], + const float co_3[3]) +{ + float vec_u[3], vec_v[3]; + float nor[3]; + + sub_v3_v3v3(vec_u, co_1, co_2); + sub_v3_v3v3(vec_v, co_1, co_3); + + cross_v3_v3v3(nor, vec_u, vec_v); + + if (normalize_v3(nor) > FLT_EPSILON) { + const float zero_vec[3] = {0.0f}; + tri_to_quat_ex(r_quat, zero_vec, vec_u, vec_v, nor); + } + else { + unit_qt(r_quat); + } +} + +static void set_crazy_vertex_quat(float r_quat[4], + const float co_1[3], + const float co_2[3], + const float co_3[3], + const float vd_1[3], + const float vd_2[3], + const float vd_3[3]) +{ + float q1[4], q2[4]; + + tan_calc_quat_v3(q1, co_1, co_2, co_3); + tan_calc_quat_v3(q2, vd_1, vd_2, vd_3); + + sub_qt_qtqt(r_quat, q2, q1); +} + +static bool modifiers_disable_subsurf_temporary(struct Scene *scene, Object *ob) +{ + bool disabled = false; + int cageIndex = BKE_modifiers_get_cage_index(scene, ob, nullptr, true); + + ModifierData *md = static_cast(ob->modifiers.first); + for (int i = 0; md && i <= cageIndex; i++, md = md->next) { + if (md->type == eModifierType_Subsurf) { + md->mode ^= eModifierMode_DisableTemporary; + disabled = true; + } + } + + return disabled; +} + +float (*BKE_crazyspace_get_mapped_editverts(struct Depsgraph *depsgraph, Object *obedit))[3] +{ + 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 = static_cast(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 derived-mesh. */ + makeDerivedMesh(depsgraph, scene_eval, obedit_eval, &CD_MASK_BAREMESH); + } + + /* now get the cage */ + Mesh *mesh_eval_cage = editbmesh_get_eval_cage_from_orig( + depsgraph, scene, obedit, &CD_MASK_BAREMESH); + + const int nverts = editmesh_eval->bm->totvert; + float(*vertexcos)[3] = static_cast( + 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 */ + modifiers_disable_subsurf_temporary(scene_eval, obedit_eval); + + return vertexcos; +} + +void BKE_crazyspace_set_quats_editmesh(BMEditMesh *em, + float (*origcos)[3], + float (*mappedcos)[3], + float (*quats)[4], + const bool use_select) +{ + BMFace *f; + BMIter iter; + int index; + + { + BMVert *v; + BM_ITER_MESH_INDEX (v, &iter, em->bm, BM_VERTS_OF_MESH, index) { + BM_elem_flag_disable(v, BM_ELEM_TAG); + BM_elem_index_set(v, index); /* set_inline */ + } + em->bm->elem_index_dirty &= ~BM_VERT; + } + + BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) { + BMLoop *l_iter, *l_first; + + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + if (BM_elem_flag_test(l_iter->v, BM_ELEM_HIDDEN) || + BM_elem_flag_test(l_iter->v, BM_ELEM_TAG) || + (use_select && !BM_elem_flag_test(l_iter->v, BM_ELEM_SELECT))) { + continue; + } + + if (!BM_elem_flag_test(l_iter->v, BM_ELEM_TAG)) { + const float *co_prev, *co_curr, *co_next; /* orig */ + const float *vd_prev, *vd_curr, *vd_next; /* deform */ + + const int i_prev = BM_elem_index_get(l_iter->prev->v); + const int i_curr = BM_elem_index_get(l_iter->v); + const int i_next = BM_elem_index_get(l_iter->next->v); + + /* retrieve mapped coordinates */ + vd_prev = mappedcos[i_prev]; + vd_curr = mappedcos[i_curr]; + vd_next = mappedcos[i_next]; + + if (origcos) { + co_prev = origcos[i_prev]; + co_curr = origcos[i_curr]; + co_next = origcos[i_next]; + } + else { + co_prev = l_iter->prev->v->co; + co_curr = l_iter->v->co; + co_next = l_iter->next->v->co; + } + + set_crazy_vertex_quat(quats[i_curr], co_curr, co_next, co_prev, vd_curr, vd_next, vd_prev); + + BM_elem_flag_enable(l_iter->v, BM_ELEM_TAG); + } + } while ((l_iter = l_iter->next) != l_first); + } +} + +void BKE_crazyspace_set_quats_mesh(Mesh *me, + float (*origcos)[3], + 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 */ + const Span verts = me->verts(); + const Span polys = me->polys(); + const Span loops = me->loops(); + + 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 < 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 */ + + /* retrieve mapped coordinates */ + vd_prev = mappedcos[ml_prev->v]; + vd_curr = mappedcos[ml_curr->v]; + vd_next = mappedcos[ml_next->v]; + + if (origcos) { + co_prev = origcos[ml_prev->v]; + co_curr = origcos[ml_curr->v]; + co_next = origcos[ml_next->v]; + } + else { + 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( + quats[ml_curr->v], co_curr, co_next, co_prev, vd_curr, vd_next, vd_prev); + + BLI_BITMAP_ENABLE(vert_tag, ml_curr->v); + } + + ml_prev = ml_curr; + ml_curr = ml_next; + ml_next++; + } + } + + MEM_freeN(vert_tag); +} + +int BKE_crazyspace_get_first_deform_matrices_editbmesh(struct Depsgraph *depsgraph, + Scene *scene, + Object *ob, + BMEditMesh *em, + float (**deformmats)[3][3], + float (**deformcos)[3]) +{ + ModifierData *md; + Mesh *me_input = static_cast(ob->data); + Mesh *me = nullptr; + int i, a, modifiers_left_num = 0, verts_num = 0; + 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, ModifierApplyFlag(0)}; + + BKE_modifiers_clear_errors(ob); + + md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData); + + /* compute the deformation matrices and coordinates for the first + * 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(static_cast(md->type)); + + if (!editbmesh_modifier_is_enabled(scene, ob, md, me != nullptr)) { + continue; + } + + if (mti->type == eModifierTypeType_OnlyDeform && mti->deformMatricesEM) { + if (!defmats) { + const int required_mode = eModifierMode_Realtime | eModifierMode_Editmode; + CustomData_MeshMasks cd_mask_extra = CD_MASK_BAREMESH; + CDMaskLink *datamasks = BKE_modifier_calc_data_masks( + scene, md, &cd_mask_extra, required_mode, nullptr, nullptr); + cd_mask_extra = datamasks->mask; + BLI_linklist_free((LinkNode *)datamasks, nullptr); + + me = BKE_mesh_wrapper_from_editmesh_with_coords(em, &cd_mask_extra, nullptr, me_input); + deformedVerts = editbmesh_vert_coords_alloc(em, &verts_num); + defmats = static_cast( + MEM_mallocN(sizeof(*defmats) * verts_num, "defmats")); + + for (a = 0; a < verts_num; a++) { + unit_m3(defmats[a]); + } + } + mti->deformMatricesEM(md, &mectx, em, me, deformedVerts, defmats, verts_num); + } + else { + break; + } + } + + for (; md && i <= cageIndex; md = md->next, i++) { + if (editbmesh_modifier_is_enabled(scene, ob, md, me != nullptr) && + BKE_modifier_is_correctable_deformed(md)) { + modifiers_left_num++; + } + } + + if (me) { + BKE_id_free(nullptr, me); + } + + *deformmats = defmats; + *deformcos = deformedVerts; + + return modifiers_left_num; +} + +/** + * Crazy-space evaluation needs to have an object which has all the fields + * evaluated, but the mesh data being at undeformed state. This way it can + * re-apply modifiers and also have proper pointers to key data blocks. + * + * Similar to #BKE_object_eval_reset(), but does not modify the actual evaluated object. + */ +static void crazyspace_init_object_for_eval(struct Depsgraph *depsgraph, + Object *object, + Object *object_crazy) +{ + Object *object_eval = DEG_get_evaluated_object(depsgraph, object); + *object_crazy = blender::dna::shallow_copy(*object_eval); + if (object_crazy->runtime.data_orig != nullptr) { + object_crazy->data = object_crazy->runtime.data_orig; + } +} + +static void crazyspace_init_verts_and_matrices(const Mesh *mesh, + float (**deformmats)[3][3], + float (**deformcos)[3]) +{ + int verts_num; + *deformcos = BKE_mesh_vert_coords_alloc(mesh, &verts_num); + *deformmats = static_cast( + MEM_callocN(sizeof(**deformmats) * verts_num, "defmats")); + for (int a = 0; a < verts_num; a++) { + unit_m3((*deformmats)[a]); + } + BLI_assert(verts_num == mesh->totvert); +} + +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(static_cast(md->type)); + return (mti->type == eModifierTypeType_OnlyDeform); +} + +static bool crazyspace_modifier_supports_deform(ModifierData *md) +{ + const ModifierTypeInfo *mti = BKE_modifier_get_info(static_cast(md->type)); + return (mti->type == eModifierTypeType_OnlyDeform); +} + +int BKE_sculpt_get_first_deform_matrices(struct Depsgraph *depsgraph, + Scene *scene, + Object *object, + float (**deformmats)[3][3], + float (**deformcos)[3]) +{ + ModifierData *md; + 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, false); + const bool is_sculpt_mode = (object->mode & OB_MODE_SCULPT) != 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 = nullptr; + *deformcos = nullptr; + return modifiers_left_num; + } + + md = BKE_modifiers_get_virtual_modifierlist(&object_eval, &virtualModifierData); + + for (; md; md = md->next) { + if (!BKE_modifier_is_enabled(scene, md, eModifierMode_Realtime)) { + continue; + } + + if (crazyspace_modifier_supports_deform_matrices(md)) { + const ModifierTypeInfo *mti = BKE_modifier_get_info(static_cast(md->type)); + if (defmats == nullptr) { + /* NOTE: Evaluated object is re-set to its original un-deformed state. */ + Mesh *me = static_cast(object_eval.data); + me_eval = BKE_mesh_copy_for_eval(me, true); + crazyspace_init_verts_and_matrices(me_eval, &defmats, &deformedVerts); + } + + if (mti->deformMatrices) { + mti->deformMatrices(md, &mectx, me_eval, deformedVerts, defmats, me_eval->totvert); + } + else { + /* More complex handling will continue in BKE_crazyspace_build_sculpt. + * Exiting the loop on a non-deform modifier causes issues - T71213. */ + BLI_assert(crazyspace_modifier_supports_deform(md)); + break; + } + } + } + + for (; md; md = md->next) { + if (!BKE_modifier_is_enabled(scene, md, eModifierMode_Realtime)) { + continue; + } + + if (crazyspace_modifier_supports_deform(md)) { + modifiers_left_num++; + } + } + + if (me_eval != nullptr) { + BKE_id_free(nullptr, me_eval); + } + + *deformmats = defmats; + *deformcos = deformedVerts; + + return modifiers_left_num; +} + +void BKE_crazyspace_build_sculpt(struct Depsgraph *depsgraph, + Scene *scene, + Object *object, + float (**deformmats)[3][3], + float (**deformcos)[3]) +{ + int totleft = BKE_sculpt_get_first_deform_matrices( + depsgraph, scene, object, deformmats, deformcos); + + if (totleft) { + /* There are deformation modifier which doesn't support deformation matrices calculation. + * Need additional crazy-space correction. */ + + Mesh *mesh = (Mesh *)object->data; + Mesh *mesh_eval = nullptr; + + if (*deformcos == nullptr) { + crazyspace_init_verts_and_matrices(mesh, deformmats, deformcos); + } + + float(*deformedVerts)[3] = *deformcos; + float(*origVerts)[3] = static_cast(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, ModifierApplyFlag(0)}; + + for (; md; md = md->next) { + if (!BKE_modifier_is_enabled(scene, md, eModifierMode_Realtime)) { + continue; + } + + if (crazyspace_modifier_supports_deform(md)) { + const ModifierTypeInfo *mti = BKE_modifier_get_info(static_cast(md->type)); + + /* skip leading modifiers which have been already + * handled in sculpt_get_first_deform_matrices */ + if (mti->deformMatrices && !deformed) { + continue; + } + + if (mesh_eval == nullptr) { + mesh_eval = BKE_mesh_copy_for_eval(mesh, true); + } + + mti->deformVerts(md, &mectx, mesh_eval, deformedVerts, mesh_eval->totvert); + deformed = 1; + } + } + + quats = static_cast(MEM_mallocN(mesh->totvert * sizeof(*quats), "crazy quats")); + + BKE_crazyspace_set_quats_mesh(mesh, origVerts, deformedVerts, quats); + + for (i = 0; i < mesh->totvert; i++) { + float qmat[3][3], tmat[3][3]; + + quat_to_mat3(qmat, quats[i]); + mul_m3_m3m3(tmat, qmat, (*deformmats)[i]); + copy_m3_m3((*deformmats)[i], tmat); + } + + MEM_freeN(origVerts); + MEM_freeN(quats); + + if (mesh_eval != nullptr) { + BKE_id_free(nullptr, mesh_eval); + } + } + + if (*deformmats == nullptr) { + int a, verts_num; + Mesh *mesh = (Mesh *)object->data; + + *deformcos = BKE_mesh_vert_coords_alloc(mesh, &verts_num); + *deformmats = static_cast( + MEM_callocN(sizeof(*(*deformmats)) * verts_num, "defmats")); + + for (a = 0; a < verts_num; a++) { + unit_m3((*deformmats)[a]); + } + } +} + +/* -------------------------------------------------------------------- */ +/** \name Crazyspace API + * \{ */ + +void BKE_crazyspace_api_eval(Depsgraph *depsgraph, + Scene *scene, + Object *object, + struct ReportList *reports) +{ + if (object->runtime.crazyspace_deform_imats != nullptr || + object->runtime.crazyspace_deform_cos != nullptr) { + return; + } + + if (object->type != OB_MESH) { + BKE_report(reports, + RPT_ERROR, + "Crazyspace transformation is only available for Mesh type of objects"); + return; + } + + const Mesh *mesh = (const Mesh *)object->data; + object->runtime.crazyspace_verts_num = mesh->totvert; + BKE_crazyspace_build_sculpt(depsgraph, + scene, + object, + &object->runtime.crazyspace_deform_imats, + &object->runtime.crazyspace_deform_cos); +} + +void BKE_crazyspace_api_displacement_to_deformed(struct Object *object, + struct ReportList *reports, + int vertex_index, + float displacement[3], + float r_displacement_deformed[3]) +{ + if (vertex_index < 0 || vertex_index >= object->runtime.crazyspace_verts_num) { + BKE_reportf(reports, + RPT_ERROR, + "Invalid vertex index %d (expected to be within 0 to %d range)", + vertex_index, + object->runtime.crazyspace_verts_num); + return; + } + + mul_v3_m3v3(r_displacement_deformed, + object->runtime.crazyspace_deform_imats[vertex_index], + displacement); +} + +void BKE_crazyspace_api_displacement_to_original(struct Object *object, + struct ReportList *reports, + int vertex_index, + float displacement_deformed[3], + float r_displacement[3]) +{ + if (vertex_index < 0 || vertex_index >= object->runtime.crazyspace_verts_num) { + BKE_reportf(reports, + RPT_ERROR, + "Invalid vertex index %d (expected to be within 0 to %d range))", + vertex_index, + object->runtime.crazyspace_verts_num); + return; + } + + float mat[3][3]; + if (!invert_m3_m3(mat, object->runtime.crazyspace_deform_imats[vertex_index])) { + copy_v3_v3(r_displacement, displacement_deformed); + return; + } + + mul_v3_m3v3(r_displacement, mat, displacement_deformed); +} + +void BKE_crazyspace_api_eval_clear(Object *object) +{ + MEM_SAFE_FREE(object->runtime.crazyspace_deform_imats); + MEM_SAFE_FREE(object->runtime.crazyspace_deform_cos); +} + +/** \} */ + +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(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(&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(); + 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(); + 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 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( - view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_ALL); - if (cryptoflags == 0) { - cryptoflags = static_cast(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( + view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_ALL); + if (cryptoflags == 0) { + cryptoflags = static_cast(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::read_from_manifest( blender::StringRefNull manifest) { @@ -625,4 +625,9 @@ const blender::Vector &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 -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 @@ -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(a, b, c, d, i * step); + dst[i] = interpolate(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) { - interpolate_to_evaluated(src.typed(), cyclic, resolution, dst.typed()); - } + interpolate_to_evaluated(src.typed(), cyclic, resolution, dst.typed()); }); } @@ -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) { - interpolate_to_evaluated(src.typed(), cyclic, evaluated_offsets, dst.typed()); - } + interpolate_to_evaluated(src.typed(), cyclic, evaluated_offsets, dst.typed()); }); } 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 CurveEval::splines() const -{ - return splines_; -} - -blender::MutableSpan 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 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 CurveEval::control_point_offsets() const -{ - Array 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 CurveEval::evaluated_point_offsets() const -{ - Array 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 CurveEval::accumulated_spline_lengths() const -{ - Array 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 spline = std::make_unique(); - spline->set_resolution(nurb.resolu); - spline->set_cyclic(nurb.flagu & CU_NURB_CYCLIC); - - Span src_points{nurb.bezt, nurb.pntsu}; - spline->resize(src_points.size()); - MutableSpan positions = spline->positions(); - MutableSpan handle_positions_left = spline->handle_positions_left(true); - MutableSpan handle_positions_right = spline->handle_positions_right(true); - MutableSpan handle_types_left = spline->handle_types_left(); - MutableSpan handle_types_right = spline->handle_types_right(); - MutableSpan radii = spline->radii(); - MutableSpan 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 spline = std::make_unique(); - 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 src_points{nurb.bp, nurb.pntsu}; - spline->resize(src_points.size()); - MutableSpan positions = spline->positions(); - MutableSpan weights = spline->weights(); - MutableSpan radii = spline->radii(); - MutableSpan 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 spline = std::make_unique(); - spline->set_cyclic(nurb.flagu & CU_NURB_CYCLIC); - - Span src_points{nurb.bp, nurb.pntsu}; - spline->resize(src_points.size()); - MutableSpan positions = spline->positions(); - MutableSpan radii = spline->radii(); - MutableSpan 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 curve_eval_from_dna_curve(const Curve &dna_curve, - const ListBase &nurbs_list) -{ - Vector nurbs(nurbs_list); - - std::unique_ptr curve = std::make_unique(); - curve->resize(nurbs.size()); - MutableSpan 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 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 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 curves_to_curve_eval(const Curves &curves_id) -{ - CurveComponent src_component; - src_component.replace(&const_cast(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 resolution = curves.resolution(); - VArray normal_mode = curves.normal_mode(); - - VArraySpan nurbs_weights{ - src_attributes.lookup_or_default("nurbs_weight", ATTR_DOMAIN_POINT, 0.0f)}; - VArraySpan nurbs_orders{ - src_attributes.lookup_or_default("nurbs_order", ATTR_DOMAIN_CURVE, 4)}; - VArraySpan nurbs_knots_modes{ - src_attributes.lookup_or_default("knots_mode", ATTR_DOMAIN_CURVE, 0)}; - - VArraySpan handle_types_right{ - src_attributes.lookup_or_default("handle_type_right", ATTR_DOMAIN_POINT, 0)}; - VArraySpan handle_types_left{ - src_attributes.lookup_or_default("handle_type_left", ATTR_DOMAIN_POINT, 0)}; - - /* Create splines with the correct size and type. */ - VArray curve_types = curves.curve_types(); - std::unique_ptr curve_eval = std::make_unique(); - for (const int curve_index : curve_types.index_range()) { - const IndexRange points = curves.points_for_curve(curve_index); - - std::unique_ptr spline; - /* #CurveEval does not support catmull rom curves, so convert those to poly splines. */ - switch (std::max(1, curve_types[curve_index])) { - case CURVE_TYPE_POLY: { - spline = std::make_unique(); - spline->resize(points.size()); - break; - } - case CURVE_TYPE_BEZIER: { - std::unique_ptr bezier_spline = std::make_unique(); - 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 nurb_spline = std::make_unique(); - 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(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(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 curve_types = curves.curve_types_for_write(); - - blender::bke::SpanAttributeWriter normal_mode = - dst_attributes.lookup_or_add_for_write_only_span("normal_mode", ATTR_DOMAIN_CURVE); - blender::bke::SpanAttributeWriter nurbs_weight; - blender::bke::SpanAttributeWriter nurbs_order; - blender::bke::SpanAttributeWriter nurbs_knots_mode; - if (curve_eval.has_spline_with_type(CURVE_TYPE_NURBS)) { - nurbs_weight = dst_attributes.lookup_or_add_for_write_only_span("nurbs_weight", - ATTR_DOMAIN_POINT); - nurbs_order = dst_attributes.lookup_or_add_for_write_only_span("nurbs_order", - ATTR_DOMAIN_CURVE); - nurbs_knots_mode = dst_attributes.lookup_or_add_for_write_only_span("knots_mode", - ATTR_DOMAIN_CURVE); - } - blender::bke::SpanAttributeWriter handle_type_right; - blender::bke::SpanAttributeWriter 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( - "handle_type_right", ATTR_DOMAIN_POINT); - handle_type_left = dst_attributes.lookup_or_add_for_write_only_span("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(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(spline); - nurbs_knots_mode.span[curve_index] = static_cast(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(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 ids_in_order(layer_len); - Array 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 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 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 handle_types_l = curves.handle_types_left_for_write(); MutableSpan 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 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 resolutions = curves.resolution_for_write(); - MutableSpan nurbs_weights = curves.nurbs_weights_for_write(); - MutableSpan nurbs_orders = curves.nurbs_orders_for_write(); - MutableSpan nurbs_knots_modes = curves.nurbs_knots_modes_for_write(); + MutableSpan resolutions = curves.resolution_for_write(); + MutableSpan nurbs_weights = curves.nurbs_weights_for_write(); + MutableSpan nurbs_orders = curves.nurbs_orders_for_write(); + MutableSpan 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 mixer{dst}; - for (const int i : dst.index_range()) { - Span 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 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 @@ -213,17 +214,18 @@ static void interpolate_to_evaluated_rational(const BasisCache &basis_cache, { attribute_math::DefaultMixer mixer{dst}; - for (const int i : dst.index_range()) { - Span 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 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 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 verts(mesh->mvert, mesh->totvert); - MutableSpan edges(mesh->medge, mesh->totedge); - MutableSpan loops(mesh->mloop, mesh->totloop); - MutableSpan polys(mesh->mpoly, mesh->totpoly); + MutableSpan verts = mesh->verts_for_write(); + MutableSpan edges = mesh->edges_for_write(); + MutableSpan polys = mesh->polys_for_write(); + MutableSpan 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 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(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(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(); + edit_component.curves_edit_hints_ = std::make_unique( + *static_cast(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(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(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(__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 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 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 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 CurvesGeometry::positions() const { - return {(const float3 *)this->position, this->point_num}; + return get_span_attribute(*this, ATTR_DOMAIN_POINT, ATTR_POSITION); } MutableSpan 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(*this, ATTR_DOMAIN_POINT, ATTR_POSITION); } Span CurvesGeometry::offsets() const @@ -724,8 +722,9 @@ Span 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 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 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> curves_bounds(const CurvesGeometry &curves) { - Span positions = curves.positions(); - if (curves.radius) { - Span radii{curves.radius, curves.points_num()}; - return bounds::min_max_with_radii(positions, radii); + const Span positions = curves.positions(); + const VArray radii = curves.attributes().lookup_or_default( + 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 point_attributes = bke::retrieve_attributes_for_transfer( + curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_POINT); + Vector 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(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(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 point_attributes = bke::retrieve_attributes_for_transfer( + curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_POINT); + Vector 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 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(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(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 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 positions_left; - MutableSpan positions_right; - MutableSpan types_left; - MutableSpan 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(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(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(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(layer.data), this->points_num()}; - continue; + if (id.is_named() && bezier_handle_names.contains(id.name())) { + return true; } - const eCustomDataType data_type = static_cast(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( - *this, curves_to_reverse, {static_cast(layer.data), this->points_num()}); + reverse_curve_point_data(*this, curves_to_reverse, attribute.span.typed()); }); - } + 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 r_values) { attribute_math::DefaultMixer 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 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 &types, const std::array &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(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(*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(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(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(source); MDisps *d = static_cast(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(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(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(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(data); size_t size = 0; @@ -737,6 +728,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(*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(source); GridPaintMask *d = static_cast(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(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(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(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(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(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(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(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(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(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 dst_layers; + for (const CustomDataLayer &layer : Span{src.layers, src.totlayer}) { + if (!attribute_stored_in_bmesh_flag(layer.name)) { + dst_layers.append(layer); + } + } + + CustomData dst = src; + dst.layers = static_cast( + 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( 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(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 &layers_to_write) +void CustomData_blend_write_prepare(CustomData &data, + Vector &layers_to_write, + const Set &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, Vectorlayers_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(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(layer->data)); } + else if (layer->type == CD_MDEFORMVERT) { + BKE_defvert_blend_read(reader, count, static_cast(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(__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( @@ -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.c deleted file mode 100644 index 0a3107eee24..00000000000 --- a/source/blender/blenkernel/intern/editmesh_tangent.c +++ /dev/null @@ -1,434 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/** \file - * \ingroup bke - */ - -#include "BLI_math.h" -#include "BLI_task.h" - -#include "DNA_customdata_types.h" -#include "DNA_defs.h" -#include "DNA_meshdata_types.h" - -#include "BKE_customdata.h" -#include "BKE_editmesh.h" -#include "BKE_editmesh_tangent.h" -#include "BKE_mesh.h" -#include "BKE_mesh_tangent.h" /* for utility functions */ - -#include "MEM_guardedalloc.h" - -/* interface */ -#include "mikktspace.h" - -/* -------------------------------------------------------------------- */ -/** \name Tangent Space Calculation - * \{ */ - -/* 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; - -#ifdef USE_LOOPTRI_DETECT_QUADS - return pMesh->num_face_as_quad_map; -#else - return pMesh->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; - -#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]; -#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; - -#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]; -#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); - } - 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; - -#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]; -#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)]); - } - else { - copy_v3_v3(r_no, l->f->no); - } - } - else { - copy_v3_v3(r_no, l->v->no); - } -} - -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; - -#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]; -#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); - } -} - -void BKE_editmesh_loop_tangent_calc(BMEditMesh *em, - bool calc_active_tangent, - const char (*tangent_names)[MAX_NAME], - int tangent_names_len, - const float (*poly_normals)[3], - const float (*loop_normals)[3], - const float (*vert_orco)[3], - /* result */ - CustomData *loopdata_out, - const uint loopdata_out_len, - short *tangent_mask_curr_p) -{ - BMesh *bm = em->bm; - - int act_uv_n = -1; - int ren_uv_n = -1; - bool calc_act = false; - bool calc_ren = false; - char act_uv_name[MAX_NAME]; - char ren_uv_name[MAX_NAME]; - short tangent_mask = 0; - short tangent_mask_curr = *tangent_mask_curr_p; - - BKE_mesh_calc_loop_tangent_step_0(&bm->ldata, - calc_active_tangent, - tangent_names, - tangent_names_len, - &calc_act, - &calc_ren, - &act_uv_n, - &ren_uv_n, - act_uv_name, - ren_uv_name, - &tangent_mask); - - if ((tangent_mask_curr | tangent_mask) != tangent_mask_curr) { - for (int i = 0; i < tangent_names_len; i++) { - if (tangent_names[i][0]) { - BKE_mesh_add_loop_tangent_named_layer_for_uv( - &bm->ldata, loopdata_out, (int)loopdata_out_len, tangent_names[i]); - } - } - 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, ""); - } - if (calc_act && act_uv_name[0]) { - BKE_mesh_add_loop_tangent_named_layer_for_uv( - &bm->ldata, loopdata_out, (int)loopdata_out_len, act_uv_name); - } - if (calc_ren && ren_uv_name[0]) { - BKE_mesh_add_loop_tangent_named_layer_for_uv( - &bm->ldata, loopdata_out, (int)loopdata_out_len, ren_uv_name); - } - int totface = em->tottri; -#ifdef USE_LOOPTRI_DETECT_QUADS - int num_face_as_quad_map; - int *face_as_quad_map = NULL; - - /* 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__); - int i, j; - for (i = 0, j = 0; j < totface; i++, j++) { - face_as_quad_map[i] = j; - /* step over all quads */ - if (em->looptris[j][0]->f->len == 4) { - j++; /* skips the nest looptri */ - } - } - num_face_as_quad_map = i; - } - else { - num_face_as_quad_map = totface; - } -#endif - /* Calculation */ - if (em->tottri != 0) { - TaskPool *task_pool; - task_pool = BLI_task_pool_create(NULL, TASK_PRIORITY_HIGH); - - tangent_mask_curr = 0; - /* Calculate tangent layers */ - SGLSLEditMeshToTangent data_array[MAX_MTFACE]; - int index = 0; - int n = 0; - CustomData_update_typemap(loopdata_out); - const int tangent_layer_num = CustomData_number_of_layers(loopdata_out, CD_TANGENT); - for (n = 0; n < tangent_layer_num; n++) { - index = CustomData_get_layer_index_n(loopdata_out, CD_TANGENT, n); - BLI_assert(n < MAX_MTFACE); - SGLSLEditMeshToTangent *mesh2tangent = &data_array[n]; - mesh2tangent->numTessFaces = em->tottri; -#ifdef USE_LOOPTRI_DETECT_QUADS - mesh2tangent->face_as_quad_map = face_as_quad_map; - mesh2tangent->num_face_as_quad_map = num_face_as_quad_map; -#endif - mesh2tangent->precomputedFaceNormals = poly_normals; - /* NOTE: we assume we do have tessellated loop normals at this point - * (in case it is object-enabled), have to check this is valid. */ - mesh2tangent->precomputedLoopNormals = loop_normals; - mesh2tangent->cd_loop_uv_offset = CustomData_get_n_offset(&bm->ldata, CD_MLOOPUV, n); - - /* needed for indexing loop-tangents */ - int htype_index = BM_LOOP; - if (mesh2tangent->cd_loop_uv_offset == -1) { - mesh2tangent->orco = vert_orco; - if (!mesh2tangent->orco) { - continue; - } - /* needed for orco lookups */ - htype_index |= BM_VERT; - tangent_mask_curr |= DM_TANGENT_MASK_ORCO; - } - else { - /* Fill the resulting tangent_mask */ - int uv_ind = CustomData_get_named_layer_index( - &bm->ldata, CD_MLOOPUV, loopdata_out->layers[index].name); - int uv_start = CustomData_get_layer_index(&bm->ldata, CD_MLOOPUV); - BLI_assert(uv_ind != -1 && uv_start != -1); - BLI_assert(uv_ind - uv_start < MAX_MTFACE); - tangent_mask_curr |= 1 << (uv_ind - uv_start); - } - if (mesh2tangent->precomputedFaceNormals) { - /* needed for face normal lookups */ - htype_index |= BM_FACE; - } - BM_mesh_elem_index_ensure(bm, htype_index); - - mesh2tangent->looptris = (const BMLoop *(*)[3])em->looptris; - mesh2tangent->tangent = loopdata_out->layers[index].data; - - BLI_task_pool_push(task_pool, emDM_calc_loop_tangents_thread, mesh2tangent, false, NULL); - } - - BLI_assert(tangent_mask_curr == tangent_mask); - BLI_task_pool_work_and_wait(task_pool); - BLI_task_pool_free(task_pool); - } - else { - tangent_mask_curr = tangent_mask; - } -#ifdef USE_LOOPTRI_DETECT_QUADS - if (face_as_quad_map) { - MEM_freeN(face_as_quad_map); - } -# undef USE_LOOPTRI_DETECT_QUADS -#endif - } - - *tangent_mask_curr_p = tangent_mask_curr; - - int act_uv_index = CustomData_get_layer_index_n(&bm->ldata, CD_MLOOPUV, act_uv_n); - if (act_uv_index >= 0) { - int tan_index = CustomData_get_named_layer_index( - loopdata_out, CD_TANGENT, bm->ldata.layers[act_uv_index].name); - CustomData_set_layer_active_index(loopdata_out, CD_TANGENT, tan_index); - } /* else tangent has been built from orco */ - - /* Update render layer index */ - int ren_uv_index = CustomData_get_layer_index_n(&bm->ldata, CD_MLOOPUV, ren_uv_n); - if (ren_uv_index >= 0) { - int tan_index = CustomData_get_named_layer_index( - loopdata_out, CD_TANGENT, bm->ldata.layers[ren_uv_index].name); - CustomData_set_layer_render_index(loopdata_out, CD_TANGENT, tan_index); - } /* else tangent has been built from orco */ -} - -/** \} */ diff --git a/source/blender/blenkernel/intern/editmesh_tangent.cc b/source/blender/blenkernel/intern/editmesh_tangent.cc new file mode 100644 index 00000000000..a65532d083d --- /dev/null +++ b/source/blender/blenkernel/intern/editmesh_tangent.cc @@ -0,0 +1,331 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup bke + */ + +#include "BLI_math.h" +#include "BLI_task.h" + +#include "DNA_customdata_types.h" +#include "DNA_defs.h" +#include "DNA_meshdata_types.h" + +#include "BKE_customdata.h" +#include "BKE_editmesh.h" +#include "BKE_editmesh_tangent.h" +#include "BKE_mesh.h" +#include "BKE_mesh_tangent.h" /* for utility functions */ + +#include "MEM_guardedalloc.h" + +/* interface */ +#include "mikktspace.hh" + +/* -------------------------------------------------------------------- */ +/** \name Tangent Space Calculation + * \{ */ + +/* Necessary complexity to handle looptri's as quads for correct tangents */ +#define USE_LOOPTRI_DETECT_QUADS + +struct SGLSLEditMeshToTangent { + uint GetNumFaces() + { +#ifdef USE_LOOPTRI_DETECT_QUADS + return (uint)num_face_as_quad_map; +#else + return (uint)numTessFaces; +#endif + } + + uint GetNumVerticesOfFace(const uint face_num) + { +#ifdef USE_LOOPTRI_DETECT_QUADS + if (face_as_quad_map) { + if (looptris[face_as_quad_map[face_num]][0]->f->len == 4) { + return 4; + } + } + return 3; +#else + UNUSED_VARS(pContext, face_num); + return 3; +#endif + } + + 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 (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]; + } +#else + lt = looptris[face_num]; +#endif + return lt[vert_index]; + } + + 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); + } + + 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); + } + } + + 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 { + return mikk::float3(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); + } + + 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 +}; + +static void emDM_calc_loop_tangents_thread(TaskPool *__restrict UNUSED(pool), void *taskdata) +{ + SGLSLEditMeshToTangent *mesh_data = static_cast(taskdata); + + mikk::Mikktspace mikk(*mesh_data); + mikk.genTangSpace(); +} + +void BKE_editmesh_loop_tangent_calc(BMEditMesh *em, + bool calc_active_tangent, + const char (*tangent_names)[MAX_NAME], + int tangent_names_len, + const float (*poly_normals)[3], + const float (*loop_normals)[3], + const float (*vert_orco)[3], + /* result */ + CustomData *loopdata_out, + const uint loopdata_out_len, + short *tangent_mask_curr_p) +{ + BMesh *bm = em->bm; + + int act_uv_n = -1; + int ren_uv_n = -1; + bool calc_act = false; + bool calc_ren = false; + char act_uv_name[MAX_NAME]; + char ren_uv_name[MAX_NAME]; + short tangent_mask = 0; + short tangent_mask_curr = *tangent_mask_curr_p; + + BKE_mesh_calc_loop_tangent_step_0(&bm->ldata, + calc_active_tangent, + tangent_names, + tangent_names_len, + &calc_act, + &calc_ren, + &act_uv_n, + &ren_uv_n, + act_uv_name, + ren_uv_name, + &tangent_mask); + + if ((tangent_mask_curr | tangent_mask) != tangent_mask_curr) { + for (int i = 0; i < tangent_names_len; i++) { + if (tangent_names[i][0]) { + BKE_mesh_add_loop_tangent_named_layer_for_uv( + &bm->ldata, loopdata_out, (int)loopdata_out_len, tangent_names[i]); + } + } + 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_SET_DEFAULT, nullptr, (int)loopdata_out_len, ""); + } + if (calc_act && act_uv_name[0]) { + BKE_mesh_add_loop_tangent_named_layer_for_uv( + &bm->ldata, loopdata_out, (int)loopdata_out_len, act_uv_name); + } + if (calc_ren && ren_uv_name[0]) { + BKE_mesh_add_loop_tangent_named_layer_for_uv( + &bm->ldata, loopdata_out, (int)loopdata_out_len, ren_uv_name); + } + int totface = em->tottri; +#ifdef USE_LOOPTRI_DETECT_QUADS + int num_face_as_quad_map; + 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 = static_cast(MEM_mallocN(sizeof(int) * totface, __func__)); + int i, j; + for (i = 0, j = 0; j < totface; i++, j++) { + face_as_quad_map[i] = j; + /* step over all quads */ + if (em->looptris[j][0]->f->len == 4) { + j++; /* skips the nest looptri */ + } + } + num_face_as_quad_map = i; + } + else { + num_face_as_quad_map = totface; + } +#endif + /* Calculation */ + if (em->tottri != 0) { + TaskPool *task_pool; + task_pool = BLI_task_pool_create(nullptr, TASK_PRIORITY_HIGH); + + tangent_mask_curr = 0; + /* Calculate tangent layers */ + SGLSLEditMeshToTangent data_array[MAX_MTFACE]; + int index = 0; + int n = 0; + CustomData_update_typemap(loopdata_out); + const int tangent_layer_num = CustomData_number_of_layers(loopdata_out, CD_TANGENT); + for (n = 0; n < tangent_layer_num; n++) { + index = CustomData_get_layer_index_n(loopdata_out, CD_TANGENT, n); + BLI_assert(n < MAX_MTFACE); + SGLSLEditMeshToTangent *mesh2tangent = &data_array[n]; + mesh2tangent->numTessFaces = em->tottri; +#ifdef USE_LOOPTRI_DETECT_QUADS + mesh2tangent->face_as_quad_map = face_as_quad_map; + mesh2tangent->num_face_as_quad_map = num_face_as_quad_map; +#endif + mesh2tangent->precomputedFaceNormals = poly_normals; + /* NOTE: we assume we do have tessellated loop normals at this point + * (in case it is object-enabled), have to check this is valid. */ + mesh2tangent->precomputedLoopNormals = loop_normals; + mesh2tangent->cd_loop_uv_offset = CustomData_get_n_offset(&bm->ldata, CD_MLOOPUV, n); + + /* needed for indexing loop-tangents */ + int htype_index = BM_LOOP; + if (mesh2tangent->cd_loop_uv_offset == -1) { + mesh2tangent->orco = vert_orco; + if (!mesh2tangent->orco) { + continue; + } + /* needed for orco lookups */ + htype_index |= BM_VERT; + tangent_mask_curr |= DM_TANGENT_MASK_ORCO; + } + else { + /* Fill the resulting tangent_mask */ + int uv_ind = CustomData_get_named_layer_index( + &bm->ldata, CD_MLOOPUV, loopdata_out->layers[index].name); + int uv_start = CustomData_get_layer_index(&bm->ldata, CD_MLOOPUV); + BLI_assert(uv_ind != -1 && uv_start != -1); + BLI_assert(uv_ind - uv_start < MAX_MTFACE); + tangent_mask_curr |= 1 << (uv_ind - uv_start); + } + if (mesh2tangent->precomputedFaceNormals) { + /* needed for face normal lookups */ + htype_index |= BM_FACE; + } + BM_mesh_elem_index_ensure(bm, htype_index); + + mesh2tangent->looptris = (const BMLoop *(*)[3])em->looptris; + mesh2tangent->tangent = static_cast(loopdata_out->layers[index].data); + + BLI_task_pool_push( + task_pool, emDM_calc_loop_tangents_thread, mesh2tangent, false, nullptr); + } + + BLI_assert(tangent_mask_curr == tangent_mask); + BLI_task_pool_work_and_wait(task_pool); + BLI_task_pool_free(task_pool); + } + else { + tangent_mask_curr = tangent_mask; + } +#ifdef USE_LOOPTRI_DETECT_QUADS + if (face_as_quad_map) { + MEM_freeN(face_as_quad_map); + } +# undef USE_LOOPTRI_DETECT_QUADS +#endif + } + + *tangent_mask_curr_p = tangent_mask_curr; + + int act_uv_index = CustomData_get_layer_index_n(&bm->ldata, CD_MLOOPUV, act_uv_n); + if (act_uv_index >= 0) { + int tan_index = CustomData_get_named_layer_index( + loopdata_out, CD_TANGENT, bm->ldata.layers[act_uv_index].name); + CustomData_set_layer_active_index(loopdata_out, CD_TANGENT, tan_index); + } /* else tangent has been built from orco */ + + /* Update render layer index */ + int ren_uv_index = CustomData_get_layer_index_n(&bm->ldata, CD_MLOOPUV, ren_uv_n); + if (ren_uv_index >= 0) { + int tan_index = CustomData_get_named_layer_index( + loopdata_out, CD_TANGENT, bm->ldata.layers[ren_uv_index].name); + CustomData_set_layer_render_index(loopdata_out, CD_TANGENT, tan_index); + } /* else tangent has been built from orco */ +} + +/** \} */ 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 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 -static void adapt_curve_domain_point_to_spline_impl(const CurveEval &curve, - const VArray &old_values, - MutableSpan r_values) -{ - const int splines_len = curve.splines().size(); - Array offsets = curve.control_point_offsets(); - BLI_assert(r_values.size() == splines_len); - attribute_math::DefaultMixer 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 &old_values, - MutableSpan r_values) -{ - const int splines_len = curve.splines().size(); - Array 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>) { - Array values(curve.splines().size()); - adapt_curve_domain_point_to_spline_impl(curve, varray.typed(), values); - new_varray = VArray::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 class VArray_For_SplineToPoint final : public VArrayImpl { - 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 original_data_; - Array offsets_; - - public: - VArray_For_SplineToPoint(GVArray original_varray, Array offsets) - : VArrayImpl(offsets.last()), - original_varray_(std::move(original_varray)), - original_data_(original_varray_.typed()), - 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 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 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 offsets = curve.control_point_offsets(); - new_varray = VArray::template For>(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(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(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(owner); - return !curve->splines().is_empty(); - } -}; - -static int get_spline_resolution(const SplinePtr &spline) -{ - if (const BezierSpline *bezier_spline = dynamic_cast(spline.get())) { - return bezier_spline->resolution(); - } - if (const NURBSpline *nurb_spline = dynamic_cast(spline.get())) { - return nurb_spline->resolution(); - } - return 1; -} - -static void set_spline_resolution(SplinePtr &spline, const int resolution) -{ - if (BezierSpline *bezier_spline = dynamic_cast(spline.get())) { - bezier_spline->set_resolution(std::max(resolution, 1)); - } - if (NURBSpline *nurb_spline = dynamic_cast(spline.get())) { - nurb_spline->set_resolution(std::max(resolution, 1)); - } -} - -static GVArray make_resolution_read_attribute(const CurveEval &curve) -{ - return VArray::ForDerivedSpan(curve.splines()); -} - -static GVMutableArray make_resolution_write_attribute(CurveEval &curve) -{ - return VMutableArray:: - ForDerivedSpan(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::ForDerivedSpan(curve.splines()); -} - -static GVMutableArray make_cyclic_write_attribute(CurveEval &curve) -{ - return VMutableArray::ForDerivedSpan( - 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 -static void point_attribute_materialize(Span> data, - Span offsets, - const IndexMask mask, - MutableSpan 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 src = data[spline_index]; - MutableSpan 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 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 -static void point_attribute_materialize_to_uninitialized(Span> data, - Span offsets, - const IndexMask mask, - MutableSpan 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 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 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 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(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(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 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(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(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 class VArrayImpl_For_SplinePoints final : public VMutableArrayImpl { - private: - Array> data_; - Array offsets_; - - public: - VArrayImpl_For_SplinePoints(Array> data, Array offsets) - : VMutableArrayImpl(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 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 r_span) const final - { - point_attribute_materialize({(Span *)data_.data(), data_.size()}, offsets_, mask, r_span); - } - - void materialize_to_uninitialized(const IndexMask mask, MutableSpan r_span) const final - { - point_attribute_materialize_to_uninitialized( - {(Span *)data_.data(), data_.size()}, offsets_, mask, r_span); - } -}; - -template VArray point_data_varray(Array> spans, Array offsets) -{ - return VArray::template For>(std::move(spans), - std::move(offsets)); -} - -template -VMutableArray point_data_varray_mutable(Array> spans, Array offsets) -{ - return VMutableArray::template For>(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 { - private: - MutableSpan splines_; - Array offsets_; - - public: - VArrayImpl_For_SplinePosition(MutableSpan splines, Array offsets) - : VMutableArrayImpl(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 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> get_position_spans() const - { - Array> spans(splines_.size()); - for (const int i : spans.index_range()) { - spans[i] = splines_[i]->positions(); - } - return spans; - } - - void materialize(const IndexMask mask, MutableSpan r_span) const final - { - Array> spans = this->get_position_spans(); - point_attribute_materialize(spans.as_span(), offsets_, mask, r_span); - } - - void materialize_to_uninitialized(const IndexMask mask, MutableSpan r_span) const final - { - Array> spans = this->get_position_spans(); - point_attribute_materialize_to_uninitialized(spans.as_span(), offsets_, mask, r_span); - } -}; - -class VArrayImpl_For_BezierHandles final : public VMutableArrayImpl { - private: - MutableSpan splines_; - Array offsets_; - bool is_right_; - - public: - VArrayImpl_For_BezierHandles(MutableSpan splines, - Array offsets, - const bool is_right) - : VMutableArrayImpl(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(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(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 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(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 r_span) const final - { - Array> 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 r_span) const final - { - Array> 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> get_handle_spans(Span splines, const bool is_right) - { - Array> spans(splines.size()); - for (const int i : spans.index_range()) { - if (splines[i]->type() == CURVE_TYPE_BEZIER) { - BezierSpline &bezier_spline = static_cast(*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 class BuiltinPointAttributeProvider : public BuiltinAttributeProvider { - protected: - using GetSpan = Span (*)(const Spline &spline); - using GetMutableSpan = MutableSpan (*)(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()), - 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(owner); - if (curve == nullptr) { - return {}; - } - - if (!this->exists(owner)) { - return {}; - } - - Span splines = curve->splines(); - if (splines.size() == 1) { - return GVArray::ForSpan(get_span_(*splines.first())); - } - - Array offsets = curve->control_point_offsets(); - Array> spans(splines.size()); - for (const int i : splines.index_range()) { - Span 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(const_cast(span.data()), span.size()); - } - - return point_data_varray(spans, offsets); - } - - GAttributeWriter try_get_for_write(void *owner) const override - { - CurveEval *curve = static_cast(owner); - if (curve == nullptr) { - return {}; - } - - if (!this->exists(owner)) { - return {}; - } - - std::function tag_modified_fn; - if (update_on_write_ != nullptr) { - tag_modified_fn = [curve, update = update_on_write_]() { - for (SplinePtr &spline : curve->splines()) { - update(*spline); - } - }; - } - - MutableSpan splines = curve->splines(); - if (splines.size() == 1) { - return {GVMutableArray::ForSpan(get_mutable_span_(*splines.first())), - domain_, - std::move(tag_modified_fn)}; - } - - Array offsets = curve->control_point_offsets(); - Array> 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(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(owner); - return create_point_attribute(curve, name_, initializer, CD_PROP_INT32); - } - - bool exists(const void *owner) const final - { - const CurveEval *curve = static_cast(owner); - if (curve == nullptr) { - return false; - } - - Span 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 { - 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(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::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 offsets = curve->control_point_offsets(); - return {VMutableArray::For(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(owner); - if (curve == nullptr) { - return {}; - } - - if (!curve->has_spline_with_type(CURVE_TYPE_BEZIER)) { - return {}; - } - - Array offsets = curve->control_point_offsets(); - /* Use const-cast because the underlying virtual array implementation is shared between const - * and non const data. */ - return VArray::For( - const_cast(curve)->splines(), std::move(offsets), is_right_); - } - - GAttributeWriter try_get_for_write(void *owner) const override - { - CurveEval *curve = static_cast(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 offsets = curve->control_point_offsets(); - return {VMutableArray::For( - 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(owner); - if (curve == nullptr) { - return false; - } - - CurveComponentLegacy component; - component.replace(const_cast(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(owner); - if (curve == nullptr || curve->splines().size() == 0) { - return {}; - } - - Span splines = curve->splines(); - Vector spans; /* GSpan has no default constructor. */ - spans.reserve(splines.size()); - std::optional 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 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 offsets = curve->control_point_offsets(); - attribute_math::convert_to_static_type(spans[0].type(), [&](auto dummy) { - using T = decltype(dummy); - Array> data(splines.size()); - for (const int i : splines.index_range()) { - Span span = spans[i].typed(); - /* Use const-cast because the underlying virtual array implementation is shared between - * const and non const data. */ - data[i] = MutableSpan(const_cast(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(owner); - if (curve == nullptr || curve->splines().size() == 0) { - return {}; - } - - MutableSpan splines = curve->splines(); - Vector spans; /* GMutableSpan has no default constructor. */ - spans.reserve(splines.size()); - std::optional 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 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 offsets = curve->control_point_offsets(); - attribute_math::convert_to_static_type(spans[0].type(), [&](auto dummy) { - using T = decltype(dummy); - Array> data(splines.size()); - for (const int i : splines.index_range()) { - data[i] = spans[i].typed(); - 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(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(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(owner); - if (curve == nullptr || curve->splines().size() == 0) { - return false; - } - - Span 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 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(owner); - return curve ? &curve->attributes.data : nullptr; - }, - [](const void *owner) -> const CustomData * { - const CurveEval *curve = static_cast(owner); - return curve ? &curve->attributes.data : nullptr; - }, - [](const void *owner) -> int { - const CurveEval *curve = static_cast(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 id( - "id", - BuiltinAttributeProvider::Creatable, - BuiltinAttributeProvider::Deletable, - [](const Spline &spline) { - std::optional span = spline.attributes.get_for_read("id"); - return span ? span->typed() : Span(); - }, - [](Spline &spline) { - std::optional span = spline.attributes.get_for_write("id"); - return span ? span->typed() : MutableSpan(); - }, - {}, - true); - - static BuiltinPointAttributeProvider radius( - "radius", - BuiltinAttributeProvider::NonCreatable, - BuiltinAttributeProvider::NonDeletable, - [](const Spline &spline) { return spline.radii(); }, - [](Spline &spline) { return spline.radii(); }, - nullptr, - false); - - static BuiltinPointAttributeProvider 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(); - fn.domain_size = [](const void *owner, const eAttrDomain domain) -> int { - if (owner == nullptr) { - return 0; - } - const CurveEval &curve_eval = *static_cast(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(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 CurveComponentLegacy::attributes() const -{ - return blender::bke::AttributeAccessor(curve_, blender::bke::get_curve_accessor_functions_ref()); -} - -std::optional 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 curve_normal_point_domain(const bke::CurvesGeometry &curves return results; } -VArray curve_normals_varray(const CurveComponent &component, const eAttrDomain domain) +VArray 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 types = curves.curve_types(); if (curves.is_single_type(CURVE_TYPE_POLY)) { - return component.attributes()->adapt_domain( + return curves.adapt_domain( VArray::ForSpan(curves.evaluated_normals()), ATTR_DOMAIN_POINT, domain); } @@ -228,7 +223,7 @@ VArray curve_normals_varray(const CurveComponent &component, const eAttr } if (domain == ATTR_DOMAIN_CURVE) { - return component.attributes()->adapt_domain( + return curves.adapt_domain( VArray::ForContainer(std::move(normals)), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE); } @@ -241,15 +236,9 @@ VArray curve_normals_varray(const CurveComponent &component, const eAttr /** \name Curve Length Field Input * \{ */ -static VArray construct_curve_length_gvarray(const CurveComponent &component, +static VArray 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 cyclic = curves.cyclic(); @@ -263,28 +252,23 @@ static VArray construct_curve_length_gvarray(const CurveComponent &compon } if (domain == ATTR_DOMAIN_POINT) { - return component.attributes()->adapt_domain( - std::move(lengths), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT); + return curves.adapt_domain(std::move(lengths), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT); } return {}; } CurveLengthFieldInput::CurveLengthFieldInput() - : GeometryFieldInput(CPPType::get(), "Spline Length node") + : CurvesFieldInput(CPPType::get(), "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(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(owner); return curves.curves_num(); - }, - [](void *owner) { - CurvesGeometry &curves = *static_cast(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(owner); return curves.points_num(); - }, - [](void *owner) { - CurvesGeometry &curves = *static_cast(owner); - curves.update_customdata_pointers(); }}; static BuiltinCustomDataLayerProvider position("position", @@ -452,6 +428,12 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() make_array_write_attribute, tag_component_positions_changed); + static const fn::CustomMF_SI_SO handle_type_clamp{ + "Handle Type Validate", + [](int8_t value) { + return std::clamp(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, make_array_write_attribute, - 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, make_array_write_attribute, - 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, tag_component_positions_changed); + static const fn::CustomMF_SI_SO nurbs_order_clamp{ + "NURBS Order Validate", + [](int8_t value) { return std::max(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, make_array_write_attribute, - tag_component_topology_changed); + tag_component_topology_changed, + AttributeValidator{&nurbs_order_clamp}); + static const fn::CustomMF_SI_SO normal_mode_clamp{ + "Normal Mode Validate", + [](int8_t value) { + return std::clamp(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, make_array_write_attribute, - tag_component_normals_changed); + tag_component_normals_changed, + AttributeValidator{&normal_mode_clamp}); + static const fn::CustomMF_SI_SO knots_mode_clamp{ + "Knots Mode Validate", + [](int8_t value) { + return std::clamp(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, make_array_write_attribute, - tag_component_topology_changed); + tag_component_topology_changed, + AttributeValidator{&knots_mode_clamp}); + static const fn::CustomMF_SI_SO curve_type_clamp{ + "Curve Type Validate", + [](int8_t value) { + return std::clamp(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, make_array_write_attribute, - tag_component_curve_types_changed); + tag_component_curve_types_changed, + AttributeValidator{&curve_type_clamp}); + static const fn::CustomMF_SI_SO resolution_clamp{ + "Resolution Validate", + [](int value) { return std::max(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, make_array_write_attribute, - tag_component_topology_changed); + tag_component_topology_changed, + AttributeValidator{&resolution_clamp}); static BuiltinCustomDataLayerProvider cyclic("cyclic", ATTR_DOMAIN_CURVE, @@ -644,6 +659,7 @@ std::optional CurveComponent::attributes() cons std::optional 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(*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()) { + return; + } + GeometryComponentEditData &edit_component = + geometry.get_component_for_write(); + 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(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 mesh_normals_varray(const MeshComponent &mesh_component, - const Mesh &mesh, +VArray mesh_normals_varray(const Mesh &mesh, const IndexMask mask, const eAttrDomain domain) { @@ -135,8 +136,8 @@ VArray 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 vert_normals{(float3 *)BKE_mesh_vertex_normals_ensure(&mesh), mesh.totvert}; + const Span edges = mesh.edges(); Array edge_normals(mask.min_array_size()); - Span 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 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::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 r_values) { BLI_assert(r_values.size() == mesh.totvert); + const Span loops = mesh.loops(); + attribute_math::DefaultMixer 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 r_values) { BLI_assert(r_values.size() == mesh.totvert); + const Span loops = mesh.loops(); + Array 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 loops = mesh.loops(); + GVArray new_varray; attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) { using T = decltype(dummy); new_varray = VArray::ForFunc(mesh.totloop, - [&mesh, varray = varray.typed()](const int64_t loop_index) { - const int vertex_index = mesh.mloop[loop_index].v; + [loops, varray = varray.typed()](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 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>) { if constexpr (std::is_same_v) { new_varray = VArray::ForFunc( - mesh.totpoly, [&mesh, varray = varray.typed()](const int face_index) { + polys.size(), [polys, varray = varray.typed()](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::ForFunc( - mesh.totpoly, [&mesh, varray = varray.typed()](const int face_index) { + polys.size(), [polys, varray = varray.typed()](const int face_index) { T return_value; attribute_math::DefaultMixer 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 r_values) { BLI_assert(r_values.size() == mesh.totedge); + const Span polys = mesh.polys(); + const Span loops = mesh.loops(); + attribute_math::DefaultMixer 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 r_values) { BLI_assert(r_values.size() == mesh.totedge); + const Span polys = mesh.polys(); + const Span loops = mesh.loops(); /* It may be possible to rely on the #ME_LOOSEEDGE flag, but that seems error-prone. */ Array 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 r_values) { BLI_assert(r_values.size() == mesh.totvert); + const Span polys = mesh.polys(); + const Span loops = mesh.loops(); + attribute_math::DefaultMixer 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 r_values) { BLI_assert(r_values.size() == mesh.totvert); + const Span polys = mesh.polys(); + const Span 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 r_values) { BLI_assert(r_values.size() == mesh.totloop); + const Span 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 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 r_values) { BLI_assert(r_values.size() == mesh.totedge); + const Span polys = mesh.polys(); + const Span loops = mesh.loops(); + attribute_math::DefaultMixer 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 r_values) { BLI_assert(r_values.size() == mesh.totedge); + const Span polys = mesh.polys(); + const Span 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 polys = mesh.polys(); + const Span 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>) { if constexpr (std::is_same_v) { new_varray = VArray::ForFunc( - mesh.totpoly, [&mesh, varray = varray.typed()](const int face_index) { + mesh.totpoly, [loops, polys, varray = varray.typed()](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::ForFunc( - mesh.totpoly, [&mesh, varray = varray.typed()](const int face_index) { + mesh.totpoly, [loops, polys, varray = varray.typed()](const int face_index) { T return_value; attribute_math::DefaultMixer 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 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) { /* An edge is selected if both of its vertices were selected. */ new_varray = VArray::ForFunc( - mesh.totedge, [&mesh, varray = varray.typed()](const int edge_index) { - const MEdge &edge = mesh.medge[edge_index]; + edges.size(), [edges, varray = varray.typed()](const int edge_index) { + const MEdge &edge = edges[edge_index]; return varray[edge.v1] && varray[edge.v2]; }); } else { new_varray = VArray::ForFunc( - mesh.totedge, [&mesh, varray = varray.typed()](const int edge_index) { + edges.size(), [edges, varray = varray.typed()](const int edge_index) { T return_value; attribute_math::DefaultMixer 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 r_values) { BLI_assert(r_values.size() == mesh.totloop); + const Span polys = mesh.polys(); + const Span loops = mesh.loops(); + attribute_math::DefaultMixer 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 r_values) { BLI_assert(r_values.size() == mesh.totloop); + const Span polys = mesh.polys(); + const Span 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 r_values) { BLI_assert(r_values.size() == mesh.totvert); + const Span edges = mesh.edges(); + attribute_math::DefaultMixer 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 r_values) { BLI_assert(r_values.size() == mesh.totvert); + const Span 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 polys = mesh.polys(); + const Span 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) { /* A face is selected if all of its edges are selected. */ new_varray = VArray::ForFunc( - mesh.totpoly, [&mesh, varray = varray.typed()](const int face_index) { - const MPoly &poly = mesh.mpoly[face_index]; + polys.size(), [loops, polys, varray = varray.typed()](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::ForFunc( - mesh.totpoly, [&mesh, varray = varray.typed()](const int face_index) { + polys.size(), [loops, polys, varray = varray.typed()](const int face_index) { T return_value; attribute_math::DefaultMixer 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(mpoly.mat_nr); -} - -static void set_material_index(MPoly &mpoly, int index) -{ - mpoly.mat_nr = static_cast(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 { const int dvert_index_; public: - VArrayImpl_For_VertexWeights(MDeformVert *dverts, const int totvert, const int dvert_index) - : VMutableArrayImpl(totvert), dverts_(dverts), dvert_index_(dvert_index) + VArrayImpl_For_VertexWeights(MutableSpan dverts, const int dvert_index) + : VMutableArrayImpl(dverts.size()), dverts_(dverts.data()), dvert_index_(dvert_index) + { + } + + VArrayImpl_For_VertexWeights(Span dverts, const int dvert_index) + : VMutableArrayImpl(dverts.size()), + dverts_(const_cast(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 dverts = mesh->deform_verts(); + if (dverts.is_empty()) { static const float default_value = 0.0f; return {VArray::ForSingle(default_value, mesh->totvert), ATTR_DOMAIN_POINT}; } - return {VArray::For( - mesh->dvert, mesh->totvert, vertex_group_index), + return {VArray::For(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::For( - mesh->dvert, mesh->totvert, vertex_group_index), + MutableSpan dverts = mesh->deform_verts_for_write(); + return {VMutableArray::For(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(owner); - BKE_mesh_update_customdata_pointers(mesh, false); - }; - #define MAKE_MUTABLE_CUSTOM_DATA_GETTER(NAME) \ [](void *owner) -> CustomData * { \ Mesh *mesh = static_cast(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, 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, - make_derived_write_attribute, - nullptr); + static const fn::CustomMF_SI_SO 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(value, 0, std::numeric_limits::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, + make_array_write_attribute, + 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 MeshComponent::attributes() const { return blender::bke::AttributeAccessor(mesh_, blender::bke::get_mesh_accessor_functions_ref()); @@ -1324,7 +1354,8 @@ std::optional MeshComponent::attributes() const std::optional 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(owner); - BKE_pointcloud_update_customdata_pointers(pointcloud); - }; static CustomDataAccessInfo point_access = { [](void *owner) -> CustomData * { PointCloud *pointcloud = static_cast(owner); @@ -127,8 +123,7 @@ static ComponentAttributeProviders create_attribute_providers_for_point_cloud() [](const void *owner) -> int { const PointCloud *pointcloud = static_cast(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 PointCloudComponent::attributes() const { return blender::bke::AttributeAccessor(pointcloud_, @@ -226,8 +223,9 @@ std::optional PointCloudComponent::attributes() std::optional 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(component); + geometry_ = mesh_component.get_for_read(); + break; + } + case GEO_COMPONENT_TYPE_CURVE: { + const CurveComponent &curve_component = static_cast(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( + component); + geometry_ = pointcloud_component.get_for_read(); + break; + } + case GEO_COMPONENT_TYPE_INSTANCES: { + const InstancesComponent &instances_component = static_cast( + 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 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(geometry_) : nullptr; +} +const CurvesGeometry *GeometryFieldContext::curves() const +{ + return this->type() == GEO_COMPONENT_TYPE_CURVE ? + static_cast(geometry_) : + nullptr; +} +const PointCloud *GeometryFieldContext::pointcloud() const +{ + return this->type() == GEO_COMPONENT_TYPE_POINT_CLOUD ? + static_cast(geometry_) : + nullptr; +} +const InstancesComponent *GeometryFieldContext::instances() const +{ + return this->type() == GEO_COMPONENT_TYPE_INSTANCES ? + static_cast(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( + &context)) { + return this->get_varray_for_context(*geometry_context, mask); + } + if (const MeshFieldContext *mesh_context = dynamic_cast(&context)) { + return this->get_varray_for_context({mesh_context->mesh(), mesh_context->domain()}, mask); + } + if (const CurvesFieldContext *curve_context = dynamic_cast( + &context)) { + return this->get_varray_for_context({curve_context->curves(), curve_context->domain()}, mask); + } + if (const PointCloudFieldContext *point_context = dynamic_cast( + &context)) { + return this->get_varray_for_context({point_context->pointcloud()}, mask); + } + if (const InstancesFieldContext *instances_context = dynamic_cast( + &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( + &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(&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( + &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( + &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( + &context)) { + if (const PointCloud *pointcloud = geometry_context->pointcloud()) { + return this->get_varray_for_context(*pointcloud, mask); + } + } + if (const PointCloudFieldContext *point_context = dynamic_cast( + &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( + &context)) { + if (const InstancesComponent *instances = geometry_context->instances()) { + return this->get_varray_for_context(*instances, mask); + } + } + if (const InstancesFieldContext *instances_context = dynamic_cast( + &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(&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(&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(&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(&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 component } } +void GeometrySet::keep_only_during_modify( + const blender::Span component_types) +{ + Vector 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 << ""; + Vector 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()->instances_num()) + + " instances"); + } + if (geometry_set.get_curve_edit_hints_for_read()) { + parts.append("curve edit hints"); + } + + 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(); + return (component == nullptr) ? nullptr : component->curves_edit_hints_.get(); +} + bool GeometrySet::has_pointcloud() const { const PointCloudComponent *component = this->get_component_for_read(); @@ -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()) { + return nullptr; + } + GeometryComponentEditData &component = + this->get_component_for_write(); + return component.curves_edit_hints_.get(); +} + void GeometrySet::attribute_foreach(const Span component_types, const bool include_instances, const AttributeForeachCallback callback) const @@ -600,48 +665,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(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(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(&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), - false); + Mesh *mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object( + &const_cast(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 verts = me->verts(); + const Span edges = me->edges(); + const Span 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 verts = me_eval->verts(); + const Span polys = me_eval->polys(); + const Span 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 mesh_material_indices = me_eval->attributes().lookup_or_default( + "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 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(__func__); ListBase *perimeter_left_side = MEM_cnew(__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 create(const StringRefNull prop_n return std::unique_ptr(property); } +std::unique_ptr 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(property); +} + static std::unique_ptr 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 create_array(StringRefNull prop_name, Span values) { - static_assert(std::is_same_v || std::is_same_v || + static_assert(std::is_same_v || std::is_same_v || std::is_same_v, "Allowed values for PrimitiveType are int32_t, float and double."); static_assert(!std::is_same_v || 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("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("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(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(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(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(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("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$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(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(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, ¤t_last_slot->render); + RE_SwapResult(re, &next_last_slot->render); } - RE_SwapResult(re, ¤t_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(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__ao.png"); verify("test.1002.ver0023.png", "test..ver0023.png"); verify("test.ver0023.1002.png", "test.ver0023..png"); - verify("1002test.png", "test.png"); - verify("test1002.png", "test.png"); + verify("test.1002.1.png", "test..1.png"); + verify("test.1.1002.png", "test.1..png"); + verify("test-2022-01-01.1002.png", "test-2022-01-01..png"); + verify("1111_11.1002.png", "1111_11..png"); + verify("2111_01.1002.png", "2111_01..png"); + verify("2022_1002_100200.1002.png", "2022_1002_100200..png"); /* UVTILE pattern detection. */ verify("uv-test.u2_v10.png", "uv-test..png"); @@ -44,8 +48,15 @@ TEST(udim, image_ensure_tile_token) verify("u2_v10uv-test.png", "uv-test.png"); verify("u2_v10u_v-test.png", "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 <->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, ¶ms); +} + +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, ¶ms); +} + +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, ¶ms); +} + +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, ¶ms); +} + +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, ¶ms); +} + +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, ¶ms); +} + +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 list) { @@ -47,8 +47,7 @@ static void test_lib_id_main_sort_check_order(std::initializer_list 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(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(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(BKE_id_new(ctx.bmain, ID_OB, "OB_A")); ID *id_b = static_cast(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(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(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(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(BKE_id_new(ctx.bmain, ID_OB, "OB_A")); ID *id_b = static_cast(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(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + ID *id_bar = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Bar")); + ID *id_baz = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Baz")); + ID *id_yes = static_cast(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(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(BKE_id_new(ctx.bmain, ID_LI, "LibOne")); + Library *lib_two = static_cast(BKE_id_new(ctx.bmain, ID_LI, "LibTwo")); + + ID *id_foo = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + ID *id_bar = static_cast(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(BKE_id_new(ctx.bmain, ID_OB, "Baz")); + ID *id_yes = static_cast(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(BKE_id_new(ctx.bmain, ID_OB, name_a)); + ID *id_b = static_cast(BKE_id_new(ctx.bmain, ID_OB, name_b)); + ID *id_c = static_cast(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(BKE_id_new(ctx.bmain, ID_OB, "Foo.123")); + ID *id_b = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo.000")); + ID *id_c = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo.003")); + ID *id_d = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo.3")); + ID *id_e = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo.0")); + ID *id_f = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo.")); + ID *id_g = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo.0123")); + ID *id_h = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + ID *id_i = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo..")); + ID *id_j = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo..001")); + ID *id_k = static_cast(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(BKE_id_new(ctx.bmain, ID_OB, "Foo.123")); + id_b = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo.000")); + id_c = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo.003")); + id_d = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo.3")); + id_e = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo.0")); + id_f = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo.")); + id_g = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo.0123")); + id_h = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + id_i = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo..")); + id_j = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo..001")); + id_k = static_cast(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(BKE_id_new(ctx.bmain, ID_OB, "Foo.002")); + ID *id_001 = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo.002")); + ID *id_003 = static_cast(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(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + ID *id_b = static_cast(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(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(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(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + EXPECT_STREQ(id_010->name + 2, "Foo.010"); + ID *id_020 = static_cast(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(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(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + EXPECT_STREQ(id_030->name + 2, "Foo.030"); + ID *id_600 = static_cast(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(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(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(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(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + EXPECT_STREQ(id_fo178->name + 2, "Fo.178"); + ID *id_fo179 = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo.2000")); + EXPECT_STREQ(id_fo179->name + 2, "Fo.179"); + ID *id_fo180 = static_cast(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(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + ID *id_b = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + ID *id_c = static_cast(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(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + ID *id_b = static_cast(BKE_id_new(ctx.bmain, ID_CA, "Foo")); + ID *id_c = static_cast(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(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(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(id_src); + Object *ob_dst = reinterpret_cast(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
(bmain), const_cast(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)); + 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(lib_override_get(const_cast(bmain), - const_cast(id), - const_cast(r_owner_id))); + return const_cast( + BKE_lib_override_library_get(const_cast(bmain), + const_cast(id), + const_cast(owner_id_hint), + const_cast(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(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(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(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(todo_ids.first); todo_id_iter != nullptr; todo_id_iter = todo_id_iter->next) { reference_id = static_cast(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(&collections_linkedlist_p))) { *collections_linkedlist_p = static_cast( 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(&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(&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(&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(&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(id_instance_hint) : nullptr; - Collection *collection_new = ((Collection *)id_root->newid); + Collection *collection_new = (reinterpret_cast(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(id_instance_hint)), collection_new); } else { BKE_collection_add_from_collection( - bmain, scene, ((Collection *)id_root), collection_new); + bmain, scene, (reinterpret_cast(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(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(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(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(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(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(BKE_key_from_id(reference_id)); + break; + case ID_GR: + BLI_assert(GS(reference_id->name) == ID_SCE); + reference_id = reinterpret_cast( + reinterpret_cast(reference_id)->master_collection); + break; + case ID_NT: + reference_id = reinterpret_cast(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(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(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( - id_override_new->override_library->properties.first); - IDOverrideLibraryProperty *op_old = static_cast( - 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( + id_override_new->override_library->properties.first); + IDOverrideLibraryProperty *op_old = static_cast( + 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(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(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(&id_resync_roots_p))) { *id_resync_roots_p = MEM_cnew(__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(library), + reinterpret_cast(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(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(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(local); if (ob_local->type == OB_ARMATURE) { - Object *ob_reference = (Object *)local->override_library->reference; + Object *ob_reference = reinterpret_cast(local->override_library->reference); BLI_assert(ob_local->data != nullptr); BLI_assert(ob_reference->data != nullptr); BKE_pose_ensure(bmain, ob_local, static_cast(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(local); if (ob_local->type == OB_ARMATURE) { - Object *ob_reference = (Object *)local->override_library->reference; + Object *ob_reference = reinterpret_cast(local->override_library->reference); BLI_assert(ob_local->data != nullptr); BLI_assert(ob_reference->data != nullptr); BKE_pose_ensure(bmain, ob_local, static_cast(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(local); if (ob_local->type == OB_ARMATURE) { - Object *ob_reference = (Object *)local->override_library->reference; + Object *ob_reference = reinterpret_cast(local->override_library->reference); BLI_assert(ob_local->data != nullptr); BLI_assert(ob_reference->data != nullptr); BKE_pose_ensure(bmain, ob_local, static_cast(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(&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(id); if (ob->type == OB_ARMATURE) { BLI_assert(ob->data != nullptr); BKE_pose_ensure(bmain, ob, static_cast(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(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
(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 -#include -#include - -#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 full_names; + /* For each base name (i.e. without numeric suffix), track the + * numeric suffixes that are in use. */ + Map 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(__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(*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 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 : ""); + 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 : ""); + } + } + } + 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(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 : ""); + } + } + } + } + } + lib = static_cast((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((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.c deleted file mode 100644 index 2a1c940493c..00000000000 --- a/source/blender/blenkernel/intern/mball.c +++ /dev/null @@ -1,754 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright 2001-2002 NaN Holding BV. All rights reserved. */ - -/** \file - * \ingroup bke - * - * MetaBalls are created from a single Object (with a name without number in it), - * here the DispList and BoundBox also is located. - * All objects with the same name (but with a number in it) are added to this. - * - * texture coordinates are patched within the displist - */ - -#include -#include -#include -#include -#include -#include - -#include "MEM_guardedalloc.h" - -/* Allow using deprecated functionality for .blend file I/O. */ -#define DNA_DEPRECATED_ALLOW - -#include "DNA_defaults.h" -#include "DNA_material_types.h" -#include "DNA_meta_types.h" -#include "DNA_object_types.h" -#include "DNA_scene_types.h" - -#include "BLI_blenlib.h" -#include "BLI_math.h" -#include "BLI_string_utils.h" -#include "BLI_utildefines.h" - -#include "BLT_translation.h" - -#include "BKE_main.h" - -#include "BKE_anim_data.h" -#include "BKE_curve.h" -#include "BKE_displist.h" -#include "BKE_idtype.h" -#include "BKE_lib_id.h" -#include "BKE_lib_query.h" -#include "BKE_material.h" -#include "BKE_mball.h" -#include "BKE_object.h" -#include "BKE_scene.h" - -#include "DEG_depsgraph.h" - -#include "BLO_read_write.h" - -static void metaball_init_data(ID *id) -{ - MetaBall *metaball = (MetaBall *)id; - - BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(metaball, id)); - - MEMCPY_STRUCT_AFTER(metaball, DNA_struct_default_get(MetaBall), id); -} - -static void metaball_copy_data(Main *UNUSED(bmain), - ID *id_dst, - const ID *id_src, - const int UNUSED(flag)) -{ - MetaBall *metaball_dst = (MetaBall *)id_dst; - const MetaBall *metaball_src = (const MetaBall *)id_src; - - BLI_duplicatelist(&metaball_dst->elems, &metaball_src->elems); - - metaball_dst->mat = MEM_dupallocN(metaball_src->mat); - - metaball_dst->editelems = NULL; - metaball_dst->lastelem = NULL; - metaball_dst->batch_cache = NULL; -} - -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); - if (metaball->disp.first) { - BKE_displist_free(&metaball->disp); - } -} - -static void metaball_foreach_id(ID *id, LibraryForeachIDData *data) -{ - MetaBall *metaball = (MetaBall *)id; - for (int i = 0; i < metaball->totcol; i++) { - BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, metaball->mat[i], IDWALK_CB_USER); - } -} - -static void metaball_blend_write(BlendWriter *writer, ID *id, const void *id_address) -{ - MetaBall *mb = (MetaBall *)id; - - /* Clean up, important in undo case to reduce false detection of changed datablocks. */ - BLI_listbase_clear(&mb->disp); - mb->editelems = NULL; - /* 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; - - /* write LibData */ - BLO_write_id_struct(writer, MetaBall, id_address, &mb->id); - BKE_id_blend_write(writer, &mb->id); - - /* direct data */ - BLO_write_pointer_array(writer, mb->totcol, mb->mat); - if (mb->adt) { - BKE_animdata_blend_write(writer, mb->adt); - } - - LISTBASE_FOREACH (MetaElem *, ml, &mb->elems) { - BLO_write_struct(writer, MetaElem, ml); - } -} - -static void metaball_blend_read_data(BlendDataReader *reader, ID *id) -{ - MetaBall *mb = (MetaBall *)id; - BLO_read_data_address(reader, &mb->adt); - BKE_animdata_blend_read_data(reader, mb->adt); - - BLO_read_pointer_array(reader, (void **)&mb->mat); - - BLO_read_list(reader, &(mb->elems)); - - BLI_listbase_clear(&mb->disp); - mb->editelems = NULL; - /* 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; -} - -static void metaball_blend_read_lib(BlendLibReader *reader, ID *id) -{ - MetaBall *mb = (MetaBall *)id; - for (int a = 0; a < mb->totcol; a++) { - BLO_read_id_address(reader, mb->id.lib, &mb->mat[a]); - } - - BLO_read_id_address(reader, mb->id.lib, &mb->ipo); // XXX deprecated - old animation system -} - -static void metaball_blend_read_expand(BlendExpander *expander, ID *id) -{ - MetaBall *mb = (MetaBall *)id; - for (int a = 0; a < mb->totcol; a++) { - BLO_expand(expander, mb->mat[a]); - } -} - -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, -}; - -/* Functions */ - -MetaBall *BKE_mball_add(Main *bmain, const char *name) -{ - MetaBall *mb; - - mb = 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"); - - unit_qt(ml->quat); - - ml->rad = 2.0; - ml->s = 2.0; - ml->flag = MB_SCALE_RAD; - - switch (type) { - case MB_BALL: - ml->type = MB_BALL; - ml->expx = ml->expy = ml->expz = 1.0; - - break; - case MB_TUBE: - ml->type = MB_TUBE; - ml->expx = ml->expy = ml->expz = 1.0; - - break; - case MB_PLANE: - ml->type = MB_PLANE; - ml->expx = ml->expy = ml->expz = 1.0; - - break; - case MB_ELIPSOID: - ml->type = MB_ELIPSOID; - ml->expx = 1.2f; - ml->expy = 0.8f; - ml->expz = 1.0; - - break; - case MB_CUBE: - ml->type = MB_CUBE; - ml->expx = ml->expy = ml->expz = 1.0; - - break; - default: - break; - } - - BLI_addtail(&mb->elems, ml); - - 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) { - 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); - } - - 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; - } - - return orcodata; -} - -bool BKE_mball_is_basis(const Object *ob) -{ - /* Meta-Ball Basis Notes from Blender-2.5x - * ======================================= - * - * NOTE(@campbellbarton): This is a can of worms. - * - * This really needs a rewrite/refactor its totally broken in anything other than basic cases - * Multiple Scenes + Set Scenes & mixing meta-ball basis _should_ work but fails to update the - * depsgraph on rename and linking into scenes or removal of basis meta-ball. - * So take care when changing this code. - * - * Main idiot thing here is that the system returns #BKE_mball_basis_find() - * objects which fail a #BKE_mball_is_basis() test. - * - * Not only that but the depsgraph and their areas depend on this behavior, - * so making small fixes here isn't worth it. */ - - /* Just a quick test. */ - const int len = strlen(ob->id.name); - return (!isdigit(ob->id.name[len - 1])); -} - -bool BKE_mball_is_same_group(const Object *ob1, const Object *ob2) -{ - int basis1nr, basis2nr; - char basis1name[MAX_ID_NAME], basis2name[MAX_ID_NAME]; - - if (ob1->id.name[2] != ob2->id.name[2]) { - /* Quick return in case first char of both ID's names is not the same... */ - return false; - } - - BLI_split_name_num(basis1name, &basis1nr, ob1->id.name + 2, '.'); - BLI_split_name_num(basis2name, &basis2nr, ob2->id.name + 2, '.'); - - return STREQ(basis1name, basis2name); -} - -bool BKE_mball_is_basis_for(const Object *ob1, const Object *ob2) -{ - return BKE_mball_is_same_group(ob1, ob2) && BKE_mball_is_basis(ob1); -} - -bool BKE_mball_is_any_selected(const MetaBall *mb) -{ - for (const MetaElem *ml = mb->editelems->first; ml != NULL; ml = ml->next) { - if (ml->flag & SELECT) { - return true; - } - } - return false; -} - -bool BKE_mball_is_any_selected_multi(Base **bases, int bases_len) -{ - for (uint base_index = 0; base_index < bases_len; base_index++) { - Object *obedit = bases[base_index]->object; - MetaBall *mb = (MetaBall *)obedit->data; - if (BKE_mball_is_any_selected(mb)) { - return true; - } - } - return false; -} - -bool BKE_mball_is_any_unselected(const MetaBall *mb) -{ - for (const MetaElem *ml = mb->editelems->first; ml != NULL; ml = ml->next) { - if ((ml->flag & SELECT) == 0) { - return true; - } - } - return false; -} - -static void mball_data_properties_copy(MetaBall *mb_dst, MetaBall *mb_src) -{ - mb_dst->wiresize = mb_src->wiresize; - mb_dst->rendersize = mb_src->rendersize; - mb_dst->thresh = mb_src->thresh; - mb_dst->flag = mb_src->flag; - DEG_id_tag_update(&mb_dst->id, 0); -} - -void BKE_mball_properties_copy(Main *bmain, MetaBall *metaball_src) -{ - /** - * WARNING: This code does not cover all potential corner-cases. E.g. if: - *
-   * |   Object   |   ObData   |
-   * | ---------- | ---------- |
-   * | Meta_A     | Meta_A     |
-   * | Meta_A.001 | Meta_A.001 |
-   * | Meta_B     | Meta_A     |
-   * | Meta_B.001 | Meta_B.001 |
-   * 
- * - * Calling this function with `metaball_src` being `Meta_A.001` will update `Meta_A`, but NOT - * `Meta_B.001`. So in the 'Meta_B' family, the two metaballs will have unmatching settings now. - * - * 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);) { - if (ob_src->data != metaball_src) { - ob_src = ob_src->id.next; - continue; - } - - /* In this code we take advantage of two facts: - * - MetaBalls of the same family have the same basis name, - * - IDs are sorted by name in their Main listbase. - * So, all MetaBall objects of the same family are contiguous in bmain list (potentially mixed - * with non-meta-ball objects with same basis names). - * - * 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; - 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) { - if (ob_iter->id.name[2] != obactive_name[0]) { - break; - } - if (ob_iter->type != OB_MBALL || ob_iter->data == metaball_src) { - continue; - } - BLI_split_name_num(ob_name, &ob_nr, ob_iter->id.name + 2, '.'); - if (!STREQ(obactive_name, ob_name)) { - break; - } - - mball_data_properties_copy(ob_iter->data, metaball_src); - } - - for (ob_iter = ob_src->id.next; ob_iter != NULL; ob_iter = ob_iter->id.next) { - if (ob_iter->id.name[2] != obactive_name[0] || ID_IS_LINKED(ob_iter)) { - break; - } - if (ob_iter->type != OB_MBALL || ob_iter->data == metaball_src) { - continue; - } - BLI_split_name_num(ob_name, &ob_nr, ob_iter->id.name + 2, '.'); - if (!STREQ(obactive_name, ob_name)) { - break; - } - - mball_data_properties_copy(ob_iter->data, metaball_src); - } - - ob_src = ob_iter; - } -} - -Object *BKE_mball_basis_find(Scene *scene, Object *object) -{ - Object *bob = object; - int basisnr, obnr; - char basisname[MAX_ID_NAME], obname[MAX_ID_NAME]; - - 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) { - Object *ob = base->object; - if ((ob->type == OB_MBALL) && !(base->flag & BASE_FROM_DUPLI)) { - if (ob != bob) { - BLI_split_name_num(obname, &obnr, ob->id.name + 2, '.'); - - /* Object ob has to be in same "group" ... it means, - * that it has to have same base of its name. */ - if (STREQ(obname, basisname)) { - if (obnr < basisnr) { - object = ob; - basisnr = obnr; - } - } - } - } - } - } - - return object; -} - -bool BKE_mball_minmax_ex( - const MetaBall *mb, float min[3], float max[3], const float obmat[4][4], const short flag) -{ - const float scale = obmat ? mat4_to_scale(obmat) : 1.0f; - bool changed = false; - float centroid[3], vec[3]; - - INIT_MINMAX(min, max); - - LISTBASE_FOREACH (const MetaElem *, ml, &mb->elems) { - if ((ml->flag & flag) == flag) { - const float scale_mb = (ml->rad * 0.5f) * scale; - - if (obmat) { - mul_v3_m4v3(centroid, obmat, &ml->x); - } - else { - copy_v3_v3(centroid, &ml->x); - } - - /* TODO(campbell): 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); - minmax_v3v3_v3(min, max, vec); - } - changed = true; - } - } - - return changed; -} - -bool BKE_mball_minmax(const MetaBall *mb, float min[3], float max[3]) -{ - INIT_MINMAX(min, max); - - LISTBASE_FOREACH (const MetaElem *, ml, &mb->elems) { - minmax_v3v3_v3(min, max, &ml->x); - } - - return (BLI_listbase_is_empty(&mb->elems) == false); -} - -bool BKE_mball_center_median(const MetaBall *mb, float r_cent[3]) -{ - int total = 0; - - zero_v3(r_cent); - - LISTBASE_FOREACH (const MetaElem *, ml, &mb->elems) { - add_v3_v3(r_cent, &ml->x); - total++; - } - - if (total) { - mul_v3_fl(r_cent, 1.0f / (float)total); - } - - return (total != 0); -} - -bool BKE_mball_center_bounds(const MetaBall *mb, float r_cent[3]) -{ - float min[3], max[3]; - - if (BKE_mball_minmax(mb, min, max)) { - mid_v3_v3v3(r_cent, min, max); - return true; - } - - return false; -} - -void BKE_mball_transform(MetaBall *mb, const float mat[4][4], const bool do_props) -{ - float quat[4]; - const float scale = mat4_to_scale(mat); - const float scale_sqrt = sqrtf(scale); - - mat4_to_quat(quat, mat); - - LISTBASE_FOREACH (MetaElem *, ml, &mb->elems) { - mul_m4_v3(mat, &ml->x); - mul_qt_qtqt(ml->quat, quat, ml->quat); - - if (do_props) { - ml->rad *= scale; - /* hrmf, probably elems shouldn't be - * treating scale differently - campbell */ - if (!MB_TYPE_SIZE_SQUARED(ml->type)) { - mul_v3_fl(&ml->expx, scale); - } - else { - mul_v3_fl(&ml->expx, scale_sqrt); - } - } - } -} - -void BKE_mball_translate(MetaBall *mb, const float offset[3]) -{ - LISTBASE_FOREACH (MetaElem *, ml, &mb->elems) { - add_v3_v3(&ml->x, offset); - } -} - -int BKE_mball_select_count(const MetaBall *mb) -{ - int sel = 0; - LISTBASE_FOREACH (const MetaElem *, ml, mb->editelems) { - if (ml->flag & SELECT) { - sel++; - } - } - return sel; -} - -int BKE_mball_select_count_multi(Base **bases, int bases_len) -{ - int sel = 0; - for (uint ob_index = 0; ob_index < bases_len; ob_index++) { - const Object *obedit = bases[ob_index]->object; - const MetaBall *mb = (MetaBall *)obedit->data; - sel += BKE_mball_select_count(mb); - } - return sel; -} - -bool BKE_mball_select_all(MetaBall *mb) -{ - bool changed = false; - LISTBASE_FOREACH (MetaElem *, ml, mb->editelems) { - if ((ml->flag & SELECT) == 0) { - ml->flag |= SELECT; - changed = true; - } - } - return changed; -} - -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; - changed_multi |= BKE_mball_select_all(mb); - } - return changed_multi; -} - -bool BKE_mball_deselect_all(MetaBall *mb) -{ - bool changed = false; - LISTBASE_FOREACH (MetaElem *, ml, mb->editelems) { - if ((ml->flag & SELECT) != 0) { - ml->flag &= ~SELECT; - changed = true; - } - } - return changed; -} - -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; - changed_multi |= BKE_mball_deselect_all(mb); - DEG_id_tag_update(&mb->id, ID_RECALC_SELECT); - } - return changed_multi; -} - -bool BKE_mball_select_swap(MetaBall *mb) -{ - bool changed = false; - LISTBASE_FOREACH (MetaElem *, ml, mb->editelems) { - ml->flag ^= SELECT; - changed = true; - } - return changed; -} - -bool BKE_mball_select_swap_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 = (MetaBall *)obedit->data; - changed_multi |= BKE_mball_select_swap(mb); - } - return changed_multi; -} - -/* **** Depsgraph evaluation **** */ - -/* Draw Engine */ - -void (*BKE_mball_batch_cache_dirty_tag_cb)(MetaBall *mb, int mode) = NULL; -void (*BKE_mball_batch_cache_free_cb)(MetaBall *mb) = NULL; - -void BKE_mball_batch_cache_dirty_tag(MetaBall *mb, int mode) -{ - if (mb->batch_cache) { - BKE_mball_batch_cache_dirty_tag_cb(mb, mode); - } -} -void BKE_mball_batch_cache_free(MetaBall *mb) -{ - if (mb->batch_cache) { - BKE_mball_batch_cache_free_cb(mb); - } -} diff --git a/source/blender/blenkernel/intern/mball.cc b/source/blender/blenkernel/intern/mball.cc new file mode 100644 index 00000000000..91797f8ed2f --- /dev/null +++ b/source/blender/blenkernel/intern/mball.cc @@ -0,0 +1,715 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2001-2002 NaN Holding BV. All rights reserved. */ + +/** \file + * \ingroup bke + * + * 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. + */ + +#include +#include +#include +#include +#include +#include + +#include "MEM_guardedalloc.h" + +/* Allow using deprecated functionality for .blend file I/O. */ +#define DNA_DEPRECATED_ALLOW + +#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" + +#include "BLI_blenlib.h" +#include "BLI_math.h" +#include "BLI_string_utils.h" +#include "BLI_utildefines.h" + +#include "BLT_translation.h" + +#include "BKE_main.h" + +#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" + +#include "DEG_depsgraph.h" + +#include "BLO_read_write.h" + +static void metaball_init_data(ID *id) +{ + MetaBall *metaball = (MetaBall *)id; + + BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(metaball, id)); + + MEMCPY_STRUCT_AFTER(metaball, DNA_struct_default_get(MetaBall), id); +} + +static void metaball_copy_data(Main *UNUSED(bmain), + ID *id_dst, + const ID *id_src, + const int UNUSED(flag)) +{ + MetaBall *metaball_dst = (MetaBall *)id_dst; + const MetaBall *metaball_src = (const MetaBall *)id_src; + + BLI_duplicatelist(&metaball_dst->elems, &metaball_src->elems); + + metaball_dst->mat = static_cast(MEM_dupallocN(metaball_src->mat)); + + metaball_dst->editelems = nullptr; + metaball_dst->lastelem = nullptr; +} + +static void metaball_free_data(ID *id) +{ + MetaBall *metaball = (MetaBall *)id; + + MEM_SAFE_FREE(metaball->mat); + + BLI_freelistN(&metaball->elems); + if (metaball->disp.first) { + BKE_displist_free(&metaball->disp); + } +} + +static void metaball_foreach_id(ID *id, LibraryForeachIDData *data) +{ + MetaBall *metaball = (MetaBall *)id; + for (int i = 0; i < metaball->totcol; i++) { + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, metaball->mat[i], IDWALK_CB_USER); + } +} + +static void metaball_blend_write(BlendWriter *writer, ID *id, const void *id_address) +{ + MetaBall *mb = (MetaBall *)id; + + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + BLI_listbase_clear(&mb->disp); + mb->editelems = nullptr; + /* Must always be cleared (meta's don't have their own edit-data). */ + mb->needs_flush_to_id = 0; + mb->lastelem = nullptr; + + /* write LibData */ + BLO_write_id_struct(writer, MetaBall, id_address, &mb->id); + BKE_id_blend_write(writer, &mb->id); + + /* direct data */ + BLO_write_pointer_array(writer, mb->totcol, mb->mat); + if (mb->adt) { + BKE_animdata_blend_write(writer, mb->adt); + } + + LISTBASE_FOREACH (MetaElem *, ml, &mb->elems) { + BLO_write_struct(writer, MetaElem, ml); + } +} + +static void metaball_blend_read_data(BlendDataReader *reader, ID *id) +{ + MetaBall *mb = (MetaBall *)id; + BLO_read_data_address(reader, &mb->adt); + BKE_animdata_blend_read_data(reader, mb->adt); + + BLO_read_pointer_array(reader, (void **)&mb->mat); + + BLO_read_list(reader, &(mb->elems)); + + BLI_listbase_clear(&mb->disp); + 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 = nullptr; + mb->lastelem = nullptr; +} + +static void metaball_blend_read_lib(BlendLibReader *reader, ID *id) +{ + MetaBall *mb = (MetaBall *)id; + for (int a = 0; a < mb->totcol; a++) { + BLO_read_id_address(reader, mb->id.lib, &mb->mat[a]); + } + + BLO_read_id_address(reader, mb->id.lib, &mb->ipo); // XXX deprecated - old animation system +} + +static void metaball_blend_read_expand(BlendExpander *expander, ID *id) +{ + MetaBall *mb = (MetaBall *)id; + for (int a = 0; a < mb->totcol; a++) { + BLO_expand(expander, mb->mat[a]); + } +} + +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 */ 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 = static_cast(BKE_id_new(bmain, ID_MB, name)); + return mb; +} + +MetaElem *BKE_mball_element_add(MetaBall *mb, const int type) +{ + MetaElem *ml = MEM_cnew(__func__); + + unit_qt(ml->quat); + + ml->rad = 2.0; + ml->s = 2.0; + ml->flag = MB_SCALE_RAD; + + switch (type) { + case MB_BALL: + ml->type = MB_BALL; + ml->expx = ml->expy = ml->expz = 1.0; + + break; + case MB_TUBE: + ml->type = MB_TUBE; + ml->expx = ml->expy = ml->expz = 1.0; + + break; + case MB_PLANE: + ml->type = MB_PLANE; + ml->expx = ml->expy = ml->expz = 1.0; + + break; + case MB_ELIPSOID: + ml->type = MB_ELIPSOID; + ml->expx = 1.2f; + ml->expy = 0.8f; + ml->expz = 1.0; + + break; + case MB_CUBE: + ml->type = MB_CUBE; + ml->expx = ml->expy = ml->expz = 1.0; + + break; + default: + break; + } + + BLI_addtail(&mb->elems, ml); + + return ml; +} + +BoundBox *BKE_mball_boundbox_get(Object *ob) +{ + BLI_assert(ob->type == OB_MBALL); + if (ob->runtime.bb != nullptr && (ob->runtime.bb->flag & BOUNDBOX_DIRTY) == 0) { + return ob->runtime.bb; + } + if (ob->runtime.bb == nullptr) { + ob->runtime.bb = MEM_cnew(__func__); + } + + /* 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); + } + + 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) +{ + /* Meta-Ball Basis Notes from Blender-2.5x + * ======================================= + * + * NOTE(@campbellbarton): This is a can of worms. + * + * This really needs a rewrite/refactor its totally broken in anything other than basic cases + * Multiple Scenes + Set Scenes & mixing meta-ball basis _should_ work but fails to update the + * depsgraph on rename and linking into scenes or removal of basis meta-ball. + * So take care when changing this code. + * + * Main idiot thing here is that the system returns #BKE_mball_basis_find() + * objects which fail a #BKE_mball_is_basis() test. + * + * Not only that but the depsgraph and their areas depend on this behavior, + * so making small fixes here isn't worth it. */ + + /* Just a quick test. */ + const int len = strlen(ob->id.name); + return (!isdigit(ob->id.name[len - 1])); +} + +bool BKE_mball_is_same_group(const Object *ob1, const Object *ob2) +{ + int basis1nr, basis2nr; + char basis1name[MAX_ID_NAME], basis2name[MAX_ID_NAME]; + + if (ob1->id.name[2] != ob2->id.name[2]) { + /* Quick return in case first char of both ID's names is not the same... */ + return false; + } + + BLI_split_name_num(basis1name, &basis1nr, ob1->id.name + 2, '.'); + BLI_split_name_num(basis2name, &basis2nr, ob2->id.name + 2, '.'); + + return STREQ(basis1name, basis2name); +} + +bool BKE_mball_is_basis_for(const Object *ob1, const Object *ob2) +{ + return BKE_mball_is_same_group(ob1, ob2) && BKE_mball_is_basis(ob1); +} + +bool BKE_mball_is_any_selected(const MetaBall *mb) +{ + LISTBASE_FOREACH (const MetaElem *, ml, mb->editelems) { + if (ml->flag & SELECT) { + return true; + } + } + return false; +} + +bool BKE_mball_is_any_selected_multi(Base **bases, int bases_len) +{ + for (uint base_index = 0; base_index < bases_len; base_index++) { + Object *obedit = bases[base_index]->object; + MetaBall *mb = (MetaBall *)obedit->data; + if (BKE_mball_is_any_selected(mb)) { + return true; + } + } + return false; +} + +bool BKE_mball_is_any_unselected(const MetaBall *mb) +{ + LISTBASE_FOREACH (const MetaElem *, ml, mb->editelems) { + if ((ml->flag & SELECT) == 0) { + return true; + } + } + return false; +} + +static void mball_data_properties_copy(MetaBall *mb_dst, MetaBall *mb_src) +{ + mb_dst->wiresize = mb_src->wiresize; + mb_dst->rendersize = mb_src->rendersize; + mb_dst->thresh = mb_src->thresh; + mb_dst->flag = mb_src->flag; + DEG_id_tag_update(&mb_dst->id, 0); +} + +void BKE_mball_properties_copy(Main *bmain, MetaBall *metaball_src) +{ + /** + * WARNING: This code does not cover all potential corner-cases. E.g. if: + *
+   * |   Object   |   ObData   |
+   * | ---------- | ---------- |
+   * | Meta_A     | Meta_A     |
+   * | Meta_A.001 | Meta_A.001 |
+   * | Meta_B     | Meta_A     |
+   * | Meta_B.001 | Meta_B.001 |
+   * 
+ * + * Calling this function with `metaball_src` being `Meta_A.001` will update `Meta_A`, but NOT + * `Meta_B.001`. So in the 'Meta_B' family, the two metaballs will have unmatching settings now. + * + * 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 = static_cast(bmain->objects.first); + ob_src != nullptr && !ID_IS_LINKED(ob_src);) { + if (ob_src->data != metaball_src) { + ob_src = static_cast(ob_src->id.next); + continue; + } + + /* In this code we take advantage of two facts: + * - MetaBalls of the same family have the same basis name, + * - IDs are sorted by name in their Main listbase. + * So, all MetaBall objects of the same family are contiguous in bmain list (potentially mixed + * with non-meta-ball objects with same basis names). + * + * 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 = 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 = static_cast(ob_src->id.prev); ob_iter != nullptr; + ob_iter = static_cast(ob_iter->id.prev)) { + if (ob_iter->id.name[2] != obactive_name[0]) { + break; + } + if (ob_iter->type != OB_MBALL || ob_iter->data == metaball_src) { + continue; + } + BLI_split_name_num(ob_name, &ob_nr, ob_iter->id.name + 2, '.'); + if (!STREQ(obactive_name, ob_name)) { + break; + } + + mball_data_properties_copy(static_cast(ob_iter->data), metaball_src); + } + + for (ob_iter = static_cast(ob_src->id.next); ob_iter != nullptr; + ob_iter = static_cast(ob_iter->id.next)) { + if (ob_iter->id.name[2] != obactive_name[0] || ID_IS_LINKED(ob_iter)) { + break; + } + if (ob_iter->type != OB_MBALL || ob_iter->data == metaball_src) { + continue; + } + BLI_split_name_num(ob_name, &ob_nr, ob_iter->id.name + 2, '.'); + if (!STREQ(obactive_name, ob_name)) { + break; + } + + mball_data_properties_copy(static_cast(ob_iter->data), metaball_src); + } + + ob_src = ob_iter; + } +} + +Object *BKE_mball_basis_find(Scene *scene, Object *object) +{ + Object *bob = object; + int basisnr, obnr; + char basisname[MAX_ID_NAME], obname[MAX_ID_NAME]; + + BLI_split_name_num(basisname, &basisnr, object->id.name + 2, '.'); + + LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { + 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) { + BLI_split_name_num(obname, &obnr, ob->id.name + 2, '.'); + + /* Object ob has to be in same "group" ... it means, + * that it has to have same base of its name. */ + if (STREQ(obname, basisname)) { + if (obnr < basisnr) { + object = ob; + basisnr = obnr; + } + } + } + } + } + } + + return object; +} + +bool BKE_mball_minmax_ex( + const MetaBall *mb, float min[3], float max[3], const float obmat[4][4], const short flag) +{ + const float scale = obmat ? mat4_to_scale(obmat) : 1.0f; + bool changed = false; + float centroid[3], vec[3]; + + INIT_MINMAX(min, max); + + LISTBASE_FOREACH (const MetaElem *, ml, &mb->elems) { + if ((ml->flag & flag) == flag) { + const float scale_mb = (ml->rad * 0.5f) * scale; + + if (obmat) { + mul_v3_m4v3(centroid, obmat, &ml->x); + } + else { + copy_v3_v3(centroid, &ml->x); + } + + /* 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); + minmax_v3v3_v3(min, max, vec); + } + changed = true; + } + } + + return changed; +} + +bool BKE_mball_minmax(const MetaBall *mb, float min[3], float max[3]) +{ + INIT_MINMAX(min, max); + + LISTBASE_FOREACH (const MetaElem *, ml, &mb->elems) { + minmax_v3v3_v3(min, max, &ml->x); + } + + return (BLI_listbase_is_empty(&mb->elems) == false); +} + +bool BKE_mball_center_median(const MetaBall *mb, float r_cent[3]) +{ + int total = 0; + + zero_v3(r_cent); + + LISTBASE_FOREACH (const MetaElem *, ml, &mb->elems) { + add_v3_v3(r_cent, &ml->x); + total++; + } + + if (total) { + mul_v3_fl(r_cent, 1.0f / (float)total); + } + + return (total != 0); +} + +bool BKE_mball_center_bounds(const MetaBall *mb, float r_cent[3]) +{ + float min[3], max[3]; + + if (BKE_mball_minmax(mb, min, max)) { + mid_v3_v3v3(r_cent, min, max); + return true; + } + + return false; +} + +void BKE_mball_transform(MetaBall *mb, const float mat[4][4], const bool do_props) +{ + float quat[4]; + const float scale = mat4_to_scale(mat); + const float scale_sqrt = sqrtf(scale); + + mat4_to_quat(quat, mat); + + LISTBASE_FOREACH (MetaElem *, ml, &mb->elems) { + mul_m4_v3(mat, &ml->x); + mul_qt_qtqt(ml->quat, quat, ml->quat); + + if (do_props) { + ml->rad *= scale; + /* hrmf, probably elems shouldn't be + * treating scale differently - campbell */ + if (!MB_TYPE_SIZE_SQUARED(ml->type)) { + mul_v3_fl(&ml->expx, scale); + } + else { + mul_v3_fl(&ml->expx, scale_sqrt); + } + } + } +} + +void BKE_mball_translate(MetaBall *mb, const float offset[3]) +{ + LISTBASE_FOREACH (MetaElem *, ml, &mb->elems) { + add_v3_v3(&ml->x, offset); + } +} + +int BKE_mball_select_count(const MetaBall *mb) +{ + int sel = 0; + LISTBASE_FOREACH (const MetaElem *, ml, mb->editelems) { + if (ml->flag & SELECT) { + sel++; + } + } + return sel; +} + +int BKE_mball_select_count_multi(Base **bases, int bases_len) +{ + int sel = 0; + for (uint ob_index = 0; ob_index < bases_len; ob_index++) { + const Object *obedit = bases[ob_index]->object; + const MetaBall *mb = (MetaBall *)obedit->data; + sel += BKE_mball_select_count(mb); + } + return sel; +} + +bool BKE_mball_select_all(MetaBall *mb) +{ + bool changed = false; + LISTBASE_FOREACH (MetaElem *, ml, mb->editelems) { + if ((ml->flag & SELECT) == 0) { + ml->flag |= SELECT; + changed = true; + } + } + return changed; +} + +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 = static_cast(obedit->data); + changed_multi |= BKE_mball_select_all(mb); + } + return changed_multi; +} + +bool BKE_mball_deselect_all(MetaBall *mb) +{ + bool changed = false; + LISTBASE_FOREACH (MetaElem *, ml, mb->editelems) { + if ((ml->flag & SELECT) != 0) { + ml->flag &= ~SELECT; + changed = true; + } + } + return changed; +} + +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 = static_cast(obedit->data); + changed_multi |= BKE_mball_deselect_all(mb); + DEG_id_tag_update(&mb->id, ID_RECALC_SELECT); + } + return changed_multi; +} + +bool BKE_mball_select_swap(MetaBall *mb) +{ + bool changed = false; + LISTBASE_FOREACH (MetaElem *, ml, mb->editelems) { + ml->flag ^= SELECT; + changed = true; + } + return changed; +} + +bool BKE_mball_select_swap_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 = (MetaBall *)obedit->data; + changed_multi |= BKE_mball_select_swap(mb); + } + return changed_multi; +} + +/* **** Depsgraph evaluation **** */ + +void BKE_mball_data_update(Depsgraph *depsgraph, Scene *scene, Object *ob) +{ + BLI_assert(ob->type == OB_MBALL); + + BKE_object_free_derived_caches(ob); + + const Object *basis_object = BKE_mball_basis_find(scene, ob); + if (ob != basis_object) { + return; + } + + Mesh *mesh = BKE_mball_polygonize(depsgraph, scene, ob); + if (mesh == nullptr) { + return; + } + + const MetaBall *mball = static_cast(ob->data); + mesh->mat = static_cast(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(__func__); + } + blender::float3 min(std::numeric_limits::max()); + blender::float3 max(-std::numeric_limits::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( + 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 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(mesh->verts().data()); + mesh->medge = const_cast(mesh->edges().data()); + mesh->mpoly = const_cast(mesh->polys().data()); + mesh->mloop = const_cast(mesh->loops().data()); + mesh->dvert = const_cast(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 loops_1 = m1->loops(); + const Span 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 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 material_indices = attributes.lookup_for_write("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 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 material_indices = attributes.lookup_or_default( + "material_index", ATTR_DOMAIN_FACE, 0); + if (material_indices.is_single()) { + return material_indices.get_internal_single() == index; } - - return false; + const VArraySpan 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 material_indices = attributes.lookup_or_add_for_write_span( + "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 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 edges = mesh->edges(); + const Span 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 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 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 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 verts = mesh->verts(); + const Span 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 verts = me->verts(); + const Span edges = me->edges(); + const Span 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 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 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 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 verts = mesh->verts(); + const Span edges = mesh->edges(); + const Span polys = mesh->polys(); + const Span 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 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 edges = mesh->edges_for_write(); + MutableSpan loops = mesh->loops_for_write(); + const Span 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 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 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 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 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 meshes, bool need_face_flip = r_info->has_negative_transform[mi] != r_info->has_negative_transform[0]; Vector verts(me->totvert); - Span mverts = Span(me->mvert, me->totvert); + const Span mesh_verts = me->verts(); + const Span polys = me->polys(); + const Span 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 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 material_remap) + Span material_remap, + MutableSpan 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 src_material_indices = orig_me->attributes().lookup_or_default( + "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 &orig_loops) + MutableSpan r_orig_loops) { - orig_loops.fill(-1); + r_orig_loops.fill(-1); + const Span 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 verts = me->verts(); + const Span loops = me->loops(); + const Span 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 dst_verts = dest_mesh->verts(); + const Span 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 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 dst_material_indices = + result->attributes_for_write().lookup_or_add_for_write_only_span("material_index", + ATTR_DOMAIN_FACE); int cur_loop_index = 0; - MLoop *l = result->mloop; + MutableSpan dst_loops = result->loops_for_write(); + MutableSpan 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()); + Span(), + 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 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 meshes, /* Store intersecting edge indices. */ if (r_intersecting_edges != nullptr) { + const Span polys = result->polys(); + const Span 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 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 edge_maps, uint32_t parallel_mask) { - const Span loops{mesh->mloop, mesh->totloop}; + const Span polys = mesh->polys(); + const Span 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 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 edge_maps, uint32_t parallel_mask) { - const MutableSpan loops{mesh->mloop, mesh->totloop}; + const Span polys = mesh->polys(); + MutableSpan 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 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 polys = mesh.polys(); + MutableSpan 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 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 verts = mesh->verts_for_write(); + MutableSpan edges = mesh->edges_for_write(); + MutableSpan polys = mesh->polys_for_write(); + MutableSpan 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 material_indices = attributes.lookup_or_add_for_write_only_span( + "material_index", ATTR_DOMAIN_FACE); + MLoopUV *mloopuv = static_cast(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 verts = me->verts(); + const Span mesh_edges = me->edges(); + const Span polys = me->polys(); + const Span 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"); @@ -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 mesh_positions = mesh_attributes.lookup_or_default( + "position", ATTR_DOMAIN_POINT, float3(0)); + bke::SpanAttributeWriter point_positions = + point_attributes.lookup_or_add_for_write_only_span("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 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 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(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 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 kb_coords(static_cast(kb->data), kb->totelem); + mesh.attributes().lookup("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(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 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 verts = me->verts(); + const Span polys = me->polys(); + const Span 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 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 verts = me->verts(); + const Span polys = me->polys(); + const Span 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 hide_vert = attributes.lookup_or_default( + ".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 hide_vert_span{hide_vert}; + const Span edges = me->edges(); + const Span polys = me->polys(); + const Span loops = me->loops(); + + /* Hide edges when either of their vertices are hidden. */ + SpanAttributeWriter hide_edge = attributes.lookup_or_add_for_write_only_span( + ".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 hide_poly = attributes.lookup_or_add_for_write_only_span( + ".hide_poly", ATTR_DOMAIN_FACE); + for (const int i : polys.index_range()) { + const MPoly &poly = polys[i]; + const Span 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 hide_poly = attributes.lookup_or_default( + ".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 hide_poly_span{hide_poly}; + const Span polys = me->polys(); + const Span loops = me->loops(); + SpanAttributeWriter hide_vert = attributes.lookup_or_add_for_write_only_span( + ".hide_vert", ATTR_DOMAIN_POINT); + SpanAttributeWriter hide_edge = attributes.lookup_or_add_for_write_only_span( + ".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 verts, const Span loops, + const VArray &hide_edge, + const VArray &hide_poly, MutableSpan edges, MutableSpan 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 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 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(".hide_edge", ATTR_DOMAIN_EDGE, false), + attributes.lookup_or_default(".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 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 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_; + Span mpoly_; + Span medge_; Vector 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 +#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( + CustomData_get_layer_named(pdata, CD_PROP_INT32, "material_index")); + if (material_indices == nullptr) { + material_indices = static_cast(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( + 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 verts = mesh->verts_for_write(); + if (const float *weights = static_cast( + 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 edges = mesh->edges_for_write(); + if (const float *weights = static_cast( + 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 verts = mesh->verts(); + if (mesh->cd_flag & ME_CDFLAG_VERT_BWEIGHT) { + float *weights = static_cast( + 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 edges = mesh->edges(); + if (mesh->cd_flag & ME_CDFLAG_EDGE_BWEIGHT) { + float *weights = static_cast( + 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 verts = mesh->verts_for_write(); + const VArray hide_vert = attributes.lookup_or_default( + ".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 edges = mesh->edges_for_write(); + const VArray hide_edge = attributes.lookup_or_default( + ".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 polys = mesh->polys_for_write(); + const VArray hide_poly = attributes.lookup_or_default( + ".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 verts = mesh->verts(); + if (std::any_of( + verts.begin(), verts.end(), [](const MVert &vert) { return vert.flag & ME_HIDE; })) { + SpanAttributeWriter hide_vert = attributes.lookup_or_add_for_write_only_span( + ".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 edges = mesh->edges(); + if (std::any_of( + edges.begin(), edges.end(), [](const MEdge &edge) { return edge.flag & ME_HIDE; })) { + SpanAttributeWriter hide_edge = attributes.lookup_or_add_for_write_only_span( + ".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 polys = mesh->polys(); + if (std::any_of( + polys.begin(), polys.end(), [](const MPoly &poly) { return poly.flag & ME_HIDE; })) { + SpanAttributeWriter hide_poly = attributes.lookup_or_add_for_write_only_span( + ".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 polys = mesh->polys_for_write(); + const VArray material_indices = attributes.lookup_or_default( + "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 polys = mesh->polys(); + if (std::any_of( + polys.begin(), polys.end(), [](const MPoly &poly) { return poly.mat_nr_legacy != 0; })) { + SpanAttributeWriter material_indices = attributes.lookup_or_add_for_write_only_span( + "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.c deleted file mode 100644 index 9c4098e2db6..00000000000 --- a/source/blender/blenkernel/intern/mesh_mapping.c +++ /dev/null @@ -1,1158 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/** \file - * \ingroup bke - * - * Functions for accessing mesh connectivity data. - * eg: polys connected to verts, UV's connected to verts. - */ - -#include "MEM_guardedalloc.h" - -#include "DNA_meshdata_types.h" -#include "DNA_vec_types.h" - -#include "BLI_bitmap.h" -#include "BLI_buffer.h" -#include "BLI_math.h" -#include "BLI_utildefines.h" - -#include "BKE_customdata.h" -#include "BKE_mesh_mapping.h" -#include "BLI_memarena.h" - -#include "BLI_strict_flags.h" - -/* -------------------------------------------------------------------- */ -/** \name Mesh Connectivity Mapping - * \{ */ - -/* ngon version wip, based on BM_uv_vert_map_create */ -UvVertMap *BKE_mesh_uv_vert_map_create(const MPoly *mpoly, - const MLoop *mloop, - const MLoopUV *mloopuv, - uint totpoly, - uint totvert, - const float limit[2], - const bool selected, - const bool use_winding) -{ - UvVertMap *vmap; - UvMapVert *buf; - const MPoly *mp; - uint a; - int i, totuv, nverts; - - bool *winding = NULL; - BLI_buffer_declare_static(vec2f, tf_uv_buf, BLI_BUFFER_NOP, 32); - - totuv = 0; - - /* generate UvMapVert array */ - mp = mpoly; - for (a = 0; a < totpoly; a++, mp++) { - if (!selected || (!(mp->flag & ME_HIDE) && (mp->flag & ME_FACE_SEL))) { - totuv += mp->totloop; - } - } - - if (totuv == 0) { - return NULL; - } - - vmap = (UvVertMap *)MEM_callocN(sizeof(*vmap), "UvVertMap"); - 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"); - } - - if (!vmap->vert || !vmap->buf) { - BKE_mesh_uv_vert_map_free(vmap); - return NULL; - } - - mp = mpoly; - for (a = 0; a < totpoly; a++, mp++) { - if (!selected || (!(mp->flag & ME_HIDE) && (mp->flag & ME_FACE_SEL))) { - float(*tf_uv)[2] = NULL; - - if (use_winding) { - tf_uv = (float(*)[2])BLI_buffer_reinit_data(&tf_uv_buf, vec2f, (size_t)mp->totloop); - } - - nverts = mp->totloop; - - for (i = 0; i < nverts; i++) { - buf->loop_of_poly_index = (unsigned short)i; - buf->poly_index = a; - buf->separate = 0; - buf->next = vmap->vert[mloop[mp->loopstart + i].v]; - vmap->vert[mloop[mp->loopstart + i].v] = buf; - - if (use_winding) { - copy_v2_v2(tf_uv[i], mloopuv[mpoly[a].loopstart + i].uv); - } - - buf++; - } - - if (use_winding) { - winding[a] = cross_poly_v2(tf_uv, (uint)nverts) > 0; - } - } - } - - /* sort individual uvs for each vert */ - for (a = 0; a < totvert; a++) { - UvMapVert *newvlist = NULL, *vlist = vmap->vert[a]; - UvMapVert *iterv, *v, *lastv, *next; - const float *uv, *uv2; - float uvdiff[2]; - - while (vlist) { - v = vlist; - vlist = vlist->next; - v->next = newvlist; - newvlist = v; - - uv = mloopuv[mpoly[v->poly_index].loopstart + v->loop_of_poly_index].uv; - lastv = NULL; - iterv = vlist; - - while (iterv) { - next = iterv->next; - - uv2 = mloopuv[mpoly[iterv->poly_index].loopstart + iterv->loop_of_poly_index].uv; - sub_v2_v2v2(uvdiff, uv2, uv); - - if (fabsf(uv[0] - uv2[0]) < limit[0] && fabsf(uv[1] - uv2[1]) < limit[1] && - (!use_winding || winding[iterv->poly_index] == winding[v->poly_index])) { - if (lastv) { - lastv->next = next; - } - else { - vlist = next; - } - iterv->next = newvlist; - newvlist = iterv; - } - else { - lastv = iterv; - } - - iterv = next; - } - - newvlist->separate = 1; - } - - vmap->vert[a] = newvlist; - } - - if (use_winding) { - MEM_freeN(winding); - } - - BLI_buffer_free(&tf_uv_buf); - - return vmap; -} - -UvMapVert *BKE_mesh_uv_vert_map_get_vert(UvVertMap *vmap, uint v) -{ - return vmap->vert[v]; -} - -void BKE_mesh_uv_vert_map_free(UvVertMap *vmap) -{ - if (vmap) { - if (vmap->vert) { - MEM_freeN(vmap->vert); - } - if (vmap->buf) { - MEM_freeN(vmap->buf); - } - MEM_freeN(vmap); - } -} - -/** - * Generates a map where the key is the vertex and the value is a list - * of polys or loops that use that vertex as a corner. The lists are allocated - * from one memory pool. - * - * Wrapped by #BKE_mesh_vert_poly_map_create & BKE_mesh_vert_loop_map_create - */ -static void mesh_vert_poly_or_loop_map_create(MeshElemMap **r_map, - int **r_mem, - const MPoly *mpoly, - const MLoop *mloop, - int totvert, - int totpoly, - int totloop, - const bool do_loops) -{ - MeshElemMap *map = MEM_callocN(sizeof(MeshElemMap) * (size_t)totvert, __func__); - int *indices, *index_iter; - int i, j; - - indices = index_iter = MEM_mallocN(sizeof(int) * (size_t)totloop, __func__); - - /* Count number of polys for each vertex */ - for (i = 0; i < totpoly; i++) { - const MPoly *p = &mpoly[i]; - - for (j = 0; j < p->totloop; j++) { - map[mloop[p->loopstart + j].v].count++; - } - } - - /* Assign indices mem */ - for (i = 0; i < totvert; i++) { - map[i].indices = index_iter; - index_iter += map[i].count; - - /* Reset 'count' for use as index in last loop */ - map[i].count = 0; - } - - /* Find the users */ - for (i = 0; i < totpoly; i++) { - const MPoly *p = &mpoly[i]; - - for (j = 0; j < p->totloop; j++) { - uint v = mloop[p->loopstart + j].v; - - map[v].indices[map[v].count] = do_loops ? p->loopstart + j : i; - map[v].count++; - } - } - - *r_map = map; - *r_mem = indices; -} - -void BKE_mesh_vert_poly_map_create(MeshElemMap **r_map, - int **r_mem, - const MPoly *mpoly, - const MLoop *mloop, - int totvert, - int totpoly, - int totloop) -{ - mesh_vert_poly_or_loop_map_create(r_map, r_mem, mpoly, mloop, totvert, totpoly, totloop, false); -} - -void BKE_mesh_vert_loop_map_create(MeshElemMap **r_map, - int **r_mem, - const MPoly *mpoly, - const MLoop *mloop, - int totvert, - int totpoly, - int totloop) -{ - mesh_vert_poly_or_loop_map_create(r_map, r_mem, mpoly, mloop, totvert, totpoly, totloop, true); -} - -void BKE_mesh_vert_looptri_map_create(MeshElemMap **r_map, - int **r_mem, - const MVert *UNUSED(mvert), - const int totvert, - const MLoopTri *mlooptri, - const int totlooptri, - 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__); - int *index_step; - const MLoopTri *mlt; - int i; - - /* count face users */ - for (i = 0, mlt = mlooptri; i < totlooptri; mlt++, i++) { - for (int j = 3; j--;) { - map[mloop[mlt->tri[j]].v].count++; - } - } - - /* create offsets */ - index_step = indices; - for (i = 0; i < totvert; i++) { - map[i].indices = index_step; - index_step += map[i].count; - - /* re-count, using this as an index below */ - map[i].count = 0; - } - - /* assign looptri-edge users */ - for (i = 0, mlt = mlooptri; i < totlooptri; mlt++, i++) { - for (int j = 3; j--;) { - MeshElemMap *map_ele = &map[mloop[mlt->tri[j]].v]; - map_ele->indices[map_ele->count++] = i; - } - } - - *r_map = map; - *r_mem = indices; -} - -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"); - int *i_pt = indices; - - int i; - - /* Count number of edges for each vertex */ - for (i = 0; i < totedge; i++) { - map[medge[i].v1].count++; - map[medge[i].v2].count++; - } - - /* Assign indices mem */ - for (i = 0; i < totvert; i++) { - map[i].indices = i_pt; - i_pt += map[i].count; - - /* Reset 'count' for use as index in last loop */ - map[i].count = 0; - } - - /* Find the users */ - for (i = 0; i < totedge; i++) { - const uint v[2] = {medge[i].v1, medge[i].v2}; - - map[v[0]].indices[map[v[0]].count] = i; - map[v[1]].indices[map[v[1]].count] = i; - - map[v[0]].count++; - map[v[1]].count++; - } - - *r_map = map; - *r_mem = indices; -} - -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"); - int *i_pt = indices; - - int i; - - /* Count number of edges for each vertex */ - for (i = 0; i < totedge; i++) { - map[medge[i].v1].count++; - map[medge[i].v2].count++; - } - - /* Assign indices mem */ - for (i = 0; i < totvert; i++) { - map[i].indices = i_pt; - i_pt += map[i].count; - - /* Reset 'count' for use as index in last loop */ - map[i].count = 0; - } - - /* Find the users */ - for (i = 0; i < totedge; i++) { - const uint v[2] = {medge[i].v1, medge[i].v2}; - - map[v[0]].indices[map[v[0]].count] = (int)v[1]; - map[v[1]].indices[map[v[1]].count] = (int)v[0]; - - map[v[0]].count++; - map[v[1]].count++; - } - - *r_map = map; - *r_mem = indices; -} - -void BKE_mesh_edge_loop_map_create(MeshElemMap **r_map, - int **r_mem, - const MEdge *UNUSED(medge), - const int totedge, - const MPoly *mpoly, - const int totpoly, - 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"); - int *index_step; - const MPoly *mp; - int i; - - /* count face users */ - for (i = 0, mp = mpoly; i < totpoly; mp++, i++) { - const MLoop *ml; - int j = mp->totloop; - for (ml = &mloop[mp->loopstart]; j--; ml++) { - map[ml->e].count += 2; - } - } - - /* create offsets */ - index_step = indices; - for (i = 0; i < totedge; i++) { - map[i].indices = index_step; - index_step += map[i].count; - - /* re-count, using this as an index below */ - map[i].count = 0; - } - - /* assign loop-edge users */ - for (i = 0, mp = mpoly; i < totpoly; mp++, i++) { - const MLoop *ml; - MeshElemMap *map_ele; - const int max_loop = mp->loopstart + mp->totloop; - int j = mp->loopstart; - for (ml = &mloop[j]; j < max_loop; j++, ml++) { - map_ele = &map[ml->e]; - map_ele->indices[map_ele->count++] = j; - map_ele->indices[map_ele->count++] = j + 1; - } - /* last edge/loop of poly, must point back to first loop! */ - map_ele->indices[map_ele->count - 1] = mp->loopstart; - } - - *r_map = map; - *r_mem = indices; -} - -void BKE_mesh_edge_poly_map_create(MeshElemMap **r_map, - int **r_mem, - const MEdge *UNUSED(medge), - const int totedge, - const MPoly *mpoly, - const int totpoly, - 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"); - int *index_step; - const MPoly *mp; - int i; - - /* count face users */ - for (i = 0, mp = mpoly; i < totpoly; mp++, i++) { - const MLoop *ml; - int j = mp->totloop; - for (ml = &mloop[mp->loopstart]; j--; ml++) { - map[ml->e].count++; - } - } - - /* create offsets */ - index_step = indices; - for (i = 0; i < totedge; i++) { - map[i].indices = index_step; - index_step += map[i].count; - - /* re-count, using this as an index below */ - map[i].count = 0; - } - - /* assign poly-edge users */ - for (i = 0, mp = mpoly; i < totpoly; mp++, i++) { - const MLoop *ml; - int j = mp->totloop; - for (ml = &mloop[mp->loopstart]; j--; ml++) { - MeshElemMap *map_ele = &map[ml->e]; - map_ele->indices[map_ele->count++] = i; - } - } - - *r_map = map; - *r_mem = indices; -} - -void BKE_mesh_origindex_map_create(MeshElemMap **r_map, - int **r_mem, - const int totsource, - 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"); - int *index_step; - int i; - - /* count face users */ - for (i = 0; i < totfinal; i++) { - if (final_origindex[i] != ORIGINDEX_NONE) { - BLI_assert(final_origindex[i] < totsource); - map[final_origindex[i]].count++; - } - } - - /* create offsets */ - index_step = indices; - for (i = 0; i < totsource; i++) { - map[i].indices = index_step; - index_step += map[i].count; - - /* re-count, using this as an index below */ - map[i].count = 0; - } - - /* assign poly-tessface users */ - for (i = 0; i < totfinal; i++) { - if (final_origindex[i] != ORIGINDEX_NONE) { - MeshElemMap *map_ele = &map[final_origindex[i]]; - map_ele->indices[map_ele->count++] = i; - } - } - - *r_map = map; - *r_mem = indices; -} - -void BKE_mesh_origindex_map_create_looptri(MeshElemMap **r_map, - int **r_mem, - const MPoly *mpoly, - const int mpoly_num, - 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"); - int *index_step; - int i; - - /* create offsets */ - index_step = indices; - for (i = 0; i < mpoly_num; i++) { - map[i].indices = index_step; - index_step += ME_POLY_TRI_TOT(&mpoly[i]); - } - - /* assign poly-tessface users */ - for (i = 0; i < looptri_num; i++) { - MeshElemMap *map_ele = &map[looptri[i].poly]; - map_ele->indices[map_ele->count++] = i; - } - - *r_map = map; - *r_mem = indices; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Mesh loops/poly islands. - * Used currently for UVs and 'smooth groups'. - * \{ */ - -/** - * 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); - -static void poly_edge_loop_islands_calc(const MEdge *medge, - const int totedge, - const MPoly *mpoly, - const int totpoly, - const MLoop *mloop, - const int totloop, - MeshElemMap *edge_poly_map, - const bool use_bitflags, - MeshRemap_CheckIslandBoundary edge_boundary_check, - void *edge_boundary_check_data, - int **r_poly_groups, - int *r_totgroup, - BLI_bitmap **r_edge_borders, - int *r_totedgeborder) -{ - int *poly_groups; - int *poly_stack; - - BLI_bitmap *edge_borders = NULL; - int num_edgeborders = 0; - - int poly_prev = 0; - const int temp_poly_group_id = 3; /* Placeholder value. */ - - /* Group we could not find any available bit, will be reset to 0 at end. */ - const int poly_group_id_overflowed = 5; - - int tot_group = 0; - bool group_id_overflow = false; - - /* map vars */ - int *edge_poly_mem = NULL; - - if (totpoly == 0) { - *r_totgroup = 0; - *r_poly_groups = NULL; - if (r_edge_borders) { - *r_edge_borders = NULL; - *r_totedgeborder = 0; - } - return; - } - - if (r_edge_borders) { - edge_borders = BLI_BITMAP_NEW(totedge, __func__); - *r_totedgeborder = 0; - } - - if (!edge_poly_map) { - BKE_mesh_edge_poly_map_create( - &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__); - - while (true) { - int poly; - int bit_poly_group_mask = 0; - int poly_group_id; - int ps_curr_idx = 0, ps_end_idx = 0; /* stack indices */ - - for (poly = poly_prev; poly < totpoly; poly++) { - if (poly_groups[poly] == 0) { - break; - } - } - - if (poly == totpoly) { - /* all done */ - break; - } - - poly_group_id = use_bitflags ? temp_poly_group_id : ++tot_group; - - /* start searching from here next time */ - poly_prev = poly + 1; - - poly_groups[poly] = poly_group_id; - poly_stack[ps_end_idx++] = poly; - - while (ps_curr_idx != ps_end_idx) { - const MPoly *mp; - const MLoop *ml; - int j; - - poly = poly_stack[ps_curr_idx++]; - BLI_assert(poly_groups[poly] == poly_group_id); - - mp = &mpoly[poly]; - for (ml = &mloop[mp->loopstart], j = mp->totloop; j--; ml++) { - /* loop over poly users */ - const int me_idx = (int)ml->e; - const MEdge *me = &medge[me_idx]; - const MeshElemMap *map_ele = &edge_poly_map[me_idx]; - const int *p = map_ele->indices; - int i = map_ele->count; - if (!edge_boundary_check(mp, ml, me, i, mpoly, map_ele, edge_boundary_check_data)) { - for (; i--; p++) { - /* if we meet other non initialized its a bug */ - BLI_assert(ELEM(poly_groups[*p], 0, poly_group_id)); - - if (poly_groups[*p] == 0) { - poly_groups[*p] = poly_group_id; - poly_stack[ps_end_idx++] = *p; - } - } - } - else { - if (edge_borders && !BLI_BITMAP_TEST(edge_borders, me_idx)) { - BLI_BITMAP_ENABLE(edge_borders, me_idx); - num_edgeborders++; - } - if (use_bitflags) { - /* Find contiguous smooth groups already assigned, - * these are the values we can't reuse! */ - for (; i--; p++) { - int bit = poly_groups[*p]; - if (!ELEM(bit, 0, poly_group_id, poly_group_id_overflowed) && - !(bit_poly_group_mask & bit)) { - bit_poly_group_mask |= bit; - } - } - } - } - } - } - /* And now, we have all our poly from current group in poly_stack - * (from 0 to (ps_end_idx - 1)), - * as well as all smoothgroups bits we can't use in bit_poly_group_mask. - */ - if (use_bitflags) { - int i, *p, gid_bit = 0; - poly_group_id = 1; - - /* Find first bit available! */ - for (; (poly_group_id & bit_poly_group_mask) && (gid_bit < 32); gid_bit++) { - poly_group_id <<= 1; /* will 'overflow' on last possible iteration. */ - } - if (UNLIKELY(gid_bit > 31)) { - /* All bits used in contiguous smooth groups, we can't do much! - * NOTE: this is *very* unlikely - theoretically, four groups are enough, - * I don't think we can reach this goal with such a simple algorithm, - * but I don't think either we'll never need all 32 groups! - */ - printf( - "Warning, could not find an available id for current smooth group, faces will me " - "marked " - "as out of any smooth group...\n"); - - /* Can't use 0, will have to set them to this value later. */ - poly_group_id = poly_group_id_overflowed; - - group_id_overflow = true; - } - if (gid_bit > tot_group) { - tot_group = gid_bit; - } - /* And assign the final smooth group id to that poly group! */ - for (i = ps_end_idx, p = poly_stack; i--; p++) { - poly_groups[*p] = poly_group_id; - } - } - } - - if (use_bitflags) { - /* used bits are zero-based. */ - tot_group++; - } - - if (UNLIKELY(group_id_overflow)) { - int i = totpoly, *gid = poly_groups; - for (; i--; gid++) { - if (*gid == poly_group_id_overflowed) { - *gid = 0; - } - } - /* Using 0 as group id adds one more group! */ - tot_group++; - } - - if (edge_poly_mem) { - MEM_freeN(edge_poly_map); - MEM_freeN(edge_poly_mem); - } - MEM_freeN(poly_stack); - - *r_totgroup = tot_group; - *r_poly_groups = poly_groups; - if (r_edge_borders) { - *r_edge_borders = edge_borders; - *r_totedgeborder = num_edgeborders; - } -} - -static bool poly_is_island_boundary_smooth_cb(const MPoly *mp, - const MLoop *UNUSED(ml), - const MEdge *me, - const int edge_user_count, - const MPoly *mpoly_array, - const MeshElemMap *edge_poly_map, - void *UNUSED(user_data)) -{ - /* Edge is sharp if one of its polys is flat, or edge itself is sharp, - * or edge is not used by exactly two polygons. */ - if ((mp->flag & ME_SMOOTH) && !(me->flag & ME_SHARP) && (edge_user_count == 2)) { - /* In that case, edge appears to be smooth, but we need to check its other poly too. */ - const MPoly *mp_other = (mp == &mpoly_array[edge_poly_map->indices[0]]) ? - &mpoly_array[edge_poly_map->indices[1]] : - &mpoly_array[edge_poly_map->indices[0]]; - return (mp_other->flag & ME_SMOOTH) == 0; - } - return true; -} - -int *BKE_mesh_calc_smoothgroups(const MEdge *medge, - const int totedge, - const MPoly *mpoly, - const int totpoly, - const MLoop *mloop, - const int totloop, - int *r_totgroup, - const bool use_bitflags) -{ - int *poly_groups = NULL; - - poly_edge_loop_islands_calc(medge, - totedge, - mpoly, - totpoly, - mloop, - totloop, - NULL, - use_bitflags, - poly_is_island_boundary_smooth_cb, - NULL, - &poly_groups, - r_totgroup, - NULL, - NULL); - - return poly_groups; -} - -#define MISLAND_DEFAULT_BUFSIZE 64 - -void BKE_mesh_loop_islands_init(MeshIslandStore *island_store, - const short item_type, - const int items_num, - const short island_type, - const short innercut_type) -{ - MemArena *mem = island_store->mem; - - if (mem == NULL) { - mem = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); - island_store->mem = mem; - } - /* else memarena should be cleared */ - - BLI_assert( - ELEM(item_type, MISLAND_TYPE_VERT, MISLAND_TYPE_EDGE, MISLAND_TYPE_POLY, MISLAND_TYPE_LOOP)); - BLI_assert(ELEM( - island_type, MISLAND_TYPE_VERT, MISLAND_TYPE_EDGE, MISLAND_TYPE_POLY, MISLAND_TYPE_LOOP)); - - 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->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->innercut_type = innercut_type; - island_store->innercuts = BLI_memarena_alloc( - mem, sizeof(*island_store->innercuts) * island_store->islands_num_alloc); -} - -void BKE_mesh_loop_islands_clear(MeshIslandStore *island_store) -{ - island_store->item_type = MISLAND_TYPE_NONE; - island_store->items_to_islands_num = 0; - island_store->items_to_islands = NULL; - - island_store->island_type = MISLAND_TYPE_NONE; - island_store->islands_num = 0; - island_store->islands = NULL; - - island_store->innercut_type = MISLAND_TYPE_NONE; - island_store->innercuts = NULL; - - if (island_store->mem) { - BLI_memarena_clear(island_store->mem); - } - - island_store->islands_num_alloc = 0; -} - -void BKE_mesh_loop_islands_free(MeshIslandStore *island_store) -{ - if (island_store->mem) { - BLI_memarena_free(island_store->mem); - island_store->mem = NULL; - } -} - -void BKE_mesh_loop_islands_add(MeshIslandStore *island_store, - const int item_num, - const int *items_indices, - const int num_island_items, - int *island_item_indices, - const int num_innercut_items, - int *innercut_item_indices) -{ - MemArena *mem = island_store->mem; - - MeshElemMap *isld, *innrcut; - const int curr_island_idx = island_store->islands_num++; - const size_t curr_num_islands = (size_t)island_store->islands_num; - int i = item_num; - - while (i--) { - island_store->items_to_islands[items_indices[i]] = curr_island_idx; - } - - if (UNLIKELY(curr_num_islands > island_store->islands_num_alloc)) { - MeshElemMap **islds, **innrcuts; - - island_store->islands_num_alloc *= 2; - islds = 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); - 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)); - isld->count = num_island_items; - isld->indices = 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)); - innrcut->count = num_innercut_items; - innrcut->indices = 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); -} - -/* TODO: I'm not sure edge seam flag is enough to define UV islands? - * Maybe we should also consider UV-maps values - * themselves (i.e. different UV-edges for a same mesh-edge => boundary edge too?). - * 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 { - 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, - const MEdge *me, - const int UNUSED(edge_user_count), - const MPoly *UNUSED(mpoly_array), - const MeshElemMap *UNUSED(edge_poly_map), - void *user_data) -{ - if (user_data) { - const MeshCheckIslandBoundaryUv *data = user_data; - const MLoop *loops = data->loops; - const MLoopUV *luvs = data->luvs; - const MeshElemMap *edge_to_loops = &data->edge_loop_map[ml->e]; - - BLI_assert(edge_to_loops->count >= 2 && (edge_to_loops->count % 2) == 0); - - const uint v1 = loops[edge_to_loops->indices[0]].v; - const uint v2 = loops[edge_to_loops->indices[1]].v; - const float *uvco_v1 = luvs[edge_to_loops->indices[0]].uv; - const float *uvco_v2 = luvs[edge_to_loops->indices[1]].uv; - for (int i = 2; i < edge_to_loops->count; i += 2) { - if (loops[edge_to_loops->indices[i]].v == v1) { - if (!equals_v2v2(uvco_v1, luvs[edge_to_loops->indices[i]].uv) || - !equals_v2v2(uvco_v2, luvs[edge_to_loops->indices[i + 1]].uv)) { - return true; - } - } - else { - BLI_assert(loops[edge_to_loops->indices[i]].v == v2); - UNUSED_VARS_NDEBUG(v2); - if (!equals_v2v2(uvco_v2, luvs[edge_to_loops->indices[i]].uv) || - !equals_v2v2(uvco_v1, luvs[edge_to_loops->indices[i + 1]].uv)) { - return true; - } - } - } - return false; - } - - /* Edge is UV boundary if tagged as seam. */ - return (me->flag & ME_SEAM) != 0; -} - -static bool mesh_calc_islands_loop_poly_uv(MVert *UNUSED(verts), - const int UNUSED(totvert), - MEdge *edges, - const int totedge, - MPoly *polys, - const int totpoly, - MLoop *loops, - const int totloop, - const MLoopUV *luvs, - MeshIslandStore *r_island_store) -{ - int *poly_groups = NULL; - int num_poly_groups; - - /* map vars */ - MeshElemMap *edge_poly_map; - int *edge_poly_mem; - - MeshElemMap *edge_loop_map; - int *edge_loop_mem; - - MeshCheckIslandBoundaryUv edge_boundary_check_data; - - int *poly_indices; - int *loop_indices; - int num_pidx, num_lidx; - - /* Those are used to detect 'inner cuts', i.e. edges that are borders, - * and yet have two or more polys of a same group using them - * (typical case: seam used to unwrap properly a cylinder). */ - BLI_bitmap *edge_borders = NULL; - int num_edge_borders = 0; - char *edge_border_count = NULL; - int *edge_innercut_indices = NULL; - int num_einnercuts = 0; - - int grp_idx, p_idx, pl_idx, l_idx; - - BKE_mesh_loop_islands_clear(r_island_store); - BKE_mesh_loop_islands_init( - r_island_store, MISLAND_TYPE_LOOP, totloop, MISLAND_TYPE_POLY, MISLAND_TYPE_EDGE); - - BKE_mesh_edge_poly_map_create( - &edge_poly_map, &edge_poly_mem, edges, totedge, polys, totpoly, loops, totloop); - - if (luvs) { - BKE_mesh_edge_loop_map_create( - &edge_loop_map, &edge_loop_mem, edges, totedge, polys, totpoly, loops, totloop); - edge_boundary_check_data.loops = loops; - edge_boundary_check_data.luvs = luvs; - edge_boundary_check_data.edge_loop_map = edge_loop_map; - } - - poly_edge_loop_islands_calc(edges, - totedge, - polys, - totpoly, - loops, - totloop, - edge_poly_map, - false, - mesh_check_island_boundary_uv, - luvs ? &edge_boundary_check_data : NULL, - &poly_groups, - &num_poly_groups, - &edge_borders, - &num_edge_borders); - - if (!num_poly_groups) { - /* Should never happen... */ - MEM_freeN(edge_poly_map); - MEM_freeN(edge_poly_mem); - - if (edge_borders) { - MEM_freeN(edge_borders); - } - return false; - } - - 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__); - } - - poly_indices = MEM_mallocN(sizeof(*poly_indices) * (size_t)totpoly, __func__); - loop_indices = 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++) { - num_pidx = num_lidx = 0; - if (num_edge_borders) { - num_einnercuts = 0; - memset(edge_border_count, 0, sizeof(*edge_border_count) * (size_t)totedge); - } - - for (p_idx = 0; p_idx < totpoly; p_idx++) { - MPoly *mp; - - if (poly_groups[p_idx] != grp_idx) { - continue; - } - - 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]; - loop_indices[num_lidx++] = l_idx; - if (num_edge_borders && BLI_BITMAP_TEST(edge_borders, ml->e) && - (edge_border_count[ml->e] < 2)) { - edge_border_count[ml->e]++; - if (edge_border_count[ml->e] == 2) { - edge_innercut_indices[num_einnercuts++] = (int)ml->e; - } - } - } - } - - BKE_mesh_loop_islands_add(r_island_store, - num_lidx, - loop_indices, - num_pidx, - poly_indices, - num_einnercuts, - edge_innercut_indices); - } - - MEM_freeN(edge_poly_map); - MEM_freeN(edge_poly_mem); - - if (luvs) { - MEM_freeN(edge_loop_map); - MEM_freeN(edge_loop_mem); - } - - MEM_freeN(poly_indices); - MEM_freeN(loop_indices); - MEM_freeN(poly_groups); - - if (edge_borders) { - MEM_freeN(edge_borders); - } - - if (num_edge_borders) { - MEM_freeN(edge_border_count); - MEM_freeN(edge_innercut_indices); - } - return true; -} - -bool BKE_mesh_calc_islands_loop_poly_edgeseam(MVert *verts, - const int totvert, - MEdge *edges, - const int totedge, - MPoly *polys, - const int totpoly, - MLoop *loops, - const int totloop, - MeshIslandStore *r_island_store) -{ - return mesh_calc_islands_loop_poly_uv( - verts, totvert, edges, totedge, polys, totpoly, loops, totloop, NULL, r_island_store); -} - -bool BKE_mesh_calc_islands_loop_poly_uvmap(MVert *verts, - const int totvert, - MEdge *edges, - const int totedge, - MPoly *polys, - const int totpoly, - MLoop *loops, - const int totloop, - const MLoopUV *luvs, - MeshIslandStore *r_island_store) -{ - BLI_assert(luvs != NULL); - return mesh_calc_islands_loop_poly_uv( - verts, totvert, edges, totedge, polys, totpoly, loops, totloop, luvs, r_island_store); -} - -/** \} */ diff --git a/source/blender/blenkernel/intern/mesh_mapping.cc b/source/blender/blenkernel/intern/mesh_mapping.cc new file mode 100644 index 00000000000..db091361223 --- /dev/null +++ b/source/blender/blenkernel/intern/mesh_mapping.cc @@ -0,0 +1,1167 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup bke + * + * Functions for accessing mesh connectivity data. + * eg: polys connected to verts, UV's connected to verts. + */ + +#include "MEM_guardedalloc.h" + +#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" +#include "BLI_utildefines.h" + +#include "BKE_customdata.h" +#include "BKE_mesh_mapping.h" +#include "BLI_memarena.h" + +#include "BLI_strict_flags.h" + +/* -------------------------------------------------------------------- */ +/** \name Mesh Connectivity Mapping + * \{ */ + +/* 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, + uint totvert, + const float limit[2], + const bool selected, + const bool use_winding) +{ + UvVertMap *vmap; + UvMapVert *buf; + const MPoly *mp; + uint a; + int i, totuv, nverts; + + bool *winding = NULL; + BLI_buffer_declare_static(vec2f, tf_uv_buf, BLI_BUFFER_NOP, 32); + + totuv = 0; + + /* generate UvMapVert array */ + mp = mpoly; + for (a = 0; a < totpoly; a++, mp++) { + if (!selected || (!(hide_poly && hide_poly[a]) && (mp->flag & ME_FACE_SEL))) { + totuv += mp->totloop; + } + } + + if (totuv == 0) { + return NULL; + } + + vmap = (UvVertMap *)MEM_callocN(sizeof(*vmap), "UvVertMap"); + 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 = static_cast(MEM_callocN(sizeof(*winding) * totpoly, "winding")); + } + + if (!vmap->vert || !vmap->buf) { + BKE_mesh_uv_vert_map_free(vmap); + return NULL; + } + + mp = mpoly; + for (a = 0; a < totpoly; a++, mp++) { + if (!selected || (!(hide_poly && hide_poly[a]) && (mp->flag & ME_FACE_SEL))) { + float(*tf_uv)[2] = NULL; + + if (use_winding) { + tf_uv = (float(*)[2])BLI_buffer_reinit_data(&tf_uv_buf, vec2f, (size_t)mp->totloop); + } + + nverts = mp->totloop; + + for (i = 0; i < nverts; i++) { + buf->loop_of_poly_index = (unsigned short)i; + buf->poly_index = a; + buf->separate = 0; + buf->next = vmap->vert[mloop[mp->loopstart + i].v]; + vmap->vert[mloop[mp->loopstart + i].v] = buf; + + if (use_winding) { + copy_v2_v2(tf_uv[i], mloopuv[mpoly[a].loopstart + i].uv); + } + + buf++; + } + + if (use_winding) { + winding[a] = cross_poly_v2(tf_uv, (uint)nverts) > 0; + } + } + } + + /* sort individual uvs for each vert */ + for (a = 0; a < totvert; a++) { + UvMapVert *newvlist = NULL, *vlist = vmap->vert[a]; + UvMapVert *iterv, *v, *lastv, *next; + const float *uv, *uv2; + float uvdiff[2]; + + while (vlist) { + v = vlist; + vlist = vlist->next; + v->next = newvlist; + newvlist = v; + + uv = mloopuv[mpoly[v->poly_index].loopstart + v->loop_of_poly_index].uv; + lastv = NULL; + iterv = vlist; + + while (iterv) { + next = iterv->next; + + uv2 = mloopuv[mpoly[iterv->poly_index].loopstart + iterv->loop_of_poly_index].uv; + sub_v2_v2v2(uvdiff, uv2, uv); + + if (fabsf(uv[0] - uv2[0]) < limit[0] && fabsf(uv[1] - uv2[1]) < limit[1] && + (!use_winding || winding[iterv->poly_index] == winding[v->poly_index])) { + if (lastv) { + lastv->next = next; + } + else { + vlist = next; + } + iterv->next = newvlist; + newvlist = iterv; + } + else { + lastv = iterv; + } + + iterv = next; + } + + newvlist->separate = 1; + } + + vmap->vert[a] = newvlist; + } + + if (use_winding) { + MEM_freeN(winding); + } + + BLI_buffer_free(&tf_uv_buf); + + return vmap; +} + +UvMapVert *BKE_mesh_uv_vert_map_get_vert(UvVertMap *vmap, uint v) +{ + return vmap->vert[v]; +} + +void BKE_mesh_uv_vert_map_free(UvVertMap *vmap) +{ + if (vmap) { + if (vmap->vert) { + MEM_freeN(vmap->vert); + } + if (vmap->buf) { + MEM_freeN(vmap->buf); + } + MEM_freeN(vmap); + } +} + +/** + * Generates a map where the key is the vertex and the value is a list + * of polys or loops that use that vertex as a corner. The lists are allocated + * from one memory pool. + * + * Wrapped by #BKE_mesh_vert_poly_map_create & BKE_mesh_vert_loop_map_create + */ +static void mesh_vert_poly_or_loop_map_create(MeshElemMap **r_map, + int **r_mem, + const MPoly *mpoly, + const MLoop *mloop, + int totvert, + int totpoly, + int totloop, + const bool do_loops) +{ + MeshElemMap *map = MEM_cnew_array((size_t)totvert, __func__); + int *indices, *index_iter; + int i, j; + + indices = static_cast(MEM_mallocN(sizeof(int) * (size_t)totloop, __func__)); + index_iter = indices; + + /* Count number of polys for each vertex */ + for (i = 0; i < totpoly; i++) { + const MPoly *p = &mpoly[i]; + + for (j = 0; j < p->totloop; j++) { + map[mloop[p->loopstart + j].v].count++; + } + } + + /* Assign indices mem */ + for (i = 0; i < totvert; i++) { + map[i].indices = index_iter; + index_iter += map[i].count; + + /* Reset 'count' for use as index in last loop */ + map[i].count = 0; + } + + /* Find the users */ + for (i = 0; i < totpoly; i++) { + const MPoly *p = &mpoly[i]; + + for (j = 0; j < p->totloop; j++) { + uint v = mloop[p->loopstart + j].v; + + map[v].indices[map[v].count] = do_loops ? p->loopstart + j : i; + map[v].count++; + } + } + + *r_map = map; + *r_mem = indices; +} + +void BKE_mesh_vert_poly_map_create(MeshElemMap **r_map, + int **r_mem, + const MPoly *mpoly, + const MLoop *mloop, + int totvert, + int totpoly, + int totloop) +{ + mesh_vert_poly_or_loop_map_create(r_map, r_mem, mpoly, mloop, totvert, totpoly, totloop, false); +} + +void BKE_mesh_vert_loop_map_create(MeshElemMap **r_map, + int **r_mem, + const MPoly *mpoly, + const MLoop *mloop, + int totvert, + int totpoly, + int totloop) +{ + mesh_vert_poly_or_loop_map_create(r_map, r_mem, mpoly, mloop, totvert, totpoly, totloop, true); +} + +void BKE_mesh_vert_looptri_map_create(MeshElemMap **r_map, + int **r_mem, + const MVert *UNUSED(mvert), + const int totvert, + const MLoopTri *mlooptri, + const int totlooptri, + const MLoop *mloop, + const int UNUSED(totloop)) +{ + MeshElemMap *map = MEM_cnew_array((size_t)totvert, __func__); + int *indices = static_cast(MEM_mallocN(sizeof(int) * (size_t)totlooptri * 3, __func__)); + int *index_step; + const MLoopTri *mlt; + int i; + + /* count face users */ + for (i = 0, mlt = mlooptri; i < totlooptri; mlt++, i++) { + for (int j = 3; j--;) { + map[mloop[mlt->tri[j]].v].count++; + } + } + + /* create offsets */ + index_step = indices; + for (i = 0; i < totvert; i++) { + map[i].indices = index_step; + index_step += map[i].count; + + /* re-count, using this as an index below */ + map[i].count = 0; + } + + /* assign looptri-edge users */ + for (i = 0, mlt = mlooptri; i < totlooptri; mlt++, i++) { + for (int j = 3; j--;) { + MeshElemMap *map_ele = &map[mloop[mlt->tri[j]].v]; + map_ele->indices[map_ele->count++] = i; + } + } + + *r_map = map; + *r_mem = indices; +} + +void BKE_mesh_vert_edge_map_create( + MeshElemMap **r_map, int **r_mem, const MEdge *medge, int totvert, int totedge) +{ + MeshElemMap *map = MEM_cnew_array((size_t)totvert, __func__); + int *indices = static_cast(MEM_mallocN(sizeof(int[2]) * (size_t)totedge, __func__)); + int *i_pt = indices; + + int i; + + /* Count number of edges for each vertex */ + for (i = 0; i < totedge; i++) { + map[medge[i].v1].count++; + map[medge[i].v2].count++; + } + + /* Assign indices mem */ + for (i = 0; i < totvert; i++) { + map[i].indices = i_pt; + i_pt += map[i].count; + + /* Reset 'count' for use as index in last loop */ + map[i].count = 0; + } + + /* Find the users */ + for (i = 0; i < totedge; i++) { + const uint v[2] = {medge[i].v1, medge[i].v2}; + + map[v[0]].indices[map[v[0]].count] = i; + map[v[1]].indices[map[v[1]].count] = i; + + map[v[0]].count++; + map[v[1]].count++; + } + + *r_map = map; + *r_mem = indices; +} + +void BKE_mesh_vert_edge_vert_map_create( + MeshElemMap **r_map, int **r_mem, const MEdge *medge, int totvert, int totedge) +{ + MeshElemMap *map = MEM_cnew_array((size_t)totvert, __func__); + int *indices = static_cast(MEM_mallocN(sizeof(int[2]) * (size_t)totedge, __func__)); + int *i_pt = indices; + + int i; + + /* Count number of edges for each vertex */ + for (i = 0; i < totedge; i++) { + map[medge[i].v1].count++; + map[medge[i].v2].count++; + } + + /* Assign indices mem */ + for (i = 0; i < totvert; i++) { + map[i].indices = i_pt; + i_pt += map[i].count; + + /* Reset 'count' for use as index in last loop */ + map[i].count = 0; + } + + /* Find the users */ + for (i = 0; i < totedge; i++) { + const uint v[2] = {medge[i].v1, medge[i].v2}; + + map[v[0]].indices[map[v[0]].count] = (int)v[1]; + map[v[1]].indices[map[v[1]].count] = (int)v[0]; + + map[v[0]].count++; + map[v[1]].count++; + } + + *r_map = map; + *r_mem = indices; +} + +void BKE_mesh_edge_loop_map_create(MeshElemMap **r_map, + int **r_mem, + const MEdge *UNUSED(medge), + const int totedge, + const MPoly *mpoly, + const int totpoly, + const MLoop *mloop, + const int totloop) +{ + MeshElemMap *map = MEM_cnew_array((size_t)totedge, __func__); + int *indices = static_cast(MEM_mallocN(sizeof(int) * (size_t)totloop * 2, __func__)); + int *index_step; + const MPoly *mp; + int i; + + /* count face users */ + for (i = 0, mp = mpoly; i < totpoly; mp++, i++) { + const MLoop *ml; + int j = mp->totloop; + for (ml = &mloop[mp->loopstart]; j--; ml++) { + map[ml->e].count += 2; + } + } + + /* create offsets */ + index_step = indices; + for (i = 0; i < totedge; i++) { + map[i].indices = index_step; + index_step += map[i].count; + + /* re-count, using this as an index below */ + map[i].count = 0; + } + + /* assign loop-edge users */ + for (i = 0, mp = mpoly; i < totpoly; mp++, i++) { + const MLoop *ml; + MeshElemMap *map_ele; + const int max_loop = mp->loopstart + mp->totloop; + int j = mp->loopstart; + for (ml = &mloop[j]; j < max_loop; j++, ml++) { + map_ele = &map[ml->e]; + map_ele->indices[map_ele->count++] = j; + map_ele->indices[map_ele->count++] = j + 1; + } + /* last edge/loop of poly, must point back to first loop! */ + map_ele->indices[map_ele->count - 1] = mp->loopstart; + } + + *r_map = map; + *r_mem = indices; +} + +void BKE_mesh_edge_poly_map_create(MeshElemMap **r_map, + int **r_mem, + const MEdge *UNUSED(medge), + const int totedge, + const MPoly *mpoly, + const int totpoly, + const MLoop *mloop, + const int totloop) +{ + MeshElemMap *map = MEM_cnew_array((size_t)totedge, __func__); + int *indices = static_cast(MEM_mallocN(sizeof(int) * (size_t)totloop, __func__)); + int *index_step; + const MPoly *mp; + int i; + + /* count face users */ + for (i = 0, mp = mpoly; i < totpoly; mp++, i++) { + const MLoop *ml; + int j = mp->totloop; + for (ml = &mloop[mp->loopstart]; j--; ml++) { + map[ml->e].count++; + } + } + + /* create offsets */ + index_step = indices; + for (i = 0; i < totedge; i++) { + map[i].indices = index_step; + index_step += map[i].count; + + /* re-count, using this as an index below */ + map[i].count = 0; + } + + /* assign poly-edge users */ + for (i = 0, mp = mpoly; i < totpoly; mp++, i++) { + const MLoop *ml; + int j = mp->totloop; + for (ml = &mloop[mp->loopstart]; j--; ml++) { + MeshElemMap *map_ele = &map[ml->e]; + map_ele->indices[map_ele->count++] = i; + } + } + + *r_map = map; + *r_mem = indices; +} + +void BKE_mesh_origindex_map_create(MeshElemMap **r_map, + int **r_mem, + const int totsource, + const int *final_origindex, + const int totfinal) +{ + MeshElemMap *map = MEM_cnew_array((size_t)totsource, __func__); + int *indices = static_cast(MEM_mallocN(sizeof(int) * (size_t)totfinal, __func__)); + int *index_step; + int i; + + /* count face users */ + for (i = 0; i < totfinal; i++) { + if (final_origindex[i] != ORIGINDEX_NONE) { + BLI_assert(final_origindex[i] < totsource); + map[final_origindex[i]].count++; + } + } + + /* create offsets */ + index_step = indices; + for (i = 0; i < totsource; i++) { + map[i].indices = index_step; + index_step += map[i].count; + + /* re-count, using this as an index below */ + map[i].count = 0; + } + + /* assign poly-tessface users */ + for (i = 0; i < totfinal; i++) { + if (final_origindex[i] != ORIGINDEX_NONE) { + MeshElemMap *map_ele = &map[final_origindex[i]]; + map_ele->indices[map_ele->count++] = i; + } + } + + *r_map = map; + *r_mem = indices; +} + +void BKE_mesh_origindex_map_create_looptri(MeshElemMap **r_map, + int **r_mem, + const MPoly *mpoly, + const int mpoly_num, + const MLoopTri *looptri, + const int looptri_num) +{ + MeshElemMap *map = MEM_cnew_array((size_t)mpoly_num, __func__); + int *indices = static_cast(MEM_mallocN(sizeof(int) * (size_t)looptri_num, __func__)); + int *index_step; + int i; + + /* create offsets */ + index_step = indices; + for (i = 0; i < mpoly_num; i++) { + map[i].indices = index_step; + index_step += ME_POLY_TRI_TOT(&mpoly[i]); + } + + /* assign poly-tessface users */ + for (i = 0; i < looptri_num; i++) { + MeshElemMap *map_ele = &map[looptri[i].poly]; + map_ele->indices[map_ele->count++] = i; + } + + *r_map = map; + *r_mem = indices; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Mesh loops/poly islands. + * Used currently for UVs and 'smooth groups'. + * \{ */ + +/** + * Callback deciding whether the given poly/loop/edge define an island boundary or not. + */ +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, + const MPoly *mpoly, + const int totpoly, + const MLoop *mloop, + const int totloop, + MeshElemMap *edge_poly_map, + const bool use_bitflags, + MeshRemap_CheckIslandBoundary edge_boundary_check, + void *edge_boundary_check_data, + int **r_poly_groups, + int *r_totgroup, + BLI_bitmap **r_edge_borders, + int *r_totedgeborder) +{ + int *poly_groups; + int *poly_stack; + + BLI_bitmap *edge_borders = NULL; + int num_edgeborders = 0; + + int poly_prev = 0; + const int temp_poly_group_id = 3; /* Placeholder value. */ + + /* Group we could not find any available bit, will be reset to 0 at end. */ + const int poly_group_id_overflowed = 5; + + int tot_group = 0; + bool group_id_overflow = false; + + /* map vars */ + int *edge_poly_mem = NULL; + + if (totpoly == 0) { + *r_totgroup = 0; + *r_poly_groups = NULL; + if (r_edge_borders) { + *r_edge_borders = NULL; + *r_totedgeborder = 0; + } + return; + } + + if (r_edge_borders) { + edge_borders = BLI_BITMAP_NEW(totedge, __func__); + *r_totedgeborder = 0; + } + + if (!edge_poly_map) { + BKE_mesh_edge_poly_map_create( + &edge_poly_map, &edge_poly_mem, medge, totedge, mpoly, totpoly, mloop, totloop); + } + + poly_groups = static_cast(MEM_callocN(sizeof(int) * (size_t)totpoly, __func__)); + poly_stack = static_cast(MEM_mallocN(sizeof(int) * (size_t)totpoly, __func__)); + + while (true) { + int poly; + int bit_poly_group_mask = 0; + int poly_group_id; + int ps_curr_idx = 0, ps_end_idx = 0; /* stack indices */ + + for (poly = poly_prev; poly < totpoly; poly++) { + if (poly_groups[poly] == 0) { + break; + } + } + + if (poly == totpoly) { + /* all done */ + break; + } + + poly_group_id = use_bitflags ? temp_poly_group_id : ++tot_group; + + /* start searching from here next time */ + poly_prev = poly + 1; + + poly_groups[poly] = poly_group_id; + poly_stack[ps_end_idx++] = poly; + + while (ps_curr_idx != ps_end_idx) { + const MPoly *mp; + const MLoop *ml; + int j; + + poly = poly_stack[ps_curr_idx++]; + BLI_assert(poly_groups[poly] == poly_group_id); + + mp = &mpoly[poly]; + for (ml = &mloop[mp->loopstart], j = mp->totloop; j--; ml++) { + /* loop over poly users */ + const int me_idx = (int)ml->e; + const MEdge *me = &medge[me_idx]; + const MeshElemMap *map_ele = &edge_poly_map[me_idx]; + const int *p = map_ele->indices; + int i = map_ele->count; + if (!edge_boundary_check(mp, ml, me, i, mpoly, map_ele, edge_boundary_check_data)) { + for (; i--; p++) { + /* if we meet other non initialized its a bug */ + BLI_assert(ELEM(poly_groups[*p], 0, poly_group_id)); + + if (poly_groups[*p] == 0) { + poly_groups[*p] = poly_group_id; + poly_stack[ps_end_idx++] = *p; + } + } + } + else { + if (edge_borders && !BLI_BITMAP_TEST(edge_borders, me_idx)) { + BLI_BITMAP_ENABLE(edge_borders, me_idx); + num_edgeborders++; + } + if (use_bitflags) { + /* Find contiguous smooth groups already assigned, + * these are the values we can't reuse! */ + for (; i--; p++) { + int bit = poly_groups[*p]; + if (!ELEM(bit, 0, poly_group_id, poly_group_id_overflowed) && + !(bit_poly_group_mask & bit)) { + bit_poly_group_mask |= bit; + } + } + } + } + } + } + /* And now, we have all our poly from current group in poly_stack + * (from 0 to (ps_end_idx - 1)), + * as well as all smoothgroups bits we can't use in bit_poly_group_mask. + */ + if (use_bitflags) { + int i, *p, gid_bit = 0; + poly_group_id = 1; + + /* Find first bit available! */ + for (; (poly_group_id & bit_poly_group_mask) && (gid_bit < 32); gid_bit++) { + poly_group_id <<= 1; /* will 'overflow' on last possible iteration. */ + } + if (UNLIKELY(gid_bit > 31)) { + /* All bits used in contiguous smooth groups, we can't do much! + * NOTE: this is *very* unlikely - theoretically, four groups are enough, + * I don't think we can reach this goal with such a simple algorithm, + * but I don't think either we'll never need all 32 groups! + */ + printf( + "Warning, could not find an available id for current smooth group, faces will me " + "marked " + "as out of any smooth group...\n"); + + /* Can't use 0, will have to set them to this value later. */ + poly_group_id = poly_group_id_overflowed; + + group_id_overflow = true; + } + if (gid_bit > tot_group) { + tot_group = gid_bit; + } + /* And assign the final smooth group id to that poly group! */ + for (i = ps_end_idx, p = poly_stack; i--; p++) { + poly_groups[*p] = poly_group_id; + } + } + } + + if (use_bitflags) { + /* used bits are zero-based. */ + tot_group++; + } + + if (UNLIKELY(group_id_overflow)) { + int i = totpoly, *gid = poly_groups; + for (; i--; gid++) { + if (*gid == poly_group_id_overflowed) { + *gid = 0; + } + } + /* Using 0 as group id adds one more group! */ + tot_group++; + } + + if (edge_poly_mem) { + MEM_freeN(edge_poly_map); + MEM_freeN(edge_poly_mem); + } + MEM_freeN(poly_stack); + + *r_totgroup = tot_group; + *r_poly_groups = poly_groups; + if (r_edge_borders) { + *r_edge_borders = edge_borders; + *r_totedgeborder = num_edgeborders; + } +} + +static bool poly_is_island_boundary_smooth_cb(const MPoly *mp, + const MLoop *UNUSED(ml), + const MEdge *me, + const int edge_user_count, + const MPoly *mpoly_array, + const MeshElemMap *edge_poly_map, + void *UNUSED(user_data)) +{ + /* Edge is sharp if one of its polys is flat, or edge itself is sharp, + * or edge is not used by exactly two polygons. */ + if ((mp->flag & ME_SMOOTH) && !(me->flag & ME_SHARP) && (edge_user_count == 2)) { + /* In that case, edge appears to be smooth, but we need to check its other poly too. */ + const MPoly *mp_other = (mp == &mpoly_array[edge_poly_map->indices[0]]) ? + &mpoly_array[edge_poly_map->indices[1]] : + &mpoly_array[edge_poly_map->indices[0]]; + return (mp_other->flag & ME_SMOOTH) == 0; + } + return true; +} + +int *BKE_mesh_calc_smoothgroups(const MEdge *medge, + const int totedge, + const MPoly *mpoly, + const int totpoly, + const MLoop *mloop, + const int totloop, + int *r_totgroup, + const bool use_bitflags) +{ + int *poly_groups = NULL; + + poly_edge_loop_islands_calc(medge, + totedge, + mpoly, + totpoly, + mloop, + totloop, + NULL, + use_bitflags, + poly_is_island_boundary_smooth_cb, + NULL, + &poly_groups, + r_totgroup, + NULL, + NULL); + + return poly_groups; +} + +#define MISLAND_DEFAULT_BUFSIZE 64 + +void BKE_mesh_loop_islands_init(MeshIslandStore *island_store, + const short item_type, + const int items_num, + const short island_type, + const short innercut_type) +{ + MemArena *mem = island_store->mem; + + if (mem == NULL) { + mem = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); + island_store->mem = mem; + } + /* else memarena should be cleared */ + + BLI_assert( + ELEM(item_type, MISLAND_TYPE_VERT, MISLAND_TYPE_EDGE, MISLAND_TYPE_POLY, MISLAND_TYPE_LOOP)); + BLI_assert(ELEM( + island_type, MISLAND_TYPE_VERT, MISLAND_TYPE_EDGE, MISLAND_TYPE_POLY, MISLAND_TYPE_LOOP)); + + island_store->item_type = item_type; + island_store->items_to_islands_num = items_num; + island_store->items_to_islands = static_cast( + 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 = static_cast( + BLI_memarena_alloc(mem, sizeof(*island_store->islands) * island_store->islands_num_alloc)); + + island_store->innercut_type = innercut_type; + island_store->innercuts = static_cast( + BLI_memarena_alloc(mem, sizeof(*island_store->innercuts) * island_store->islands_num_alloc)); +} + +void BKE_mesh_loop_islands_clear(MeshIslandStore *island_store) +{ + island_store->item_type = MISLAND_TYPE_NONE; + island_store->items_to_islands_num = 0; + island_store->items_to_islands = NULL; + + island_store->island_type = MISLAND_TYPE_NONE; + island_store->islands_num = 0; + island_store->islands = NULL; + + island_store->innercut_type = MISLAND_TYPE_NONE; + island_store->innercuts = NULL; + + if (island_store->mem) { + BLI_memarena_clear(island_store->mem); + } + + island_store->islands_num_alloc = 0; +} + +void BKE_mesh_loop_islands_free(MeshIslandStore *island_store) +{ + if (island_store->mem) { + BLI_memarena_free(island_store->mem); + island_store->mem = NULL; + } +} + +void BKE_mesh_loop_islands_add(MeshIslandStore *island_store, + const int item_num, + const int *items_indices, + const int num_island_items, + int *island_item_indices, + const int num_innercut_items, + int *innercut_item_indices) +{ + MemArena *mem = island_store->mem; + + MeshElemMap *isld, *innrcut; + const int curr_island_idx = island_store->islands_num++; + const size_t curr_num_islands = (size_t)island_store->islands_num; + int i = item_num; + + while (i--) { + island_store->items_to_islands[items_indices[i]] = curr_island_idx; + } + + if (UNLIKELY(curr_num_islands > island_store->islands_num_alloc)) { + MeshElemMap **islds, **innrcuts; + + island_store->islands_num_alloc *= 2; + islds = static_cast( + 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 = static_cast( + 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 = static_cast( + BLI_memarena_alloc(mem, sizeof(*isld))); + isld->count = num_island_items; + isld->indices = static_cast( + 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 = static_cast( + BLI_memarena_alloc(mem, sizeof(*innrcut))); + innrcut->count = num_innercut_items; + innrcut->indices = static_cast( + 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); +} + +/* TODO: I'm not sure edge seam flag is enough to define UV islands? + * Maybe we should also consider UV-maps values + * themselves (i.e. different UV-edges for a same mesh-edge => boundary edge too?). + * Would make things much more complex though, + * and each UVMap would then need its own mesh mapping, not sure we want that at all! + */ +struct MeshCheckIslandBoundaryUv { + const MLoop *loops; + const MLoopUV *luvs; + const MeshElemMap *edge_loop_map; +}; + +static bool mesh_check_island_boundary_uv(const MPoly *UNUSED(mp), + const MLoop *ml, + const MEdge *me, + const int UNUSED(edge_user_count), + const MPoly *UNUSED(mpoly_array), + const MeshElemMap *UNUSED(edge_poly_map), + void *user_data) +{ + if (user_data) { + const MeshCheckIslandBoundaryUv *data = static_cast( + user_data); + const MLoop *loops = data->loops; + const MLoopUV *luvs = data->luvs; + const MeshElemMap *edge_to_loops = &data->edge_loop_map[ml->e]; + + BLI_assert(edge_to_loops->count >= 2 && (edge_to_loops->count % 2) == 0); + + const uint v1 = loops[edge_to_loops->indices[0]].v; + const uint v2 = loops[edge_to_loops->indices[1]].v; + const float *uvco_v1 = luvs[edge_to_loops->indices[0]].uv; + const float *uvco_v2 = luvs[edge_to_loops->indices[1]].uv; + for (int i = 2; i < edge_to_loops->count; i += 2) { + if (loops[edge_to_loops->indices[i]].v == v1) { + if (!equals_v2v2(uvco_v1, luvs[edge_to_loops->indices[i]].uv) || + !equals_v2v2(uvco_v2, luvs[edge_to_loops->indices[i + 1]].uv)) { + return true; + } + } + else { + BLI_assert(loops[edge_to_loops->indices[i]].v == v2); + UNUSED_VARS_NDEBUG(v2); + if (!equals_v2v2(uvco_v2, luvs[edge_to_loops->indices[i]].uv) || + !equals_v2v2(uvco_v1, luvs[edge_to_loops->indices[i + 1]].uv)) { + return true; + } + } + } + return false; + } + + /* Edge is UV boundary if tagged as seam. */ + return (me->flag & ME_SEAM) != 0; +} + +static bool mesh_calc_islands_loop_poly_uv(const MVert *UNUSED(verts), + const int UNUSED(totvert), + const MEdge *edges, + const int totedge, + const MPoly *polys, + const int totpoly, + const MLoop *loops, + const int totloop, + const MLoopUV *luvs, + MeshIslandStore *r_island_store) +{ + int *poly_groups = NULL; + int num_poly_groups; + + /* map vars */ + MeshElemMap *edge_poly_map; + int *edge_poly_mem; + + MeshElemMap *edge_loop_map; + int *edge_loop_mem; + + MeshCheckIslandBoundaryUv edge_boundary_check_data; + + int *poly_indices; + int *loop_indices; + int num_pidx, num_lidx; + + /* Those are used to detect 'inner cuts', i.e. edges that are borders, + * and yet have two or more polys of a same group using them + * (typical case: seam used to unwrap properly a cylinder). */ + BLI_bitmap *edge_borders = NULL; + int num_edge_borders = 0; + char *edge_border_count = NULL; + int *edge_innercut_indices = NULL; + int num_einnercuts = 0; + + int grp_idx, p_idx, pl_idx, l_idx; + + BKE_mesh_loop_islands_clear(r_island_store); + BKE_mesh_loop_islands_init( + r_island_store, MISLAND_TYPE_LOOP, totloop, MISLAND_TYPE_POLY, MISLAND_TYPE_EDGE); + + BKE_mesh_edge_poly_map_create( + &edge_poly_map, &edge_poly_mem, edges, totedge, polys, totpoly, loops, totloop); + + if (luvs) { + BKE_mesh_edge_loop_map_create( + &edge_loop_map, &edge_loop_mem, edges, totedge, polys, totpoly, loops, totloop); + edge_boundary_check_data.loops = loops; + edge_boundary_check_data.luvs = luvs; + edge_boundary_check_data.edge_loop_map = edge_loop_map; + } + + poly_edge_loop_islands_calc(edges, + totedge, + polys, + totpoly, + loops, + totloop, + edge_poly_map, + false, + mesh_check_island_boundary_uv, + luvs ? &edge_boundary_check_data : NULL, + &poly_groups, + &num_poly_groups, + &edge_borders, + &num_edge_borders); + + if (!num_poly_groups) { + /* Should never happen... */ + MEM_freeN(edge_poly_map); + MEM_freeN(edge_poly_mem); + + if (edge_borders) { + MEM_freeN(edge_borders); + } + return false; + } + + if (num_edge_borders) { + edge_border_count = static_cast( + MEM_mallocN(sizeof(*edge_border_count) * (size_t)totedge, __func__)); + edge_innercut_indices = static_cast( + MEM_mallocN(sizeof(*edge_innercut_indices) * (size_t)num_edge_borders, __func__)); + } + + poly_indices = static_cast( + MEM_mallocN(sizeof(*poly_indices) * (size_t)totpoly, __func__)); + loop_indices = static_cast( + 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++) { + num_pidx = num_lidx = 0; + if (num_edge_borders) { + num_einnercuts = 0; + memset(edge_border_count, 0, sizeof(*edge_border_count) * (size_t)totedge); + } + + for (p_idx = 0; p_idx < totpoly; p_idx++) { + if (poly_groups[p_idx] != grp_idx) { + continue; + } + 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++) { + 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)) { + edge_border_count[ml->e]++; + if (edge_border_count[ml->e] == 2) { + edge_innercut_indices[num_einnercuts++] = (int)ml->e; + } + } + } + } + + BKE_mesh_loop_islands_add(r_island_store, + num_lidx, + loop_indices, + num_pidx, + poly_indices, + num_einnercuts, + edge_innercut_indices); + } + + MEM_freeN(edge_poly_map); + MEM_freeN(edge_poly_mem); + + if (luvs) { + MEM_freeN(edge_loop_map); + MEM_freeN(edge_loop_mem); + } + + MEM_freeN(poly_indices); + MEM_freeN(loop_indices); + MEM_freeN(poly_groups); + + if (edge_borders) { + MEM_freeN(edge_borders); + } + + if (num_edge_borders) { + MEM_freeN(edge_border_count); + MEM_freeN(edge_innercut_indices); + } + return true; +} + +bool BKE_mesh_calc_islands_loop_poly_edgeseam(const MVert *verts, + const int totvert, + const MEdge *edges, + const int totedge, + const MPoly *polys, + const int totpoly, + const MLoop *loops, + const int totloop, + MeshIslandStore *r_island_store) +{ + return mesh_calc_islands_loop_poly_uv( + verts, totvert, edges, totedge, polys, totpoly, loops, totloop, NULL, r_island_store); +} + +bool BKE_mesh_calc_islands_loop_poly_uvmap(MVert *verts, + const int totvert, + MEdge *edges, + const int totedge, + MPoly *polys, + const int totpoly, + MLoop *loops, + const int totloop, + const MLoopUV *luvs, + MeshIslandStore *r_island_store) +{ + BLI_assert(luvs != NULL); + return mesh_calc_islands_loop_poly_uv( + verts, totvert, edges, totedge, polys, totpoly, loops, totloop, luvs, 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_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); + const Span verts = mesh_mutable.verts(); + const Span polys = mesh_mutable.polys(); + const Span 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); + const Span verts = mesh_mutable.verts(); + const Span polys = mesh_mutable.polys(); + const Span 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 verts = mesh->verts(); + MutableSpan edges = mesh->edges_for_write(); + const Span polys = mesh->polys(); + const Span 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 input_verts = input_mesh->verts(); + const Span 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 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 mesh_verts = mesh->verts_for_write(); + MutableSpan polys = mesh->polys_for_write(); + MutableSpan 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{mesh->mloop, mesh->totloop}; + const Span verts = mesh->verts(); + const Span loops = mesh->loops(); Span 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 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 mverts{mesh->mvert, mesh->totvert}; - MutableSpan mloops{mesh->mloop, mesh->totloop}; - MutableSpan mpolys{mesh->mpoly, mesh->totpoly}; + MutableSpan mesh_verts = mesh->verts_for_write(); + MutableSpan mesh_polys = mesh->polys_for_write(); + MutableSpan 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 verts = mesh->verts(); + const Span polys = mesh->polys(); + const Span 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 verts = me_eval->verts_for_write(); + MutableSpan edges = me_eval->edges_for_write(); + MutableSpan polys = me_eval->polys_for_write(); + MutableSpan 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(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 BLI_NOINLINE static void sample_point_attribute(const Mesh &mesh, const Span looptri_indices, const Span bary_coords, - const VArray &data_in, + const VArray &src, const IndexMask mask, - const MutableSpan data_out) + const MutableSpan dst) { + const Span loops = mesh.loops(); const Span 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 looptri_indices, const Span 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( - mesh, looptri_indices, bary_coords, data_in.typed(), mask, data_out.typed()); + mesh, looptri_indices, bary_coords, src.typed(), mask, dst.typed()); }); } @@ -63,9 +65,9 @@ template BLI_NOINLINE static void sample_corner_attribute(const Mesh &mesh, const Span looptri_indices, const Span bary_coords, - const VArray &data_in, + const VArray &src, const IndexMask mask, - const MutableSpan data_out) + const MutableSpan dst) { const Span 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 looptri_indices, const Span 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( - mesh, looptri_indices, bary_coords, data_in.typed(), mask, data_out.typed()); + mesh, looptri_indices, bary_coords, src.typed(), mask, dst.typed()); }); } template void sample_face_attribute(const Mesh &mesh, const Span looptri_indices, - const VArray &data_in, + const VArray &src, const IndexMask mask, - const MutableSpan data_out) + const MutableSpan dst) { const Span 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 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(mesh, looptri_indices, data_in.typed(), mask, data_out.typed()); + sample_face_attribute(mesh, looptri_indices, src.typed(), mask, dst.typed()); }); } @@ -157,6 +159,8 @@ Span MeshAttributeInterpolator::ensure_barycentric_coords() } bary_coords_.reinitialize(mask_.min_array_size()); + const Span verts = mesh_->verts(); + const Span loops = mesh_->loops(); const Span looptris{BKE_mesh_runtime_looptri_ensure(mesh_), BKE_mesh_runtime_looptri_len(mesh_)}; @@ -164,14 +168,14 @@ Span 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 MeshAttributeInterpolator::ensure_nearest_weights() } nearest_weights_.reinitialize(mask_.min_array_size()); + const Span verts = mesh_->verts(); + const Span loops = mesh_->loops(); const Span looptris{BKE_mesh_runtime_looptri_ensure(mesh_), BKE_mesh_runtime_looptri_len(mesh_)}; @@ -192,13 +198,13 @@ Span 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 &r_looptri_indices, Vector &r_positions) { + const Span verts = mesh.verts(); + const Span loops = mesh.loops(); const Span 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 &r_looptri_indices, Vector &r_positions) { + const Span verts = mesh.verts(); + const Span loops = mesh.loops(); const Span 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 verts, + const Span 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.c deleted file mode 100644 index a677a0d6ebb..00000000000 --- a/source/blender/blenkernel/intern/mesh_tangent.c +++ /dev/null @@ -1,741 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright 2001-2002 NaN Holding BV. All rights reserved. */ - -/** \file - * \ingroup bke - * - * Functions to evaluate mesh tangents. - */ - -#include - -#include "MEM_guardedalloc.h" - -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" - -#include "BLI_math.h" -#include "BLI_task.h" -#include "BLI_utildefines.h" - -#include "BKE_customdata.h" -#include "BKE_mesh.h" -#include "BKE_mesh_runtime.h" -#include "BKE_mesh_tangent.h" -#include "BKE_report.h" - -#include "BLI_strict_flags.h" - -#include "atomic_ops.h" -#include "mikktspace.h" - -/* -------------------------------------------------------------------- */ -/** \name Mesh Tangent Calculations (Single Layer) - * \{ */ - -/* Tangent space utils. */ - -/* User data. */ -typedef struct { - const MPoly *mpolys; /* faces */ - const MLoop *mloops; /* faces's 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), - const MLoop *mloops, - float (*r_looptangent)[4], - const float (*loopnors)[3], - const MLoopUV *loopuvs, - const int UNUSED(numLoops), - const MPoly *mpolys, - 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. */ - mesh_to_tangent.mpolys = mpolys; - mesh_to_tangent.mloops = mloops; - mesh_to_tangent.mverts = mverts; - mesh_to_tangent.luvs = loopuvs; - mesh_to_tangent.lnors = loopnors; - 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!"); - } -} - -void BKE_mesh_calc_loop_tangent_single(Mesh *mesh, - const char *uvmap, - float (*r_looptangents)[4], - ReportList *reports) -{ - const MLoopUV *loopuvs; - - /* Check we have valid texture coordinates first! */ - if (uvmap) { - loopuvs = CustomData_get_layer_named(&mesh->ldata, CD_MLOOPUV, uvmap); - } - else { - loopuvs = CustomData_get_layer(&mesh->ldata, CD_MLOOPUV); - } - if (!loopuvs) { - BKE_reportf(reports, - RPT_ERROR, - "Tangent space computation needs a UV Map, \"%s\" not found, aborting", - uvmap); - return; - } - - const float(*loopnors)[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, - mesh->totvert, - mesh->mloop, - r_looptangents, - loopnors, - loopuvs, - mesh->totloop, - mesh->mpoly, - mesh->totpoly, - reports); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Mesh Tangent Calculations (All Layers) - * \{ */ - -/* 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; - -#ifdef USE_LOOPTRI_DETECT_QUADS - return pMesh->num_face_as_quad_map; -#else - return pMesh->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; - -#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 */ - } - else { - lt = &pMesh->looptri[face_num]; - } -#else - lt = &pMesh->looptri[face_num]; -#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; - -#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 */ - } - else { - lt = &pMesh->looptri[face_num]; - } -#else - lt = &pMesh->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]); - } -} - -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; - -#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 */ - } - 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]); - } - 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 -#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); - } - } - } - 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; - -#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 */ - } - else { - lt = &pMesh->looptri[face_num]; - } -#else - lt = &pMesh->looptri[face_num]; -#endif - loop_index = lt->tri[vert_index]; - - float *pRes; - -finally: - pRes = pMesh->tangent[loop_index]; - copy_v3_v3(pRes, fvTangent); - pRes[3] = fSign; -} - -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); - } -} - -void BKE_mesh_add_loop_tangent_named_layer_for_uv(CustomData *uv_data, - CustomData *tan_data, - int numLoopData, - const char *layer_name) -{ - 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); - } -} - -void BKE_mesh_calc_loop_tangent_step_0(const CustomData *loopData, - bool calc_active_tangent, - const char (*tangent_names)[MAX_NAME], - int tangent_names_count, - bool *rcalc_act, - bool *rcalc_ren, - int *ract_uv_n, - int *rren_uv_n, - char *ract_uv_name, - char *rren_uv_name, - short *rtangent_mask) -{ - /* Active uv in viewport */ - int layer_index = CustomData_get_layer_index(loopData, CD_MLOOPUV); - *ract_uv_n = CustomData_get_active_layer(loopData, CD_MLOOPUV); - ract_uv_name[0] = 0; - if (*ract_uv_n != -1) { - strcpy(ract_uv_name, loopData->layers[*ract_uv_n + layer_index].name); - } - - /* Active tangent in render */ - *rren_uv_n = CustomData_get_render_layer(loopData, CD_MLOOPUV); - rren_uv_name[0] = 0; - if (*rren_uv_n != -1) { - strcpy(rren_uv_name, loopData->layers[*rren_uv_n + layer_index].name); - } - - /* If active tangent not in tangent_names we take it into account */ - *rcalc_act = false; - *rcalc_ren = false; - for (int i = 0; i < tangent_names_count; i++) { - if (tangent_names[i][0] == 0) { - calc_active_tangent = true; - } - } - if (calc_active_tangent) { - *rcalc_act = true; - *rcalc_ren = true; - for (int i = 0; i < tangent_names_count; i++) { - if (STREQ(ract_uv_name, tangent_names[i])) { - *rcalc_act = false; - } - if (STREQ(rren_uv_name, tangent_names[i])) { - *rcalc_ren = false; - } - } - } - *rtangent_mask = 0; - - const int uv_layer_num = CustomData_number_of_layers(loopData, CD_MLOOPUV); - for (int n = 0; n < uv_layer_num; n++) { - const char *name = CustomData_get_layer_name(loopData, CD_MLOOPUV, n); - bool add = false; - for (int i = 0; i < tangent_names_count; i++) { - if (tangent_names[i][0] && STREQ(tangent_names[i], name)) { - add = true; - break; - } - } - if (!add && ((*rcalc_act && ract_uv_name[0] && STREQ(ract_uv_name, name)) || - (*rcalc_ren && rren_uv_name[0] && STREQ(rren_uv_name, name)))) { - add = true; - } - if (add) { - *rtangent_mask |= (short)(1 << n); - } - } - - if (uv_layer_num == 0) { - *rtangent_mask |= DM_TANGENT_MASK_ORCO; - } -} - -void BKE_mesh_calc_loop_tangent_ex(const MVert *mvert, - const MPoly *mpoly, - const uint mpoly_len, - const MLoop *mloop, - const MLoopTri *looptri, - const uint looptri_len, - - CustomData *loopdata, - bool calc_active_tangent, - const char (*tangent_names)[MAX_NAME], - int tangent_names_len, - const float (*vert_normals)[3], - const float (*poly_normals)[3], - const float (*loop_normals)[3], - const float (*vert_orco)[3], - /* result */ - CustomData *loopdata_out, - const uint loopdata_out_len, - short *tangent_mask_curr_p) -{ - int act_uv_n = -1; - int ren_uv_n = -1; - bool calc_act = false; - bool calc_ren = false; - char act_uv_name[MAX_NAME]; - char ren_uv_name[MAX_NAME]; - short tangent_mask = 0; - short tangent_mask_curr = *tangent_mask_curr_p; - - BKE_mesh_calc_loop_tangent_step_0(loopdata, - calc_active_tangent, - tangent_names, - tangent_names_len, - &calc_act, - &calc_ren, - &act_uv_n, - &ren_uv_n, - act_uv_name, - ren_uv_name, - &tangent_mask); - if ((tangent_mask_curr | tangent_mask) != tangent_mask_curr) { - /* Check we have all the needed layers */ - /* Allocate needed tangent layers */ - for (int i = 0; i < tangent_names_len; i++) { - if (tangent_names[i][0]) { - BKE_mesh_add_loop_tangent_named_layer_for_uv( - loopdata, loopdata_out, (int)loopdata_out_len, tangent_names[i]); - } - } - 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, ""); - } - if (calc_act && act_uv_name[0]) { - BKE_mesh_add_loop_tangent_named_layer_for_uv( - loopdata, loopdata_out, (int)loopdata_out_len, act_uv_name); - } - if (calc_ren && ren_uv_name[0]) { - BKE_mesh_add_loop_tangent_named_layer_for_uv( - loopdata, loopdata_out, (int)loopdata_out_len, ren_uv_name); - } - -#ifdef USE_LOOPTRI_DETECT_QUADS - int num_face_as_quad_map; - int *face_as_quad_map = NULL; - - /* 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__); - int k, j; - for (k = 0, j = 0; j < (int)looptri_len; k++, j++) { - face_as_quad_map[k] = j; - /* step over all quads */ - if (mpoly[looptri[j].poly].totloop == 4) { - j++; /* skips the nest looptri */ - } - } - num_face_as_quad_map = k; - } - else { - num_face_as_quad_map = (int)looptri_len; - } -#endif - - /* Calculation */ - if (looptri_len != 0) { - TaskPool *task_pool = BLI_task_pool_create(NULL, TASK_PRIORITY_HIGH); - - tangent_mask_curr = 0; - /* Calculate tangent layers */ - SGLSLMeshToTangent data_array[MAX_MTFACE]; - const int tangent_layer_num = CustomData_number_of_layers(loopdata_out, CD_TANGENT); - for (int n = 0; n < tangent_layer_num; n++) { - int index = CustomData_get_layer_index_n(loopdata_out, CD_TANGENT, n); - BLI_assert(n < MAX_MTFACE); - SGLSLMeshToTangent *mesh2tangent = &data_array[n]; - mesh2tangent->numTessFaces = (int)looptri_len; -#ifdef USE_LOOPTRI_DETECT_QUADS - mesh2tangent->face_as_quad_map = face_as_quad_map; - mesh2tangent->num_face_as_quad_map = num_face_as_quad_map; -#endif - mesh2tangent->mvert = mvert; - mesh2tangent->vert_normals = vert_normals; - mesh2tangent->mpoly = mpoly; - mesh2tangent->mloop = mloop; - mesh2tangent->looptri = looptri; - /* NOTE: we assume we do have tessellated loop normals at this point - * (in case it is object-enabled), have to check this is valid. */ - 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); - - /* Fill the resulting tangent_mask */ - if (!mesh2tangent->mloopuv) { - mesh2tangent->orco = vert_orco; - if (!mesh2tangent->orco) { - continue; - } - - tangent_mask_curr |= DM_TANGENT_MASK_ORCO; - } - else { - int uv_ind = CustomData_get_named_layer_index( - loopdata, CD_MLOOPUV, loopdata_out->layers[index].name); - int uv_start = CustomData_get_layer_index(loopdata, CD_MLOOPUV); - BLI_assert(uv_ind != -1 && uv_start != -1); - BLI_assert(uv_ind - uv_start < MAX_MTFACE); - 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); - } - - BLI_assert(tangent_mask_curr == tangent_mask); - BLI_task_pool_work_and_wait(task_pool); - BLI_task_pool_free(task_pool); - } - else { - tangent_mask_curr = tangent_mask; - } -#ifdef USE_LOOPTRI_DETECT_QUADS - if (face_as_quad_map) { - MEM_freeN(face_as_quad_map); - } -# undef USE_LOOPTRI_DETECT_QUADS - -#endif - - *tangent_mask_curr_p = tangent_mask_curr; - - /* Update active layer index */ - int act_uv_index = (act_uv_n != -1) ? - CustomData_get_layer_index_n(loopdata, CD_MLOOPUV, act_uv_n) : - -1; - if (act_uv_index != -1) { - int tan_index = CustomData_get_named_layer_index( - loopdata, CD_TANGENT, loopdata->layers[act_uv_index].name); - CustomData_set_layer_active_index(loopdata, CD_TANGENT, tan_index); - } /* else tangent has been built from orco */ - - /* Update render layer index */ - int ren_uv_index = (ren_uv_n != -1) ? - CustomData_get_layer_index_n(loopdata, CD_MLOOPUV, ren_uv_n) : - -1; - if (ren_uv_index != -1) { - int tan_index = CustomData_get_named_layer_index( - loopdata, CD_TANGENT, loopdata->layers[ren_uv_index].name); - CustomData_set_layer_render_index(loopdata, CD_TANGENT, tan_index); - } /* else tangent has been built from orco */ - } -} - -void BKE_mesh_calc_loop_tangents(Mesh *me_eval, - bool calc_active_tangent, - const char (*tangent_names)[MAX_NAME], - int tangent_names_len) -{ - BKE_mesh_runtime_looptri_ensure(me_eval); - - /* TODO(campbell): 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); -} - -/** \} */ diff --git a/source/blender/blenkernel/intern/mesh_tangent.cc b/source/blender/blenkernel/intern/mesh_tangent.cc new file mode 100644 index 00000000000..8f9af5e9258 --- /dev/null +++ b/source/blender/blenkernel/intern/mesh_tangent.cc @@ -0,0 +1,605 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2001-2002 NaN Holding BV. All rights reserved. */ + +/** \file + * \ingroup bke + * + * Functions to evaluate mesh tangents. + */ + +#include + +#include "MEM_guardedalloc.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BLI_math.h" +#include "BLI_task.h" +#include "BLI_utildefines.h" + +#include "BKE_customdata.h" +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" +#include "BKE_mesh_tangent.h" +#include "BKE_report.h" + +#include "BLI_strict_flags.h" + +#include "atomic_ops.h" +#include "mikktspace.hh" + +/* -------------------------------------------------------------------- */ +/** \name Mesh Tangent Calculations (Single Layer) + * \{ */ + +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); + } + + const MPoly *mpolys; /* faces */ + 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 */ +}; + +void BKE_mesh_calc_loop_tangent_single_ex(const MVert *mverts, + const int UNUSED(numVerts), + const MLoop *mloops, + float (*r_looptangent)[4], + const float (*loopnors)[3], + const MLoopUV *loopuvs, + const int UNUSED(numLoops), + const MPoly *mpolys, + const int numPolys, + ReportList *reports) +{ + /* Compute Mikktspace's tangent normals. */ + BKEMeshToTangent mesh_to_tangent; + mesh_to_tangent.mpolys = mpolys; + mesh_to_tangent.mloops = mloops; + mesh_to_tangent.mverts = mverts; + mesh_to_tangent.luvs = loopuvs; + mesh_to_tangent.lnors = loopnors; + mesh_to_tangent.tangents = r_looptangent; + mesh_to_tangent.num_polys = numPolys; + + mikk::Mikktspace 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, + const char *uvmap, + float (*r_looptangents)[4], + ReportList *reports) +{ + const MLoopUV *loopuvs; + + /* Check we have valid texture coordinates first! */ + if (uvmap) { + loopuvs = static_cast(CustomData_get_layer_named(&mesh->ldata, CD_MLOOPUV, uvmap)); + } + else { + loopuvs = static_cast(CustomData_get_layer(&mesh->ldata, CD_MLOOPUV)); + } + if (!loopuvs) { + BKE_reportf(reports, + RPT_ERROR, + "Tangent space computation needs a UV Map, \"%s\" not found, aborting", + uvmap); + return; + } + + const float(*loopnors)[3] = static_cast( + 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(BKE_mesh_verts(mesh), + mesh->totvert, + BKE_mesh_loops(mesh), + r_looptangents, + loopnors, + loopuvs, + mesh->totloop, + BKE_mesh_polys(mesh), + mesh->totpoly, + reports); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Mesh Tangent Calculations (All Layers) + * \{ */ + +/* Necessary complexity to handle looptri's as quads for correct tangents */ +#define USE_LOOPTRI_DETECT_QUADS + +struct SGLSLMeshToTangent { + uint GetNumFaces() + { +#ifdef USE_LOOPTRI_DETECT_QUADS + return (uint)num_face_as_quad_map; +#else + return (uint)numTessFaces; +#endif + } + + uint GetNumVerticesOfFace(const uint face_num) + { +#ifdef USE_LOOPTRI_DETECT_QUADS + 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; + } + } + return 3; +#else + UNUSED_VARS(pContext, face_num); + return 3; +#endif + } + + uint GetLoop(const uint face_num, const uint vert_num, const MLoopTri *<) + { +#ifdef USE_LOOPTRI_DETECT_QUADS + 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]; + } +#else + lt = &looptri[face_num]; +#endif + return lt->tri[vert_num]; + } + + 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); + } + + 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); + } + } + + 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 if ((mpoly[lt->poly].flag & ME_SMOOTH) == 0) { /* flat */ + if (precomputedFaceNormals) { + return mikk::float3(precomputedFaceNormals[lt->poly]); + } + 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(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]); + } + } + + 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); + + copy_v4_fl4(tangent[loop_index], T.x, T.y, T.z, orientation ? 1.0f : -1.0f); + } + + 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 +}; + +static void DM_calc_loop_tangents_thread(TaskPool *__restrict UNUSED(pool), void *taskdata) +{ + SGLSLMeshToTangent *mesh_data = static_cast(taskdata); + + mikk::Mikktspace mikk(*mesh_data); + mikk.genTangSpace(); +} + +void BKE_mesh_add_loop_tangent_named_layer_for_uv(CustomData *uv_data, + CustomData *tan_data, + int numLoopData, + const char *layer_name) +{ + 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_SET_DEFAULT, nullptr, numLoopData, layer_name); + } +} + +void BKE_mesh_calc_loop_tangent_step_0(const CustomData *loopData, + bool calc_active_tangent, + const char (*tangent_names)[MAX_NAME], + int tangent_names_count, + bool *rcalc_act, + bool *rcalc_ren, + int *ract_uv_n, + int *rren_uv_n, + char *ract_uv_name, + char *rren_uv_name, + short *rtangent_mask) +{ + /* Active uv in viewport */ + int layer_index = CustomData_get_layer_index(loopData, CD_MLOOPUV); + *ract_uv_n = CustomData_get_active_layer(loopData, CD_MLOOPUV); + ract_uv_name[0] = 0; + if (*ract_uv_n != -1) { + strcpy(ract_uv_name, loopData->layers[*ract_uv_n + layer_index].name); + } + + /* Active tangent in render */ + *rren_uv_n = CustomData_get_render_layer(loopData, CD_MLOOPUV); + rren_uv_name[0] = 0; + if (*rren_uv_n != -1) { + strcpy(rren_uv_name, loopData->layers[*rren_uv_n + layer_index].name); + } + + /* If active tangent not in tangent_names we take it into account */ + *rcalc_act = false; + *rcalc_ren = false; + for (int i = 0; i < tangent_names_count; i++) { + if (tangent_names[i][0] == 0) { + calc_active_tangent = true; + } + } + if (calc_active_tangent) { + *rcalc_act = true; + *rcalc_ren = true; + for (int i = 0; i < tangent_names_count; i++) { + if (STREQ(ract_uv_name, tangent_names[i])) { + *rcalc_act = false; + } + if (STREQ(rren_uv_name, tangent_names[i])) { + *rcalc_ren = false; + } + } + } + *rtangent_mask = 0; + + const int uv_layer_num = CustomData_number_of_layers(loopData, CD_MLOOPUV); + for (int n = 0; n < uv_layer_num; n++) { + const char *name = CustomData_get_layer_name(loopData, CD_MLOOPUV, n); + bool add = false; + for (int i = 0; i < tangent_names_count; i++) { + if (tangent_names[i][0] && STREQ(tangent_names[i], name)) { + add = true; + break; + } + } + if (!add && ((*rcalc_act && ract_uv_name[0] && STREQ(ract_uv_name, name)) || + (*rcalc_ren && rren_uv_name[0] && STREQ(rren_uv_name, name)))) { + add = true; + } + if (add) { + *rtangent_mask |= (short)(1 << n); + } + } + + if (uv_layer_num == 0) { + *rtangent_mask |= DM_TANGENT_MASK_ORCO; + } +} + +void BKE_mesh_calc_loop_tangent_ex(const MVert *mvert, + const MPoly *mpoly, + const uint mpoly_len, + const MLoop *mloop, + const MLoopTri *looptri, + const uint looptri_len, + + CustomData *loopdata, + bool calc_active_tangent, + const char (*tangent_names)[MAX_NAME], + int tangent_names_len, + const float (*vert_normals)[3], + const float (*poly_normals)[3], + const float (*loop_normals)[3], + const float (*vert_orco)[3], + /* result */ + CustomData *loopdata_out, + const uint loopdata_out_len, + short *tangent_mask_curr_p) +{ + int act_uv_n = -1; + int ren_uv_n = -1; + bool calc_act = false; + bool calc_ren = false; + char act_uv_name[MAX_NAME]; + char ren_uv_name[MAX_NAME]; + short tangent_mask = 0; + short tangent_mask_curr = *tangent_mask_curr_p; + + BKE_mesh_calc_loop_tangent_step_0(loopdata, + calc_active_tangent, + tangent_names, + tangent_names_len, + &calc_act, + &calc_ren, + &act_uv_n, + &ren_uv_n, + act_uv_name, + ren_uv_name, + &tangent_mask); + if ((tangent_mask_curr | tangent_mask) != tangent_mask_curr) { + /* Check we have all the needed layers */ + /* Allocate needed tangent layers */ + for (int i = 0; i < tangent_names_len; i++) { + if (tangent_names[i][0]) { + BKE_mesh_add_loop_tangent_named_layer_for_uv( + loopdata, loopdata_out, (int)loopdata_out_len, tangent_names[i]); + } + } + 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_SET_DEFAULT, nullptr, (int)loopdata_out_len, ""); + } + if (calc_act && act_uv_name[0]) { + BKE_mesh_add_loop_tangent_named_layer_for_uv( + loopdata, loopdata_out, (int)loopdata_out_len, act_uv_name); + } + if (calc_ren && ren_uv_name[0]) { + BKE_mesh_add_loop_tangent_named_layer_for_uv( + loopdata, loopdata_out, (int)loopdata_out_len, ren_uv_name); + } + +#ifdef USE_LOOPTRI_DETECT_QUADS + int num_face_as_quad_map; + 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 = static_cast(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; + /* step over all quads */ + if (mpoly[looptri[j].poly].totloop == 4) { + j++; /* skips the nest looptri */ + } + } + num_face_as_quad_map = k; + } + else { + num_face_as_quad_map = (int)looptri_len; + } +#endif + + /* Calculation */ + if (looptri_len != 0) { + TaskPool *task_pool = BLI_task_pool_create(nullptr, TASK_PRIORITY_HIGH); + + tangent_mask_curr = 0; + /* Calculate tangent layers */ + SGLSLMeshToTangent data_array[MAX_MTFACE]; + const int tangent_layer_num = CustomData_number_of_layers(loopdata_out, CD_TANGENT); + for (int n = 0; n < tangent_layer_num; n++) { + int index = CustomData_get_layer_index_n(loopdata_out, CD_TANGENT, n); + BLI_assert(n < MAX_MTFACE); + SGLSLMeshToTangent *mesh2tangent = &data_array[n]; + mesh2tangent->numTessFaces = (int)looptri_len; +#ifdef USE_LOOPTRI_DETECT_QUADS + mesh2tangent->face_as_quad_map = face_as_quad_map; + mesh2tangent->num_face_as_quad_map = num_face_as_quad_map; +#endif + mesh2tangent->mvert = mvert; + mesh2tangent->vert_normals = vert_normals; + mesh2tangent->mpoly = mpoly; + mesh2tangent->mloop = mloop; + mesh2tangent->looptri = looptri; + /* NOTE: we assume we do have tessellated loop normals at this point + * (in case it is object-enabled), have to check this is valid. */ + mesh2tangent->precomputedLoopNormals = loop_normals; + mesh2tangent->precomputedFaceNormals = poly_normals; + + mesh2tangent->orco = nullptr; + mesh2tangent->mloopuv = static_cast( + CustomData_get_layer_named(loopdata, CD_MLOOPUV, loopdata_out->layers[index].name)); + + /* Fill the resulting tangent_mask */ + if (!mesh2tangent->mloopuv) { + mesh2tangent->orco = vert_orco; + if (!mesh2tangent->orco) { + continue; + } + + tangent_mask_curr |= DM_TANGENT_MASK_ORCO; + } + else { + int uv_ind = CustomData_get_named_layer_index( + loopdata, CD_MLOOPUV, loopdata_out->layers[index].name); + int uv_start = CustomData_get_layer_index(loopdata, CD_MLOOPUV); + BLI_assert(uv_ind != -1 && uv_start != -1); + BLI_assert(uv_ind - uv_start < MAX_MTFACE); + tangent_mask_curr |= (short)(1 << (uv_ind - uv_start)); + } + + mesh2tangent->tangent = static_cast(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); + BLI_task_pool_work_and_wait(task_pool); + BLI_task_pool_free(task_pool); + } + else { + tangent_mask_curr = tangent_mask; + } +#ifdef USE_LOOPTRI_DETECT_QUADS + if (face_as_quad_map) { + MEM_freeN(face_as_quad_map); + } +# undef USE_LOOPTRI_DETECT_QUADS + +#endif + + *tangent_mask_curr_p = tangent_mask_curr; + + /* Update active layer index */ + int act_uv_index = (act_uv_n != -1) ? + CustomData_get_layer_index_n(loopdata, CD_MLOOPUV, act_uv_n) : + -1; + if (act_uv_index != -1) { + int tan_index = CustomData_get_named_layer_index( + loopdata, CD_TANGENT, loopdata->layers[act_uv_index].name); + CustomData_set_layer_active_index(loopdata, CD_TANGENT, tan_index); + } /* else tangent has been built from orco */ + + /* Update render layer index */ + int ren_uv_index = (ren_uv_n != -1) ? + CustomData_get_layer_index_n(loopdata, CD_MLOOPUV, ren_uv_n) : + -1; + if (ren_uv_index != -1) { + int tan_index = CustomData_get_named_layer_index( + loopdata, CD_TANGENT, loopdata->layers[ren_uv_index].name); + CustomData_set_layer_render_index(loopdata, CD_TANGENT, tan_index); + } /* else tangent has been built from orco */ + } +} + +void BKE_mesh_calc_loop_tangents(Mesh *me_eval, + bool calc_active_tangent, + const char (*tangent_names)[MAX_NAME], + int tangent_names_len) +{ + BKE_mesh_runtime_looptri_ensure(me_eval); + + /* TODO(@campbellbarton): store in Mesh.runtime to avoid recalculation. */ + short tangent_mask = 0; + 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(CustomData_get_layer(&me_eval->ldata, CD_NORMAL)), + /* may be nullptr */ + static_cast(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.c deleted file mode 100644 index 173fb98912b..00000000000 --- a/source/blender/blenkernel/intern/mesh_tessellate.c +++ /dev/null @@ -1,342 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright 2001-2002 NaN Holding BV. All rights reserved. */ - -/** \file - * \ingroup bke - * - * This file contains code for polygon tessellation - * (creating triangles from polygons). - * - * \see bmesh_mesh_tessellate.c for the #BMesh equivalent of this file. - */ - -#include - -#include "MEM_guardedalloc.h" - -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" - -#include "BLI_math.h" -#include "BLI_memarena.h" -#include "BLI_polyfill_2d.h" -#include "BLI_task.h" -#include "BLI_utildefines.h" - -#include "BKE_customdata.h" -#include "BKE_mesh.h" /* Own include. */ - -#include "BLI_strict_flags.h" - -/** Compared against total loops. */ -#define MESH_FACE_TESSELLATE_THREADED_LIMIT 4096 - -/* -------------------------------------------------------------------- */ -/** \name Loop Tessellation - * - * Fill in #MLoopTri data-structure. - * \{ */ - -/** - * \param face_normal: This will be optimized out as a constant. - */ -BLI_INLINE void mesh_calc_tessellation_for_face_impl(const MLoop *mloop, - const MPoly *mpoly, - const MVert *mvert, - uint poly_index, - MLoopTri *mlt, - MemArena **pf_arena_p, - const bool face_normal, - const float normal_precalc[3]) -{ - const uint mp_loopstart = (uint)mpoly[poly_index].loopstart; - const uint mp_totloop = (uint)mpoly[poly_index].totloop; - -#define ML_TO_MLT(i1, i2, i3) \ - { \ - ARRAY_SET_ITEMS(mlt->tri, mp_loopstart + i1, mp_loopstart + i2, mp_loopstart + i3); \ - mlt->poly = poly_index; \ - } \ - ((void)0) - - switch (mp_totloop) { - case 3: { - ML_TO_MLT(0, 1, 2); - break; - } - case 4: { - ML_TO_MLT(0, 1, 2); - MLoopTri *mlt_a = mlt++; - ML_TO_MLT(0, 2, 3); - MLoopTri *mlt_b = mlt; - - if (UNLIKELY(face_normal ? is_quad_flip_v3_first_third_fast_with_normal( - /* Simpler calculation (using the normal). */ - mvert[mloop[mlt_a->tri[0]].v].co, - mvert[mloop[mlt_a->tri[1]].v].co, - mvert[mloop[mlt_a->tri[2]].v].co, - mvert[mloop[mlt_b->tri[2]].v].co, - normal_precalc) : - is_quad_flip_v3_first_third_fast( - /* Expensive calculation (no normal). */ - mvert[mloop[mlt_a->tri[0]].v].co, - mvert[mloop[mlt_a->tri[1]].v].co, - mvert[mloop[mlt_a->tri[2]].v].co, - mvert[mloop[mlt_b->tri[2]].v].co))) { - /* Flip out of degenerate 0-2 state. */ - mlt_a->tri[2] = mlt_b->tri[2]; - mlt_b->tri[0] = mlt_a->tri[1]; - } - break; - } - default: { - const MLoop *ml; - float axis_mat[3][3]; - - /* Calculate `axis_mat` to project verts to 2D. */ - if (face_normal == false) { - float normal[3]; - const float *co_curr, *co_prev; - - zero_v3(normal); - - /* Calc normal, flipped: to get a positive 2D cross product. */ - ml = mloop + mp_loopstart; - co_prev = mvert[ml[mp_totloop - 1].v].co; - for (uint j = 0; j < mp_totloop; j++, ml++) { - co_curr = mvert[ml->v].co; - add_newell_cross_v3_v3v3(normal, co_prev, co_curr); - co_prev = co_curr; - } - if (UNLIKELY(normalize_v3(normal) == 0.0f)) { - normal[2] = 1.0f; - } - axis_dominant_v3_to_m3_negate(axis_mat, normal); - } - else { - axis_dominant_v3_to_m3_negate(axis_mat, normal_precalc); - } - - const uint totfilltri = mp_totloop - 2; - - MemArena *pf_arena = *pf_arena_p; - if (UNLIKELY(pf_arena == NULL)) { - pf_arena = *pf_arena_p = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); - } - - 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); - - ml = mloop + mp_loopstart; - for (uint j = 0; j < mp_totloop; j++, ml++) { - mul_v2_m3v3(projverts[j], axis_mat, mvert[ml->v].co); - } - - BLI_polyfill_calc_arena(projverts, mp_totloop, 1, tris, pf_arena); - - /* Apply fill. */ - for (uint j = 0; j < totfilltri; j++, mlt++) { - const uint *tri = tris[j]; - ML_TO_MLT(tri[0], tri[1], tri[2]); - } - - BLI_memarena_clear(pf_arena); - - break; - } - } -#undef ML_TO_MLT -} - -static void mesh_calc_tessellation_for_face(const MLoop *mloop, - const MPoly *mpoly, - const MVert *mvert, - uint poly_index, - MLoopTri *mlt, - MemArena **pf_arena_p) -{ - mesh_calc_tessellation_for_face_impl( - mloop, mpoly, mvert, poly_index, mlt, pf_arena_p, false, NULL); -} - -static void mesh_calc_tessellation_for_face_with_normal(const MLoop *mloop, - const MPoly *mpoly, - const MVert *mvert, - uint poly_index, - MLoopTri *mlt, - MemArena **pf_arena_p, - const float normal_precalc[3]) -{ - mesh_calc_tessellation_for_face_impl( - mloop, mpoly, mvert, poly_index, mlt, pf_arena_p, true, normal_precalc); -} - -static void mesh_recalc_looptri__single_threaded(const MLoop *mloop, - const MPoly *mpoly, - const MVert *mvert, - int totloop, - int totpoly, - MLoopTri *mlooptri, - const float (*poly_normals)[3]) -{ - MemArena *pf_arena = NULL; - const MPoly *mp = mpoly; - uint tri_index = 0; - - if (poly_normals != NULL) { - for (uint poly_index = 0; poly_index < (uint)totpoly; poly_index++, mp++) { - mesh_calc_tessellation_for_face_with_normal(mloop, - mpoly, - mvert, - poly_index, - &mlooptri[tri_index], - &pf_arena, - poly_normals[poly_index]); - tri_index += (uint)(mp->totloop - 2); - } - } - else { - for (uint poly_index = 0; poly_index < (uint)totpoly; poly_index++, mp++) { - mesh_calc_tessellation_for_face( - mloop, mpoly, mvert, poly_index, &mlooptri[tri_index], &pf_arena); - tri_index += (uint)(mp->totloop - 2); - } - } - - if (pf_arena) { - BLI_memarena_free(pf_arena); - pf_arena = NULL; - } - BLI_assert(tri_index == (uint)poly_to_tri_count(totpoly, totloop)); - UNUSED_VARS_NDEBUG(totloop); -} - -struct TessellationUserData { - const MLoop *mloop; - const MPoly *mpoly; - const MVert *mvert; - - /** Output array. */ - MLoopTri *mlooptri; - - /** Optional pre-calculated polygon normals array. */ - const float (*poly_normals)[3]; -}; - -struct TessellationUserTLS { - MemArena *pf_arena; -}; - -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 int tri_index = poly_to_tri_count(index, data->mpoly[index].loopstart); - mesh_calc_tessellation_for_face_impl(data->mloop, - data->mpoly, - data->mvert, - (uint)index, - &data->mlooptri[tri_index], - &tls_data->pf_arena, - false, - NULL); -} - -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 int tri_index = poly_to_tri_count(index, data->mpoly[index].loopstart); - mesh_calc_tessellation_for_face_impl(data->mloop, - data->mpoly, - data->mvert, - (uint)index, - &data->mlooptri[tri_index], - &tls_data->pf_arena, - true, - data->poly_normals[index]); -} - -static void mesh_calc_tessellation_for_face_free_fn(const void *__restrict UNUSED(userdata), - void *__restrict tls_v) -{ - struct TessellationUserTLS *tls_data = tls_v; - if (tls_data->pf_arena) { - BLI_memarena_free(tls_data->pf_arena); - } -} - -static void mesh_recalc_looptri__multi_threaded(const MLoop *mloop, - const MPoly *mpoly, - const MVert *mvert, - int UNUSED(totloop), - int totpoly, - 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, - }; - - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - - settings.userdata_chunk = &tls_data_dummy; - settings.userdata_chunk_size = sizeof(tls_data_dummy); - - settings.func_free = mesh_calc_tessellation_for_face_free_fn; - - BLI_task_parallel_range(0, - totpoly, - &data, - poly_normals ? mesh_calc_tessellation_for_face_with_normal_fn : - mesh_calc_tessellation_for_face_fn, - &settings); -} - -void BKE_mesh_recalc_looptri(const MLoop *mloop, - const MPoly *mpoly, - const MVert *mvert, - int totloop, - int totpoly, - MLoopTri *mlooptri) -{ - if (totloop < MESH_FACE_TESSELLATE_THREADED_LIMIT) { - mesh_recalc_looptri__single_threaded(mloop, mpoly, mvert, totloop, totpoly, mlooptri, NULL); - } - else { - mesh_recalc_looptri__multi_threaded(mloop, mpoly, mvert, totloop, totpoly, mlooptri, NULL); - } -} - -void BKE_mesh_recalc_looptri_with_normals(const MLoop *mloop, - const MPoly *mpoly, - const MVert *mvert, - int totloop, - int totpoly, - MLoopTri *mlooptri, - const float (*poly_normals)[3]) -{ - BLI_assert(poly_normals != NULL); - if (totloop < MESH_FACE_TESSELLATE_THREADED_LIMIT) { - mesh_recalc_looptri__single_threaded( - mloop, mpoly, mvert, totloop, totpoly, mlooptri, poly_normals); - } - else { - mesh_recalc_looptri__multi_threaded( - mloop, mpoly, mvert, totloop, totpoly, mlooptri, poly_normals); - } -} - -/** \} */ diff --git a/source/blender/blenkernel/intern/mesh_tessellate.cc b/source/blender/blenkernel/intern/mesh_tessellate.cc new file mode 100644 index 00000000000..de4c60b28db --- /dev/null +++ b/source/blender/blenkernel/intern/mesh_tessellate.cc @@ -0,0 +1,343 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2001-2002 NaN Holding BV. All rights reserved. */ + +/** \file + * \ingroup bke + * + * This file contains code for polygon tessellation + * (creating triangles from polygons). + * + * \see bmesh_mesh_tessellate.c for the #BMesh equivalent of this file. + */ + +#include + +#include "MEM_guardedalloc.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BLI_math.h" +#include "BLI_memarena.h" +#include "BLI_polyfill_2d.h" +#include "BLI_task.h" +#include "BLI_utildefines.h" + +#include "BKE_customdata.h" +#include "BKE_mesh.h" /* Own include. */ + +#include "BLI_strict_flags.h" + +/** Compared against total loops. */ +#define MESH_FACE_TESSELLATE_THREADED_LIMIT 4096 + +/* -------------------------------------------------------------------- */ +/** \name Loop Tessellation + * + * Fill in #MLoopTri data-structure. + * \{ */ + +/** + * \param face_normal: This will be optimized out as a constant. + */ +BLI_INLINE void mesh_calc_tessellation_for_face_impl(const MLoop *mloop, + const MPoly *mpoly, + const MVert *mvert, + uint poly_index, + MLoopTri *mlt, + MemArena **pf_arena_p, + const bool face_normal, + const float normal_precalc[3]) +{ + const uint mp_loopstart = (uint)mpoly[poly_index].loopstart; + const uint mp_totloop = (uint)mpoly[poly_index].totloop; + +#define ML_TO_MLT(i1, i2, i3) \ + { \ + ARRAY_SET_ITEMS(mlt->tri, mp_loopstart + i1, mp_loopstart + i2, mp_loopstart + i3); \ + mlt->poly = poly_index; \ + } \ + ((void)0) + + switch (mp_totloop) { + case 3: { + ML_TO_MLT(0, 1, 2); + break; + } + case 4: { + ML_TO_MLT(0, 1, 2); + MLoopTri *mlt_a = mlt++; + ML_TO_MLT(0, 2, 3); + MLoopTri *mlt_b = mlt; + + if (UNLIKELY(face_normal ? is_quad_flip_v3_first_third_fast_with_normal( + /* Simpler calculation (using the normal). */ + mvert[mloop[mlt_a->tri[0]].v].co, + mvert[mloop[mlt_a->tri[1]].v].co, + mvert[mloop[mlt_a->tri[2]].v].co, + mvert[mloop[mlt_b->tri[2]].v].co, + normal_precalc) : + is_quad_flip_v3_first_third_fast( + /* Expensive calculation (no normal). */ + mvert[mloop[mlt_a->tri[0]].v].co, + mvert[mloop[mlt_a->tri[1]].v].co, + mvert[mloop[mlt_a->tri[2]].v].co, + mvert[mloop[mlt_b->tri[2]].v].co))) { + /* Flip out of degenerate 0-2 state. */ + mlt_a->tri[2] = mlt_b->tri[2]; + mlt_b->tri[0] = mlt_a->tri[1]; + } + break; + } + default: { + const MLoop *ml; + float axis_mat[3][3]; + + /* Calculate `axis_mat` to project verts to 2D. */ + if (face_normal == false) { + float normal[3]; + const float *co_curr, *co_prev; + + zero_v3(normal); + + /* Calc normal, flipped: to get a positive 2D cross product. */ + ml = mloop + mp_loopstart; + co_prev = mvert[ml[mp_totloop - 1].v].co; + for (uint j = 0; j < mp_totloop; j++, ml++) { + co_curr = mvert[ml->v].co; + add_newell_cross_v3_v3v3(normal, co_prev, co_curr); + co_prev = co_curr; + } + if (UNLIKELY(normalize_v3(normal) == 0.0f)) { + normal[2] = 1.0f; + } + axis_dominant_v3_to_m3_negate(axis_mat, normal); + } + else { + axis_dominant_v3_to_m3_negate(axis_mat, normal_precalc); + } + + const uint totfilltri = mp_totloop - 2; + + MemArena *pf_arena = *pf_arena_p; + if (UNLIKELY(pf_arena == nullptr)) { + pf_arena = *pf_arena_p = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); + } + + uint(*tris)[3] = static_cast( + BLI_memarena_alloc(pf_arena, sizeof(*tris) * (size_t)totfilltri)); + float(*projverts)[2] = static_cast( + BLI_memarena_alloc(pf_arena, sizeof(*projverts) * (size_t)mp_totloop)); + + ml = mloop + mp_loopstart; + for (uint j = 0; j < mp_totloop; j++, ml++) { + mul_v2_m3v3(projverts[j], axis_mat, mvert[ml->v].co); + } + + BLI_polyfill_calc_arena(projverts, mp_totloop, 1, tris, pf_arena); + + /* Apply fill. */ + for (uint j = 0; j < totfilltri; j++, mlt++) { + const uint *tri = tris[j]; + ML_TO_MLT(tri[0], tri[1], tri[2]); + } + + BLI_memarena_clear(pf_arena); + + break; + } + } +#undef ML_TO_MLT +} + +static void mesh_calc_tessellation_for_face(const MLoop *mloop, + const MPoly *mpoly, + const MVert *mvert, + uint poly_index, + MLoopTri *mlt, + MemArena **pf_arena_p) +{ + mesh_calc_tessellation_for_face_impl( + mloop, mpoly, mvert, poly_index, mlt, pf_arena_p, false, nullptr); +} + +static void mesh_calc_tessellation_for_face_with_normal(const MLoop *mloop, + const MPoly *mpoly, + const MVert *mvert, + uint poly_index, + MLoopTri *mlt, + MemArena **pf_arena_p, + const float normal_precalc[3]) +{ + mesh_calc_tessellation_for_face_impl( + mloop, mpoly, mvert, poly_index, mlt, pf_arena_p, true, normal_precalc); +} + +static void mesh_recalc_looptri__single_threaded(const MLoop *mloop, + const MPoly *mpoly, + const MVert *mvert, + int totloop, + int totpoly, + MLoopTri *mlooptri, + const float (*poly_normals)[3]) +{ + MemArena *pf_arena = nullptr; + const MPoly *mp = mpoly; + uint tri_index = 0; + + 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, + mvert, + poly_index, + &mlooptri[tri_index], + &pf_arena, + poly_normals[poly_index]); + tri_index += (uint)(mp->totloop - 2); + } + } + else { + for (uint poly_index = 0; poly_index < (uint)totpoly; poly_index++, mp++) { + mesh_calc_tessellation_for_face( + mloop, mpoly, mvert, poly_index, &mlooptri[tri_index], &pf_arena); + tri_index += (uint)(mp->totloop - 2); + } + } + + if (pf_arena) { + BLI_memarena_free(pf_arena); + pf_arena = nullptr; + } + BLI_assert(tri_index == (uint)poly_to_tri_count(totpoly, totloop)); + UNUSED_VARS_NDEBUG(totloop); +} + +struct TessellationUserData { + const MLoop *mloop; + const MPoly *mpoly; + const MVert *mvert; + + /** Output array. */ + MLoopTri *mlooptri; + + /** Optional pre-calculated polygon normals array. */ + const float (*poly_normals)[3]; +}; + +struct TessellationUserTLS { + MemArena *pf_arena; +}; + +static void mesh_calc_tessellation_for_face_fn(void *__restrict userdata, + const int index, + const TaskParallelTLS *__restrict tls) +{ + const TessellationUserData *data = static_cast(userdata); + TessellationUserTLS *tls_data = static_cast(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, + data->mvert, + (uint)index, + &data->mlooptri[tri_index], + &tls_data->pf_arena, + false, + nullptr); +} + +static void mesh_calc_tessellation_for_face_with_normal_fn(void *__restrict userdata, + const int index, + const TaskParallelTLS *__restrict tls) +{ + const TessellationUserData *data = static_cast(userdata); + TessellationUserTLS *tls_data = static_cast(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, + data->mvert, + (uint)index, + &data->mlooptri[tri_index], + &tls_data->pf_arena, + true, + data->poly_normals[index]); +} + +static void mesh_calc_tessellation_for_face_free_fn(const void *__restrict UNUSED(userdata), + void *__restrict tls_v) +{ + TessellationUserTLS *tls_data = static_cast(tls_v); + if (tls_data->pf_arena) { + BLI_memarena_free(tls_data->pf_arena); + } +} + +static void mesh_recalc_looptri__multi_threaded(const MLoop *mloop, + const MPoly *mpoly, + const MVert *mvert, + int UNUSED(totloop), + int totpoly, + MLoopTri *mlooptri, + const float (*poly_normals)[3]) +{ + 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); + + settings.userdata_chunk = &tls_data_dummy; + settings.userdata_chunk_size = sizeof(tls_data_dummy); + + settings.func_free = mesh_calc_tessellation_for_face_free_fn; + + BLI_task_parallel_range(0, + totpoly, + &data, + poly_normals ? mesh_calc_tessellation_for_face_with_normal_fn : + mesh_calc_tessellation_for_face_fn, + &settings); +} + +void BKE_mesh_recalc_looptri(const MLoop *mloop, + const MPoly *mpoly, + const MVert *mvert, + int totloop, + int totpoly, + MLoopTri *mlooptri) +{ + if (totloop < MESH_FACE_TESSELLATE_THREADED_LIMIT) { + mesh_recalc_looptri__single_threaded(mloop, mpoly, mvert, totloop, totpoly, mlooptri, nullptr); + } + else { + mesh_recalc_looptri__multi_threaded(mloop, mpoly, mvert, totloop, totpoly, mlooptri, nullptr); + } +} + +void BKE_mesh_recalc_looptri_with_normals(const MLoop *mloop, + const MPoly *mpoly, + const MVert *mvert, + int totloop, + int totpoly, + MLoopTri *mlooptri, + const float (*poly_normals)[3]) +{ + 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); + } + else { + mesh_recalc_looptri__multi_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 material_indices = + mesh->attributes_for_write().lookup_for_write("material_index"); + blender::MutableVArraySpan 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 verts = me->verts_for_write(); + MutableSpan edges = me->edges_for_write(); + MutableSpan polys = me->polys_for_write(); + MutableSpan 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 verts = me->verts_for_write(); + MutableSpan edges = me->edges_for_write(); + MutableSpan polys = me->polys_for_write(); + MutableSpan 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 material_indices = + me->attributes_for_write().lookup_for_write("material_index"); + blender::MutableVArraySpan 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 polys = me->polys_for_write(); + MutableSpan 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 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 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 verts = me->verts(); + const Span polys = me->polys(); + MutableSpan 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 loops = mesh->loops(); + MutableSpan 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(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( - me->runtime.wrapper_type); - me->runtime.wrapper_type = ME_WRAPPER_TYPE_MDATA; - - switch (geom_type_orig) { + switch (static_cast(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 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 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( 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(__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(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(__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(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(from_sock->in_out), from_sock->idname, from_sock->name); + bNodeSocket *iosock = ntreeAddSocketInterface(ntree, + static_cast(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 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 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(&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 &sockets_in_current_chain, + Vector &r_logical_origins, + Vector &r_skipped_origins) +{ + if (sockets_in_current_chain.contains(&input_socket)) { + /* Protect against reroute recursions. */ + return; + } + sockets_in_current_chain.append(&input_socket); + + Span 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 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 node_states, + Vector &r_sorted_nodes, + bool &r_cycle_detected) +{ + struct Item { + bNode *node; + int socket_index = 0; + int link_index = 0; + }; + + Stack 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 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 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 &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 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 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 gather_input_socket_dependencies( - const OutputFieldDependency &field_dependency, const NodeRef &node) +static Vector gather_input_socket_dependencies( + const OutputFieldDependency &field_dependency, const bNode &node) { const OutputSocketFieldType type = field_dependency.field_type(); - Vector input_sockets; + Vector input_sockets; switch (type) { case OutputSocketFieldType::FieldSource: case OutputSocketFieldType::None: { @@ -221,13 +230,13 @@ static Vector 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 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 field_state_by_socket_id) + const bNodeSocket &group_output_socket, const Span 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 handled_sockets; - Stack sockets_to_check; + Set handled_sockets; + Stack 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 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 field_state_by_socket_id) + const bNodeTree &tree, const MutableSpan field_state_by_socket_id) { - const NodeTreeRef::ToposortResult toposort_result = tree.toposort( - NodeTreeRef::ToposortDirection::RightToLeft); + const Span 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 connected_inputs = gather_input_socket_dependencies( + const Vector 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 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 field_state_by_socket_id) + const bNodeTree &tree, const MutableSpan field_state_by_socket_id) { - const NodeTreeRef::ToposortResult toposort_result = tree.toposort( - NodeTreeRef::ToposortDirection::LeftToRight); + const Span 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 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 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(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(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 new_inferencing_interface = std::make_unique(); - 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 field_state_by_socket_id(tree.sockets().size()); + Array 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 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 &tree_ref) - { - if (!tree_ref) { - tree_ref = std::make_unique(&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 &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 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 &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> 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(input_socket), const_cast(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 &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(group_node->bnode()->id); + for (const bNode *group_node : ntree.nodes_by_type("ShaderNodeGroup")) { + const bNodeTree *group = reinterpret_cast(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(node->bnode()->id); + for (const bNode *node : ntree.nodes_by_type(idname)) { + Image *image = reinterpret_cast(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 nodes = tree_ref.nodes_by_type(idname); + const Span 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 tree_output_sockets = this->find_output_sockets(tree); - const uint32_t old_topology_hash = btree.runtime->output_topology_hash; + const Vector 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 find_output_sockets(const NodeTreeRef &tree) + Vector find_output_sockets(const bNodeTree &tree) { - Vector sockets; - for (const NodeRef *node : tree.nodes()) { + Vector 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(bnode.id); + if (node.type == NODE_GROUP) { + const bNodeTree *node_group = reinterpret_cast(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 sockets) + uint32_t get_combined_socket_topology_hash(const bNodeTree &tree, + Span 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 get_socket_topology_hashes(const NodeTreeRef &tree, - Span sockets) + Array get_socket_topology_hashes(const bNodeTree &tree, + Span sockets) { - BLI_assert(!tree.has_link_cycles()); - Array> hash_by_socket_id(tree.sockets().size()); - Stack sockets_to_check = sockets; + BLI_assert(!tree.has_available_link_cycle()); + Array> hash_by_socket_id(tree.all_sockets().size()); + Stack 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 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 sockets) + bool check_if_socket_outputs_changed_based_on_flags(const bNodeTree &tree, + Span sockets) { /* Avoid visiting the same socket twice when multiple links point to the same socket. */ - Array pushed_by_socket_id(tree.sockets().size(), false); - Stack sockets_to_check = sockets; + Array pushed_by_socket_id(tree.all_sockets().size(), false); + Stack 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 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 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 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(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(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()) { 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 -#include - -#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 +#include + +#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 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::create_from_treestore(BLI_mempool &treestore) +{ + /* Can't use `make_unique()` here because of private constructor. */ + std::unique_ptr 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(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 &group = elem_groups_.lookup_or_add_cb( + TreeStoreElemKey(elem), []() { return std::make_unique(); }); + 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.c deleted file mode 100644 index 89cc25b31e6..00000000000 --- a/source/blender/blenkernel/intern/paint.c +++ /dev/null @@ -1,2348 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright 2009 by Nicholas Bishop. All rights reserved. */ - -/** \file - * \ingroup bke - */ - -#include -#include - -#include "MEM_guardedalloc.h" - -#include "DNA_brush_types.h" -#include "DNA_gpencil_types.h" -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" -#include "DNA_modifier_types.h" -#include "DNA_object_types.h" -#include "DNA_scene_types.h" -#include "DNA_space_types.h" -#include "DNA_view3d_types.h" -#include "DNA_workspace_types.h" - -#include "BLI_bitmap.h" -#include "BLI_hash.h" -#include "BLI_listbase.h" -#include "BLI_math_vector.h" -#include "BLI_utildefines.h" - -#include "BLT_translation.h" - -#include "BKE_attribute.h" -#include "BKE_brush.h" -#include "BKE_ccg.h" -#include "BKE_colortools.h" -#include "BKE_context.h" -#include "BKE_crazyspace.h" -#include "BKE_deform.h" -#include "BKE_gpencil.h" -#include "BKE_idtype.h" -#include "BKE_image.h" -#include "BKE_key.h" -#include "BKE_lib_id.h" -#include "BKE_main.h" -#include "BKE_material.h" -#include "BKE_mesh.h" -#include "BKE_mesh_mapping.h" -#include "BKE_mesh_runtime.h" -#include "BKE_modifier.h" -#include "BKE_multires.h" -#include "BKE_object.h" -#include "BKE_paint.h" -#include "BKE_pbvh.h" -#include "BKE_subdiv_ccg.h" -#include "BKE_subsurf.h" - -#include "DEG_depsgraph.h" -#include "DEG_depsgraph_query.h" - -#include "RNA_enum_types.h" - -#include "BLO_read_write.h" - -#include "bmesh.h" - -static void palette_init_data(ID *id) -{ - Palette *palette = (Palette *)id; - - BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(palette, id)); - - /* Enable fake user by default. */ - id_fake_user_set(&palette->id); -} - -static void palette_copy_data(Main *UNUSED(bmain), - ID *id_dst, - const ID *id_src, - const int UNUSED(flag)) -{ - Palette *palette_dst = (Palette *)id_dst; - const Palette *palette_src = (const Palette *)id_src; - - BLI_duplicatelist(&palette_dst->colors, &palette_src->colors); -} - -static void palette_free_data(ID *id) -{ - Palette *palette = (Palette *)id; - - BLI_freelistN(&palette->colors); -} - -static void palette_blend_write(BlendWriter *writer, ID *id, const void *id_address) -{ - 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); - } -} - -static void palette_blend_read_data(BlendDataReader *reader, ID *id) -{ - Palette *palette = (Palette *)id; - BLO_read_list(reader, &palette->colors); -} - -static void palette_undo_preserve(BlendLibReader *UNUSED(reader), ID *id_new, ID *id_old) -{ - /* Whole Palette is preserved across undo-steps, and it has no extra pointer, simple. */ - /* 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); - 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, -}; - -static void paint_curve_copy_data(Main *UNUSED(bmain), - ID *id_dst, - const ID *id_src, - const int UNUSED(flag)) -{ - PaintCurve *paint_curve_dst = (PaintCurve *)id_dst; - 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); - } -} - -static void paint_curve_free_data(ID *id) -{ - PaintCurve *paint_curve = (PaintCurve *)id; - - MEM_SAFE_FREE(paint_curve->points); - paint_curve->tot_points = 0; -} - -static void paint_curve_blend_write(BlendWriter *writer, ID *id, const void *id_address) -{ - PaintCurve *pc = (PaintCurve *)id; - - BLO_write_id_struct(writer, PaintCurve, id_address, &pc->id); - BKE_id_blend_write(writer, &pc->id); - - BLO_write_struct_array(writer, PaintCurvePoint, pc->tot_points, pc->points); -} - -static void paint_curve_blend_read_data(BlendDataReader *reader, ID *id) -{ - PaintCurve *pc = (PaintCurve *)id; - BLO_read_data_address(reader, &pc->points); -} - -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, -}; - -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}; - -static ePaintOverlayControlFlags overlay_flags = 0; - -void BKE_paint_invalidate_overlay_tex(Scene *scene, ViewLayer *view_layer, const Tex *tex) -{ - Paint *p = BKE_paint_get_active(scene, view_layer); - if (!p) { - return; - } - - Brush *br = p->brush; - if (!br) { - return; - } - - if (br->mtex.tex == tex) { - overlay_flags |= PAINT_OVERLAY_INVALID_TEXTURE_PRIMARY; - } - if (br->mask_mtex.tex == tex) { - overlay_flags |= PAINT_OVERLAY_INVALID_TEXTURE_SECONDARY; - } -} - -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) { - return; - } - - Brush *br = p->brush; - if (br && br->curve == curve) { - overlay_flags |= PAINT_OVERLAY_INVALID_CURVE; - } -} - -void BKE_paint_invalidate_overlay_all(void) -{ - overlay_flags |= (PAINT_OVERLAY_INVALID_TEXTURE_SECONDARY | - PAINT_OVERLAY_INVALID_TEXTURE_PRIMARY | PAINT_OVERLAY_INVALID_CURVE); -} - -ePaintOverlayControlFlags BKE_paint_get_overlay_flags(void) -{ - return overlay_flags; -} - -void BKE_paint_set_overlay_override(eOverlayFlags flags) -{ - if (flags & BRUSH_OVERLAY_OVERRIDE_MASK) { - if (flags & BRUSH_OVERLAY_CURSOR_OVERRIDE_ON_STROKE) { - overlay_flags |= PAINT_OVERLAY_OVERRIDE_CURSOR; - } - if (flags & BRUSH_OVERLAY_PRIMARY_OVERRIDE_ON_STROKE) { - overlay_flags |= PAINT_OVERLAY_OVERRIDE_PRIMARY; - } - if (flags & BRUSH_OVERLAY_SECONDARY_OVERRIDE_ON_STROKE) { - overlay_flags |= PAINT_OVERLAY_OVERRIDE_SECONDARY; - } - } - else { - overlay_flags &= ~(PAINT_OVERRIDE_MASK); - } -} - -void BKE_paint_reset_overlay_invalid(ePaintOverlayControlFlags flag) -{ - overlay_flags &= ~(flag); -} - -bool BKE_paint_ensure_from_paintmode(Scene *sce, ePaintMode mode) -{ - ToolSettings *ts = sce->toolsettings; - Paint **paint_ptr = NULL; - /* 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; - - switch (mode) { - case PAINT_MODE_SCULPT: - paint_ptr = (Paint **)&ts->sculpt; - break; - case PAINT_MODE_VERTEX: - paint_ptr = (Paint **)&ts->vpaint; - break; - case PAINT_MODE_WEIGHT: - paint_ptr = (Paint **)&ts->wpaint; - break; - case PAINT_MODE_TEXTURE_2D: - case PAINT_MODE_TEXTURE_3D: - paint_tmp = (Paint *)&ts->imapaint; - paint_ptr = &paint_tmp; - break; - case PAINT_MODE_SCULPT_UV: - paint_ptr = (Paint **)&ts->uvsculpt; - break; - case PAINT_MODE_GPENCIL: - paint_ptr = (Paint **)&ts->gp_paint; - break; - case PAINT_MODE_VERTEX_GPENCIL: - paint_ptr = (Paint **)&ts->gp_vertexpaint; - break; - case PAINT_MODE_SCULPT_GPENCIL: - paint_ptr = (Paint **)&ts->gp_sculptpaint; - break; - case PAINT_MODE_WEIGHT_GPENCIL: - paint_ptr = (Paint **)&ts->gp_weightpaint; - break; - case PAINT_MODE_SCULPT_CURVES: - paint_ptr = (Paint **)&ts->curves_sculpt; - break; - case PAINT_MODE_INVALID: - break; - } - if (paint_ptr) { - BKE_paint_ensure(ts, paint_ptr); - return true; - } - return false; -} - -Paint *BKE_paint_get_active_from_paintmode(Scene *sce, ePaintMode mode) -{ - if (sce) { - ToolSettings *ts = sce->toolsettings; - - switch (mode) { - case PAINT_MODE_SCULPT: - return &ts->sculpt->paint; - case PAINT_MODE_VERTEX: - return &ts->vpaint->paint; - case PAINT_MODE_WEIGHT: - return &ts->wpaint->paint; - case PAINT_MODE_TEXTURE_2D: - case PAINT_MODE_TEXTURE_3D: - return &ts->imapaint.paint; - case PAINT_MODE_SCULPT_UV: - return &ts->uvsculpt->paint; - case PAINT_MODE_GPENCIL: - return &ts->gp_paint->paint; - case PAINT_MODE_VERTEX_GPENCIL: - return &ts->gp_vertexpaint->paint; - case PAINT_MODE_SCULPT_GPENCIL: - return &ts->gp_sculptpaint->paint; - case PAINT_MODE_WEIGHT_GPENCIL: - return &ts->gp_weightpaint->paint; - case PAINT_MODE_SCULPT_CURVES: - return &ts->curves_sculpt->paint; - case PAINT_MODE_INVALID: - return NULL; - default: - return &ts->imapaint.paint; - } - } - - return NULL; -} - -const EnumPropertyItem *BKE_paint_get_tool_enum_from_paintmode(ePaintMode mode) -{ - switch (mode) { - case PAINT_MODE_SCULPT: - return rna_enum_brush_sculpt_tool_items; - case PAINT_MODE_VERTEX: - return rna_enum_brush_vertex_tool_items; - case PAINT_MODE_WEIGHT: - return rna_enum_brush_weight_tool_items; - case PAINT_MODE_TEXTURE_2D: - case PAINT_MODE_TEXTURE_3D: - return rna_enum_brush_image_tool_items; - case PAINT_MODE_SCULPT_UV: - return rna_enum_brush_uv_sculpt_tool_items; - case PAINT_MODE_GPENCIL: - return rna_enum_brush_gpencil_types_items; - case PAINT_MODE_VERTEX_GPENCIL: - return rna_enum_brush_gpencil_vertex_types_items; - case PAINT_MODE_SCULPT_GPENCIL: - return rna_enum_brush_gpencil_sculpt_types_items; - case PAINT_MODE_WEIGHT_GPENCIL: - return rna_enum_brush_gpencil_weight_types_items; - case PAINT_MODE_SCULPT_CURVES: - return rna_enum_brush_curves_sculpt_tool_items; - case PAINT_MODE_INVALID: - break; - } - return NULL; -} - -const char *BKE_paint_get_tool_prop_id_from_paintmode(ePaintMode mode) -{ - switch (mode) { - case PAINT_MODE_SCULPT: - return "sculpt_tool"; - case PAINT_MODE_VERTEX: - return "vertex_tool"; - case PAINT_MODE_WEIGHT: - return "weight_tool"; - case PAINT_MODE_TEXTURE_2D: - case PAINT_MODE_TEXTURE_3D: - return "image_tool"; - case PAINT_MODE_SCULPT_UV: - return "uv_sculpt_tool"; - case PAINT_MODE_GPENCIL: - return "gpencil_tool"; - case PAINT_MODE_VERTEX_GPENCIL: - return "gpencil_vertex_tool"; - case PAINT_MODE_SCULPT_GPENCIL: - return "gpencil_sculpt_tool"; - case PAINT_MODE_WEIGHT_GPENCIL: - return "gpencil_weight_tool"; - case PAINT_MODE_SCULPT_CURVES: - return "curves_sculpt_tool"; - case PAINT_MODE_INVALID: - break; - } - - /* Invalid paint mode. */ - return NULL; -} - -Paint *BKE_paint_get_active(Scene *sce, ViewLayer *view_layer) -{ - if (sce && view_layer) { - ToolSettings *ts = sce->toolsettings; - - if (view_layer->basact && view_layer->basact->object) { - switch (view_layer->basact->object->mode) { - case OB_MODE_SCULPT: - return &ts->sculpt->paint; - case OB_MODE_VERTEX_PAINT: - return &ts->vpaint->paint; - case OB_MODE_WEIGHT_PAINT: - return &ts->wpaint->paint; - case OB_MODE_TEXTURE_PAINT: - return &ts->imapaint.paint; - case OB_MODE_PAINT_GPENCIL: - return &ts->gp_paint->paint; - case OB_MODE_VERTEX_GPENCIL: - return &ts->gp_vertexpaint->paint; - case OB_MODE_SCULPT_GPENCIL: - return &ts->gp_sculptpaint->paint; - case OB_MODE_WEIGHT_GPENCIL: - return &ts->gp_weightpaint->paint; - case OB_MODE_SCULPT_CURVES: - return &ts->curves_sculpt->paint; - case OB_MODE_EDIT: - return ts->uvsculpt ? &ts->uvsculpt->paint : NULL; - default: - break; - } - } - - /* default to image paint */ - return &ts->imapaint.paint; - } - - return NULL; -} - -Paint *BKE_paint_get_active_from_context(const bContext *C) -{ - Scene *sce = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - SpaceImage *sima; - - if (sce && view_layer) { - ToolSettings *ts = sce->toolsettings; - Object *obact = NULL; - - if (view_layer->basact && view_layer->basact->object) { - obact = view_layer->basact->object; - } - - if ((sima = CTX_wm_space_image(C)) != NULL) { - if (obact && obact->mode == OB_MODE_EDIT) { - if (sima->mode == SI_MODE_PAINT) { - return &ts->imapaint.paint; - } - if (sima->mode == SI_MODE_UV) { - return &ts->uvsculpt->paint; - } - } - else { - return &ts->imapaint.paint; - } - } - else { - return BKE_paint_get_active(sce, view_layer); - } - } - - return NULL; -} - -ePaintMode BKE_paintmode_get_active_from_context(const bContext *C) -{ - Scene *sce = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - SpaceImage *sima; - - if (sce && view_layer) { - Object *obact = NULL; - - if (view_layer->basact && view_layer->basact->object) { - obact = view_layer->basact->object; - } - - if ((sima = CTX_wm_space_image(C)) != NULL) { - if (obact && obact->mode == OB_MODE_EDIT) { - if (sima->mode == SI_MODE_PAINT) { - return PAINT_MODE_TEXTURE_2D; - } - if (sima->mode == SI_MODE_UV) { - return PAINT_MODE_SCULPT_UV; - } - } - else { - return PAINT_MODE_TEXTURE_2D; - } - } - else if (obact) { - switch (obact->mode) { - case OB_MODE_SCULPT: - return PAINT_MODE_SCULPT; - case OB_MODE_VERTEX_PAINT: - return PAINT_MODE_VERTEX; - case OB_MODE_WEIGHT_PAINT: - return PAINT_MODE_WEIGHT; - case OB_MODE_TEXTURE_PAINT: - return PAINT_MODE_TEXTURE_3D; - case OB_MODE_EDIT: - return PAINT_MODE_SCULPT_UV; - case OB_MODE_SCULPT_CURVES: - return PAINT_MODE_SCULPT_CURVES; - default: - return PAINT_MODE_TEXTURE_2D; - } - } - else { - /* default to image paint */ - return PAINT_MODE_TEXTURE_2D; - } - } - - return PAINT_MODE_INVALID; -} - -ePaintMode BKE_paintmode_get_from_tool(const struct bToolRef *tref) -{ - if (tref->space_type == SPACE_VIEW3D) { - switch (tref->mode) { - case CTX_MODE_SCULPT: - return PAINT_MODE_SCULPT; - case CTX_MODE_PAINT_VERTEX: - return PAINT_MODE_VERTEX; - case CTX_MODE_PAINT_WEIGHT: - return PAINT_MODE_WEIGHT; - case CTX_MODE_PAINT_GPENCIL: - return PAINT_MODE_GPENCIL; - case CTX_MODE_PAINT_TEXTURE: - return PAINT_MODE_TEXTURE_3D; - case CTX_MODE_VERTEX_GPENCIL: - return PAINT_MODE_VERTEX_GPENCIL; - case CTX_MODE_SCULPT_GPENCIL: - return PAINT_MODE_SCULPT_GPENCIL; - case CTX_MODE_WEIGHT_GPENCIL: - return PAINT_MODE_WEIGHT_GPENCIL; - case CTX_MODE_SCULPT_CURVES: - return PAINT_MODE_SCULPT_CURVES; - } - } - else if (tref->space_type == SPACE_IMAGE) { - switch (tref->mode) { - case SI_MODE_PAINT: - return PAINT_MODE_TEXTURE_2D; - case SI_MODE_UV: - return PAINT_MODE_SCULPT_UV; - } - } - - return PAINT_MODE_INVALID; -} - -Brush *BKE_paint_brush(Paint *p) -{ - return (Brush *)BKE_paint_brush_for_read((const Paint *)p); -} - -const Brush *BKE_paint_brush_for_read(const Paint *p) -{ - return p ? p->brush : NULL; -} - -void BKE_paint_brush_set(Paint *p, Brush *br) -{ - if (p) { - id_us_min((ID *)p->brush); - id_us_plus((ID *)br); - p->brush = br; - - BKE_paint_toolslots_brush_update(p); - } -} - -void BKE_paint_runtime_init(const ToolSettings *ts, Paint *paint) -{ - if (paint == &ts->imapaint.paint) { - paint->runtime.tool_offset = offsetof(Brush, imagepaint_tool); - paint->runtime.ob_mode = OB_MODE_TEXTURE_PAINT; - } - else if (ts->sculpt && paint == &ts->sculpt->paint) { - paint->runtime.tool_offset = offsetof(Brush, sculpt_tool); - paint->runtime.ob_mode = OB_MODE_SCULPT; - } - else if (ts->vpaint && paint == &ts->vpaint->paint) { - paint->runtime.tool_offset = offsetof(Brush, vertexpaint_tool); - paint->runtime.ob_mode = OB_MODE_VERTEX_PAINT; - } - else if (ts->wpaint && paint == &ts->wpaint->paint) { - paint->runtime.tool_offset = offsetof(Brush, weightpaint_tool); - paint->runtime.ob_mode = OB_MODE_WEIGHT_PAINT; - } - else if (ts->uvsculpt && paint == &ts->uvsculpt->paint) { - paint->runtime.tool_offset = offsetof(Brush, uv_sculpt_tool); - paint->runtime.ob_mode = OB_MODE_EDIT; - } - else if (ts->gp_paint && paint == &ts->gp_paint->paint) { - paint->runtime.tool_offset = offsetof(Brush, gpencil_tool); - paint->runtime.ob_mode = OB_MODE_PAINT_GPENCIL; - } - else if (ts->gp_vertexpaint && paint == &ts->gp_vertexpaint->paint) { - paint->runtime.tool_offset = offsetof(Brush, gpencil_vertex_tool); - paint->runtime.ob_mode = OB_MODE_VERTEX_GPENCIL; - } - else if (ts->gp_sculptpaint && paint == &ts->gp_sculptpaint->paint) { - paint->runtime.tool_offset = offsetof(Brush, gpencil_sculpt_tool); - paint->runtime.ob_mode = OB_MODE_SCULPT_GPENCIL; - } - else if (ts->gp_weightpaint && paint == &ts->gp_weightpaint->paint) { - paint->runtime.tool_offset = offsetof(Brush, gpencil_weight_tool); - paint->runtime.ob_mode = OB_MODE_WEIGHT_GPENCIL; - } - else if (ts->curves_sculpt && paint == &ts->curves_sculpt->paint) { - paint->runtime.tool_offset = offsetof(Brush, curves_sculpt_tool); - paint->runtime.ob_mode = OB_MODE_SCULPT_CURVES; - } - else { - BLI_assert_unreachable(); - } -} - -uint BKE_paint_get_brush_tool_offset_from_paintmode(const ePaintMode mode) -{ - switch (mode) { - case PAINT_MODE_TEXTURE_2D: - case PAINT_MODE_TEXTURE_3D: - return offsetof(Brush, imagepaint_tool); - case PAINT_MODE_SCULPT: - return offsetof(Brush, sculpt_tool); - case PAINT_MODE_VERTEX: - return offsetof(Brush, vertexpaint_tool); - case PAINT_MODE_WEIGHT: - return offsetof(Brush, weightpaint_tool); - case PAINT_MODE_SCULPT_UV: - return offsetof(Brush, uv_sculpt_tool); - case PAINT_MODE_GPENCIL: - return offsetof(Brush, gpencil_tool); - case PAINT_MODE_VERTEX_GPENCIL: - return offsetof(Brush, gpencil_vertex_tool); - case PAINT_MODE_SCULPT_GPENCIL: - return offsetof(Brush, gpencil_sculpt_tool); - case PAINT_MODE_WEIGHT_GPENCIL: - return offsetof(Brush, gpencil_weight_tool); - case PAINT_MODE_SCULPT_CURVES: - return offsetof(Brush, curves_sculpt_tool); - case PAINT_MODE_INVALID: - break; /* We don't use these yet. */ - } - return 0; -} - -PaintCurve *BKE_paint_curve_add(Main *bmain, const char *name) -{ - PaintCurve *pc; - - pc = BKE_id_new(bmain, ID_PC, name); - - return pc; -} - -Palette *BKE_paint_palette(Paint *p) -{ - return p ? p->palette : NULL; -} - -void BKE_paint_palette_set(Paint *p, Palette *palette) -{ - if (p) { - id_us_min((ID *)p->palette); - p->palette = palette; - id_us_plus((ID *)p->palette); - } -} - -void BKE_paint_curve_set(Brush *br, PaintCurve *pc) -{ - if (br) { - id_us_min((ID *)br->paint_curve); - br->paint_curve = pc; - id_us_plus((ID *)br->paint_curve); - } -} - -void BKE_paint_curve_clamp_endpoint_add_index(PaintCurve *pc, const int add_index) -{ - pc->add_index = (add_index || pc->tot_points == 1) ? (add_index + 1) : 0; -} - -void BKE_palette_color_remove(Palette *palette, PaletteColor *color) -{ - if (BLI_listbase_count_at_most(&palette->colors, palette->active_color) == - palette->active_color) { - palette->active_color--; - } - - BLI_remlink(&palette->colors, color); - - if (palette->active_color < 0 && !BLI_listbase_is_empty(&palette->colors)) { - palette->active_color = 0; - } - - MEM_freeN(color); -} - -void BKE_palette_clear(Palette *palette) -{ - BLI_freelistN(&palette->colors); - palette->active_color = 0; -} - -Palette *BKE_palette_add(Main *bmain, const char *name) -{ - Palette *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"); - BLI_addtail(&palette->colors, color); - return color; -} - -bool BKE_palette_is_empty(const struct Palette *palette) -{ - return BLI_listbase_is_empty(&palette->colors); -} - -/* helper function to sort using qsort */ -static int palettecolor_compare_hsv(const void *a1, const void *a2) -{ - const tPaletteColorHSV *ps1 = a1, *ps2 = a2; - - /* Hue */ - if (ps1->h > ps2->h) { - return 1; - } - if (ps1->h < ps2->h) { - return -1; - } - - /* Saturation. */ - if (ps1->s > ps2->s) { - return 1; - } - if (ps1->s < ps2->s) { - return -1; - } - - /* Value. */ - if (1.0f - ps1->v > 1.0f - ps2->v) { - return 1; - } - if (1.0f - ps1->v < 1.0f - ps2->v) { - return -1; - } - - return 0; -} - -/* helper function to sort using qsort */ -static int palettecolor_compare_svh(const void *a1, const void *a2) -{ - const tPaletteColorHSV *ps1 = a1, *ps2 = a2; - - /* Saturation. */ - if (ps1->s > ps2->s) { - return 1; - } - if (ps1->s < ps2->s) { - return -1; - } - - /* Value. */ - if (1.0f - ps1->v > 1.0f - ps2->v) { - return 1; - } - if (1.0f - ps1->v < 1.0f - ps2->v) { - return -1; - } - - /* Hue */ - if (ps1->h > ps2->h) { - return 1; - } - if (ps1->h < ps2->h) { - return -1; - } - - return 0; -} - -static int palettecolor_compare_vhs(const void *a1, const void *a2) -{ - const tPaletteColorHSV *ps1 = a1, *ps2 = a2; - - /* Value. */ - if (1.0f - ps1->v > 1.0f - ps2->v) { - return 1; - } - if (1.0f - ps1->v < 1.0f - ps2->v) { - return -1; - } - - /* Hue */ - if (ps1->h > ps2->h) { - return 1; - } - if (ps1->h < ps2->h) { - return -1; - } - - /* Saturation. */ - if (ps1->s > ps2->s) { - return 1; - } - if (ps1->s < ps2->s) { - return -1; - } - - return 0; -} - -static int palettecolor_compare_luminance(const void *a1, const void *a2) -{ - const tPaletteColorHSV *ps1 = a1, *ps2 = 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; - - if (lumi1 > lumi2) { - return -1; - } - if (lumi1 < lumi2) { - return 1; - } - - return 0; -} - -void BKE_palette_sort_hsv(tPaletteColorHSV *color_array, const int totcol) -{ - /* Sort by Hue, Saturation and Value. */ - qsort(color_array, totcol, sizeof(tPaletteColorHSV), palettecolor_compare_hsv); -} - -void BKE_palette_sort_svh(tPaletteColorHSV *color_array, const int totcol) -{ - /* Sort by Saturation, Value and Hue. */ - qsort(color_array, totcol, sizeof(tPaletteColorHSV), palettecolor_compare_svh); -} - -void BKE_palette_sort_vhs(tPaletteColorHSV *color_array, const int totcol) -{ - /* Sort by Saturation, Value and Hue. */ - qsort(color_array, totcol, sizeof(tPaletteColorHSV), palettecolor_compare_vhs); -} - -void BKE_palette_sort_luminance(tPaletteColorHSV *color_array, const int totcol) -{ - /* Sort by Luminance (calculated with the average, enough for sorting). */ - qsort(color_array, totcol, sizeof(tPaletteColorHSV), palettecolor_compare_luminance); -} - -bool BKE_palette_from_hash(Main *bmain, GHash *color_table, const char *name, const bool linear) -{ - tPaletteColorHSV *color_array = NULL; - tPaletteColorHSV *col_elm = NULL; - bool done = false; - - const int totpal = BLI_ghash_len(color_table); - - if (totpal > 0) { - color_array = MEM_calloc_arrayN(totpal, sizeof(tPaletteColorHSV), __func__); - /* Put all colors in an array. */ - GHashIterator gh_iter; - int t = 0; - GHASH_ITER (gh_iter, color_table) { - const uint col = POINTER_AS_INT(BLI_ghashIterator_getValue(&gh_iter)); - float r, g, b; - float h, s, v; - cpack_to_rgb(col, &r, &g, &b); - rgb_to_hsv(r, g, b, &h, &s, &v); - - col_elm = &color_array[t]; - col_elm->rgb[0] = r; - col_elm->rgb[1] = g; - col_elm->rgb[2] = b; - col_elm->h = h; - col_elm->s = s; - col_elm->v = v; - t++; - } - } - - /* Create the Palette. */ - if (totpal > 0) { - /* Sort by Hue and saturation. */ - BKE_palette_sort_hsv(color_array, totpal); - - Palette *palette = BKE_palette_add(bmain, name); - if (palette) { - for (int i = 0; i < totpal; i++) { - col_elm = &color_array[i]; - PaletteColor *palcol = BKE_palette_color_add(palette); - if (palcol) { - copy_v3_v3(palcol->rgb, col_elm->rgb); - if (linear) { - linearrgb_to_srgb_v3_v3(palcol->rgb, palcol->rgb); - } - } - } - done = true; - } - } - else { - done = false; - } - - if (totpal > 0) { - MEM_SAFE_FREE(color_array); - } - - return done; -} - -bool BKE_paint_select_face_test(Object *ob) -{ - return ((ob != NULL) && (ob->type == OB_MESH) && (ob->data != NULL) && - (((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) && - (((Mesh *)ob->data)->editflag & ME_EDIT_PAINT_VERT_SEL) && - (ob->mode & OB_MODE_WEIGHT_PAINT || ob->mode & OB_MODE_VERTEX_PAINT)); -} - -bool BKE_paint_select_elem_test(Object *ob) -{ - return (BKE_paint_select_vert_test(ob) || BKE_paint_select_face_test(ob)); -} - -void BKE_paint_cavity_curve_preset(Paint *p, int preset) -{ - CurveMapping *cumap = NULL; - CurveMap *cuma = NULL; - - if (!p->cavity_curve) { - p->cavity_curve = BKE_curvemapping_add(1, 0, 0, 1, 1); - } - cumap = p->cavity_curve; - cumap->flag &= ~CUMA_EXTEND_EXTRAPOLATE; - cumap->preset = preset; - - cuma = cumap->cm; - BKE_curvemap_reset(cuma, &cumap->clipr, cumap->preset, CURVEMAP_SLOPE_POSITIVE); - BKE_curvemapping_changed(cumap, false); -} - -eObjectMode BKE_paint_object_mode_from_paintmode(ePaintMode mode) -{ - switch (mode) { - case PAINT_MODE_SCULPT: - return OB_MODE_SCULPT; - case PAINT_MODE_VERTEX: - return OB_MODE_VERTEX_PAINT; - case PAINT_MODE_WEIGHT: - return OB_MODE_WEIGHT_PAINT; - case PAINT_MODE_TEXTURE_2D: - case PAINT_MODE_TEXTURE_3D: - return OB_MODE_TEXTURE_PAINT; - case PAINT_MODE_SCULPT_UV: - return OB_MODE_EDIT; - case PAINT_MODE_INVALID: - default: - return 0; - } -} - -bool BKE_paint_ensure(ToolSettings *ts, struct Paint **r_paint) -{ - Paint *paint = NULL; - if (*r_paint) { - /* Tool offset should never be 0 for initialized paint settings, so it's a reliable way to - * check if already initialized. */ - if ((*r_paint)->runtime.tool_offset == 0) { - /* Currently only image painting is initialized this way, others have to be allocated. */ - BLI_assert(ELEM(*r_paint, (Paint *)&ts->imapaint)); - - BKE_paint_runtime_init(ts, *r_paint); - } - else { - BLI_assert(ELEM(*r_paint, - /* Cast is annoying, but prevent NULL-pointer access. */ - (Paint *)ts->gp_paint, - (Paint *)ts->gp_vertexpaint, - (Paint *)ts->gp_sculptpaint, - (Paint *)ts->gp_weightpaint, - (Paint *)ts->sculpt, - (Paint *)ts->vpaint, - (Paint *)ts->wpaint, - (Paint *)ts->uvsculpt, - (Paint *)ts->curves_sculpt, - (Paint *)&ts->imapaint)); -#ifdef DEBUG - struct 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); - BLI_assert(paint_test.runtime.ob_mode == (*r_paint)->runtime.ob_mode); - BLI_assert(paint_test.runtime.tool_offset == (*r_paint)->runtime.tool_offset); -#endif - } - return true; - } - - if (((VPaint **)r_paint == &ts->vpaint) || ((VPaint **)r_paint == &ts->wpaint)) { - VPaint *data = MEM_callocN(sizeof(*data), __func__); - paint = &data->paint; - } - else if ((Sculpt **)r_paint == &ts->sculpt) { - Sculpt *data = MEM_callocN(sizeof(*data), __func__); - paint = &data->paint; - - /* Turn on X plane mirror symmetry by default */ - paint->symmetry_flags |= PAINT_SYMM_X; - - /* 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__); - paint = &data->paint; - } - else if ((GpVertexPaint **)r_paint == &ts->gp_vertexpaint) { - GpVertexPaint *data = MEM_callocN(sizeof(*data), __func__); - paint = &data->paint; - } - else if ((GpSculptPaint **)r_paint == &ts->gp_sculptpaint) { - GpSculptPaint *data = MEM_callocN(sizeof(*data), __func__); - paint = &data->paint; - } - else if ((GpWeightPaint **)r_paint == &ts->gp_weightpaint) { - GpWeightPaint *data = MEM_callocN(sizeof(*data), __func__); - paint = &data->paint; - } - else if ((UvSculpt **)r_paint == &ts->uvsculpt) { - UvSculpt *data = MEM_callocN(sizeof(*data), __func__); - paint = &data->paint; - } - else if ((CurvesSculpt **)r_paint == &ts->curves_sculpt) { - CurvesSculpt *data = MEM_callocN(sizeof(*data), __func__); - paint = &data->paint; - } - else if (*r_paint == &ts->imapaint.paint) { - paint = &ts->imapaint.paint; - } - - paint->flags |= PAINT_SHOW_BRUSH; - - *r_paint = paint; - - BKE_paint_runtime_init(ts, paint); - - return false; -} - -void BKE_paint_init(Main *bmain, Scene *sce, ePaintMode mode, const char col[3]) -{ - UnifiedPaintSettings *ups = &sce->toolsettings->unified_paint_settings; - Paint *paint = BKE_paint_get_active_from_paintmode(sce, mode); - - BKE_paint_ensure_from_paintmode(sce, mode); - - /* If there's no brush, create one */ - if (PAINT_MODE_HAS_BRUSH(mode)) { - Brush *brush = BKE_paint_brush(paint); - if (brush == NULL) { - 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 */ - } - BKE_paint_brush_set(paint, brush); - } - } - - memcpy(paint->paint_cursor_col, col, 3); - paint->paint_cursor_col[3] = 128; - ups->last_stroke_valid = false; - zero_v3(ups->average_stroke_accum); - ups->average_stroke_counter = 0; - if (!paint->cavity_curve) { - BKE_paint_cavity_curve_preset(paint, CURVE_PRESET_LINE); - } -} - -void BKE_paint_free(Paint *paint) -{ - BKE_curvemapping_free(paint->cavity_curve); - MEM_SAFE_FREE(paint->tool_slots); -} - -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); - - 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) { - for (int i = 0; i < tar->tool_slots_len; i++) { - id_us_plus((ID *)tar->tool_slots[i].brush); - } - } - } -} - -void BKE_paint_stroke_get_average(Scene *scene, Object *ob, float stroke[3]) -{ - UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; - if (ups->last_stroke_valid && ups->average_stroke_counter > 0) { - float fac = 1.0f / ups->average_stroke_counter; - mul_v3_v3fl(stroke, ups->average_stroke_accum, fac); - } - else { - copy_v3_v3(stroke, ob->obmat[3]); - } -} - -void BKE_paint_blend_write(BlendWriter *writer, Paint *p) -{ - if (p->cavity_curve) { - BKE_curvemapping_blend_write(writer, p->cavity_curve); - } - BLO_write_struct_array(writer, PaintToolSlot, p->tool_slots_len, p->tool_slots); -} - -void BKE_paint_blend_read_data(BlendDataReader *reader, const Scene *scene, Paint *p) -{ - if (p->num_input_samples < 1) { - p->num_input_samples = 1; - } - - BLO_read_data_address(reader, &p->cavity_curve); - if (p->cavity_curve) { - BKE_curvemapping_blend_read(reader, p->cavity_curve); - } - else { - BKE_paint_cavity_curve_preset(p, CURVE_PRESET_LINE); - } - - BLO_read_data_address(reader, &p->tool_slots); - - /* Workaround for invalid data written in older versions. */ - 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"); - } - - BKE_paint_runtime_init(scene->toolsettings, p); -} - -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) { - 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; - - BKE_paint_runtime_init(sce->toolsettings, p); - } -} - -bool paint_is_face_hidden(const MLoopTri *lt, const MVert *mvert, const MLoop *mloop) -{ - 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)); -} - -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 */ - 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) || - BLI_BITMAP_TEST(grid_hidden, (y + 1) * gridsize + x)); -} - -bool paint_is_bmesh_face_hidden(BMFace *f) -{ - BMLoop *l_iter; - BMLoop *l_first; - - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - if (BM_elem_flag_test(l_iter->v, BM_ELEM_HIDDEN)) { - return true; - } - } while ((l_iter = l_iter->next) != l_first); - - return false; -} - -float paint_grid_paint_mask(const GridPaintMask *gpm, uint level, uint x, uint y) -{ - int factor = BKE_ccg_factor(level, gpm->level); - int gridsize = BKE_ccg_gridsize(gpm->level); - - return gpm->data[(y * factor) * gridsize + (x * factor)]; -} - -/* threshold to move before updating the brush rotation */ -#define RAKE_THRESHHOLD 20 - -void paint_update_brush_rake_rotation(UnifiedPaintSettings *ups, Brush *brush, float rotation) -{ - if (brush->mtex.brush_angle_mode & MTEX_ANGLE_RAKE) { - ups->brush_rotation = rotation; - } - else { - ups->brush_rotation = 0.0f; - } - - if (brush->mask_mtex.brush_angle_mode & MTEX_ANGLE_RAKE) { - ups->brush_rotation_sec = rotation; - } - else { - ups->brush_rotation_sec = 0.0f; - } -} - -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)) { - const float r = RAKE_THRESHHOLD; - float rotation; - - float dpos[2]; - sub_v2_v2v2(dpos, ups->last_rake, mouse_pos); - - if (len_squared_v2(dpos) >= r * r) { - rotation = atan2f(dpos[0], dpos[1]); - - copy_v2_v2(ups->last_rake, mouse_pos); - - ups->last_rake_angle = rotation; - - 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 */ - else { - paint_update_brush_rake_rotation(ups, brush, ups->last_rake_angle); - ok = false; - } - } - else { - ups->brush_rotation = ups->brush_rotation_sec = 0.0f; - ok = true; - } - return ok; -} - -void BKE_sculptsession_free_deformMats(SculptSession *ss) -{ - MEM_SAFE_FREE(ss->orig_cos); - MEM_SAFE_FREE(ss->deform_cos); - MEM_SAFE_FREE(ss->deform_imats); -} - -void BKE_sculptsession_free_vwpaint_data(struct SculptSession *ss) -{ - struct SculptVertexPaintGeomMap *gmap = NULL; - if (ss->mode_type == OB_MODE_VERTEX_PAINT) { - gmap = &ss->mode.vpaint.gmap; - } - else if (ss->mode_type == OB_MODE_WEIGHT_PAINT) { - gmap = &ss->mode.wpaint.gmap; - - MEM_SAFE_FREE(ss->mode.wpaint.alpha_weight); - 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; - } - } - else { - return; - } - MEM_SAFE_FREE(gmap->vert_to_loop); - MEM_SAFE_FREE(gmap->vert_map_mem); - MEM_SAFE_FREE(gmap->vert_to_poly); - MEM_SAFE_FREE(gmap->poly_map_mem); -} - -/** - * Write out the sculpt dynamic-topology #BMesh to the #Mesh. - */ -static void sculptsession_bm_to_me_update_data_only(Object *ob, bool reorder) -{ - SculptSession *ss = ob->sculpt; - - if (ss->bm) { - if (ob->data) { - BMIter iter; - BMFace *efa; - BM_ITER_MESH (efa, &iter, ss->bm, BM_FACES_OF_MESH) { - BM_elem_flag_set(efa, BM_ELEM_SMOOTH, ss->bm_smooth_shading); - } - 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, - })); - } - } -} - -void BKE_sculptsession_bm_to_me(Object *ob, bool reorder) -{ - if (ob && ob->sculpt) { - sculptsession_bm_to_me_update_data_only(ob, reorder); - - /* Ensure the objects evaluated mesh doesn't hold onto arrays - * now realloc'd in the mesh T34473. */ - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - } -} - -static void sculptsession_free_pbvh(Object *object) -{ - SculptSession *ss = object->sculpt; - - if (!ss) { - return; - } - - if (ss->pbvh) { - BKE_pbvh_free(ss->pbvh); - ss->pbvh = NULL; - } - - MEM_SAFE_FREE(ss->pmap); - MEM_SAFE_FREE(ss->pmap_mem); - - MEM_SAFE_FREE(ss->epmap); - MEM_SAFE_FREE(ss->epmap_mem); - - 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->vertex_info.connected_component); - MEM_SAFE_FREE(ss->vertex_info.boundary); - - MEM_SAFE_FREE(ss->fake_neighbors.fake_neighbor_index); -} - -void BKE_sculptsession_bm_to_me_for_render(Object *object) -{ - if (object && object->sculpt) { - if (object->sculpt->bm) { - /* Ensure no points to old arrays are stored in DM - * - * Apparently, we could not use DEG_id_tag_update - * here because this will lead to the while object - * surface to disappear, so we'll release DM in place. - */ - BKE_object_free_derived_caches(object); - - sculptsession_bm_to_me_update_data_only(object, false); - - /* In contrast with sculptsession_bm_to_me no need in - * DAG tag update here - derived mesh was freed and - * old pointers are nowhere stored. - */ - } - } -} - -void BKE_sculptsession_free(Object *ob) -{ - if (ob && ob->sculpt) { - SculptSession *ss = ob->sculpt; - - if (ss->bm) { - BKE_sculptsession_bm_to_me(ob, true); - BM_mesh_free(ss->bm); - } - - sculptsession_free_pbvh(ob); - - MEM_SAFE_FREE(ss->pmap); - MEM_SAFE_FREE(ss->pmap_mem); - - MEM_SAFE_FREE(ss->epmap); - MEM_SAFE_FREE(ss->epmap_mem); - - MEM_SAFE_FREE(ss->vemap); - MEM_SAFE_FREE(ss->vemap_mem); - - if (ss->bm_log) { - BM_log_free(ss->bm_log); - } - - if (ss->tex_pool) { - BKE_image_pool_free(ss->tex_pool); - } - - MEM_SAFE_FREE(ss->orig_cos); - MEM_SAFE_FREE(ss->deform_cos); - MEM_SAFE_FREE(ss->deform_imats); - - if (ss->pose_ik_chain_preview) { - for (int i = 0; i < ss->pose_ik_chain_preview->tot_segments; i++) { - MEM_SAFE_FREE(ss->pose_ik_chain_preview->segments[i].weights); - } - MEM_SAFE_FREE(ss->pose_ik_chain_preview->segments); - MEM_SAFE_FREE(ss->pose_ik_chain_preview); - } - - if (ss->boundary_preview) { - MEM_SAFE_FREE(ss->boundary_preview->vertices); - MEM_SAFE_FREE(ss->boundary_preview->edges); - MEM_SAFE_FREE(ss->boundary_preview->distance); - MEM_SAFE_FREE(ss->boundary_preview->edit_info); - MEM_SAFE_FREE(ss->boundary_preview); - } - - BKE_sculptsession_free_vwpaint_data(ob->sculpt); - - MEM_SAFE_FREE(ss->last_paint_canvas_key); - - MEM_freeN(ss); - - ob->sculpt = NULL; - } -} - -MultiresModifierData *BKE_sculpt_multires_active(const Scene *scene, Object *ob) -{ - Mesh *me = (Mesh *)ob->data; - ModifierData *md; - VirtualModifierData virtualModifierData; - - if (ob->sculpt && ob->sculpt->bm) { - /* can't combine multires and dynamic topology */ - return NULL; - } - - if (!CustomData_get_layer(&me->ldata, CD_MDISPS)) { - /* multires can't work without displacement layer */ - return NULL; - } - - /* 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; - } - - for (md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData); md; md = md->next) { - if (md->type == eModifierType_Multires) { - MultiresModifierData *mmd = (MultiresModifierData *)md; - - if (!BKE_modifier_is_enabled(scene, md, eModifierMode_Realtime)) { - continue; - } - - if (mmd->sculptlvl > 0 && !(mmd->flags & eMultiresModifierFlag_UseSculptBaseMesh)) { - return mmd; - } - - return NULL; - } - } - - return NULL; -} - -/* Checks if there are any supported deformation modifiers active */ -static bool sculpt_modifiers_active(Scene *scene, Sculpt *sd, Object *ob) -{ - ModifierData *md; - Mesh *me = (Mesh *)ob->data; - VirtualModifierData virtualModifierData; - - if (ob->sculpt->bm || BKE_sculpt_multires_active(scene, ob)) { - return false; - } - - /* 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 */ - for (; md; md = md->next) { - const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); - if (!BKE_modifier_is_enabled(scene, md, eModifierMode_Realtime)) { - continue; - } - if (md->type == eModifierType_Multires && (ob->mode & OB_MODE_SCULPT)) { - MultiresModifierData *mmd = (MultiresModifierData *)md; - if (!(mmd->flags & eMultiresModifierFlag_UseSculptBaseMesh)) { - continue; - } - } - if (md->type == eModifierType_ShapeKey) { - continue; - } - - if (mti->type == eModifierTypeType_OnlyDeform) { - return true; - } - if ((sd->flags & SCULPT_ONLY_DEFORM) == 0) { - return true; - } - } - - return false; -} - -/** - * \param need_mask: So that the evaluated mesh that is returned has mask data. - */ -static void sculpt_update_object(Depsgraph *depsgraph, - Object *ob, - Mesh *me_eval, - bool need_pmap, - bool need_mask, - 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); - const bool use_face_sets = (ob->mode & OB_MODE_SCULPT) != 0; - - ss->depsgraph = depsgraph; - - ss->deform_modifiers_active = sculpt_modifiers_active(scene, sd, ob); - ss->show_mask = (sd->flags & SCULPT_HIDE_MASK) == 0; - ss->show_face_sets = (sd->flags & SCULPT_HIDE_FACE_SETS) == 0; - - ss->building_vp_handle = false; - - 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; - - /* NOTE: Weight pPaint require mesh info for loop lookup, but it never uses multires code path, - * so no extra checks is needed here. */ - if (mmd) { - ss->multires.active = true; - ss->multires.modifier = mmd; - ss->multires.level = mmd->sculptlvl; - ss->totvert = me_eval->totvert; - ss->totpoly = me_eval->totpoly; - ss->totfaces = me->totpoly; - - /* 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; - } - 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->multires.active = false; - ss->multires.modifier = NULL; - ss->multires.level = 0; - ss->vmask = 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; - } - else { - ss->mcol = layer->data; - } - - ss->vcol_domain = domain; - ss->vcol_type = layer->type; - } - else { - ss->vcol = NULL; - ss->mcol = NULL; - - ss->vcol_type = -1; - ss->vcol_domain = ATTR_DOMAIN_NUM; - } - } - - /* 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); - } - else { - ss->face_sets = NULL; - } - - ss->subdiv_ccg = me_eval->runtime.subdiv_ccg; - - PBVH *pbvh = BKE_sculpt_object_pbvh_ensure(depsgraph, ob); - BLI_assert(pbvh == ss->pbvh); - UNUSED_VARS_NDEBUG(pbvh); - - BKE_pbvh_subdiv_cgg_set(ss->pbvh, ss->subdiv_ccg); - BKE_pbvh_face_sets_set(ss->pbvh, ss->face_sets); - - BKE_pbvh_face_sets_color_set(ss->pbvh, me->face_sets_color_seed, me->face_sets_color_default); - - 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); - - if (ss->pbvh) { - BKE_pbvh_pmap_set(ss->pbvh, ss->pmap); - } - } - - pbvh_show_mask_set(ss->pbvh, ss->show_mask); - pbvh_show_face_sets_set(ss->pbvh, ss->show_face_sets); - - if (ss->deform_modifiers_active) { - if (!ss->orig_cos) { - 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_crazyspace_build_sculpt(depsgraph, scene, ob, &ss->deform_imats, &ss->deform_cos); - BKE_pbvh_vert_coords_apply(ss->pbvh, ss->deform_cos, me->totvert); - - for (a = 0; a < me->totvert; a++) { - invert_m3(ss->deform_imats[a]); - } - } - } - else { - BKE_sculptsession_free_deformMats(ss); - } - - if (ss->shapekey_active != NULL && ss->deform_cos == NULL) { - 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) { - float(*vertCos)[3] = BKE_keyblock_convert_to_vertcos(ob, ss->shapekey_active); - - if (vertCos) { - if (!pbvh_deformed) { - /* apply shape keys coordinates to PBVH */ - BKE_pbvh_vert_coords_apply(ss->pbvh, vertCos, me->totvert); - } - if (ss->deform_cos == NULL) { - ss->deform_cos = vertCos; - } - if (vertCos != ss->deform_cos) { - MEM_freeN(vertCos); - } - } - } - } - - if (is_paint_tool) { - /* - * We should rebuild the PBVH_pixels when painting canvas changes. - * - * The relevant changes are stored/encoded in the paint canvas key. - * These include the active uv map, and resolutions. - */ - 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 || - !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; - BKE_pbvh_mark_rebuild_pixels(ss->pbvh); - } - else { - MEM_freeN(paint_canvas_key); - } - } - - /* We could be more precise when we have access to the active tool. */ - const bool use_paint_slots = (ob->mode & OB_MODE_SCULPT) != 0; - if (use_paint_slots) { - BKE_texpaint_slots_refresh_object(scene, ob); - } - } -} - -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) -{ - /* Update before mesh evaluation in the dependency graph. */ - SculptSession *ss = ob_eval->sculpt; - - if (ss && ss->building_vp_handle == false) { - if (!ss->cache && !ss->filter_cache && !ss->expand_cache) { - /* We free pbvh on changes, except in the middle of drawing a stroke - * since it can't deal with changing PVBH node organization, we hope - * topology does not change in the meantime .. weak. */ - sculptsession_free_pbvh(ob_eval); - - BKE_sculptsession_free_deformMats(ob_eval->sculpt); - - /* In vertex/weight paint, force maps to be rebuilt. */ - BKE_sculptsession_free_vwpaint_data(ob_eval->sculpt); - } - else { - PBVHNode **nodes; - int n, totnode; - - BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); - - for (n = 0; n < totnode; n++) { - BKE_pbvh_node_mark_update(nodes[n]); - } - - 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) -{ - /* 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); -} - -void BKE_sculpt_color_layer_create_if_needed(struct Object *object) -{ - Mesh *orig_me = BKE_object_get_original_mesh(object); - - int types[] = {CD_PROP_COLOR, CD_PROP_BYTE_COLOR}; - bool has_color = false; - - for (int i = 0; i < ARRAY_SIZE(types); i++) { - has_color = CustomData_has_layer(&orig_me->vdata, types[i]) || - CustomData_has_layer(&orig_me->ldata, types[i]); - - if (has_color) { - break; - } - } - - if (has_color) { - return; - } - - CustomData_add_layer(&orig_me->vdata, CD_PROP_COLOR, CD_DEFAULT, NULL, 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_id_attributes_active_color_set(&orig_me->id, layer); - DEG_id_tag_update(&orig_me->id, ID_RECALC_GEOMETRY_ALL_MODES); - - if (object->sculpt && object->sculpt->pbvh) { - BKE_pbvh_update_active_vcol(object->sculpt->pbvh, orig_me); - } -} - -void BKE_sculpt_update_object_for_edit( - 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); -} - -int BKE_sculpt_mask_layers_ensure(Object *ob, MultiresModifierData *mmd) -{ - const float *paint_mask; - Mesh *me = ob->data; - int ret = 0; - - paint_mask = CustomData_get_layer(&me->vdata, CD_PAINT_MASK); - - /* if multires is active, create a grid paint mask layer if there - * isn't one already */ - if (mmd && !CustomData_has_layer(&me->ldata, CD_GRID_PAINT_MASK)) { - GridPaintMask *gmask; - int level = max_ii(1, mmd->sculptlvl); - int gridsize = BKE_ccg_gridsize(level); - int gridarea = gridsize * gridsize; - int i, j; - - gmask = CustomData_add_layer(&me->ldata, CD_GRID_PAINT_MASK, CD_CALLOC, NULL, 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"); - } - - /* 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]; - float avg = 0; - - /* mask center */ - for (j = 0; j < p->totloop; j++) { - const MLoop *l = &me->mloop[p->loopstart + j]; - avg += paint_mask[l->v]; - } - avg /= (float)p->totloop; - - /* 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); - - gpm->data[0] = avg; - gpm->data[1] = (paint_mask[l->v] + paint_mask[next->v]) * 0.5f; - gpm->data[2] = (paint_mask[l->v] + paint_mask[prev->v]) * 0.5f; - gpm->data[3] = paint_mask[l->v]; - } - } - } - - ret |= SCULPT_MASK_LAYER_CALC_LOOP; - } - - /* 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); - ret |= SCULPT_MASK_LAYER_CALC_VERT; - } - - return ret; -} - -void BKE_sculpt_toolsettings_data_ensure(struct Scene *scene) -{ - BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->sculpt); - - Sculpt *sd = scene->toolsettings->sculpt; - if (!sd->detail_size) { - sd->detail_size = 12; - } - if (!sd->detail_percent) { - sd->detail_percent = 25; - } - if (sd->constant_detail == 0.0f) { - sd->constant_detail = 3.0f; - } - - /* Set sane default tiling offsets */ - if (!sd->paint.tile_offset[0]) { - sd->paint.tile_offset[0] = 1.0f; - } - if (!sd->paint.tile_offset[1]) { - sd->paint.tile_offset[1] = 1.0f; - } - if (!sd->paint.tile_offset[2]) { - sd->paint.tile_offset[2] = 1.0f; - } -} - -static bool check_sculpt_object_deformed(Object *object, const bool for_construction) -{ - bool deformed = false; - - /* 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. - */ - deformed |= object->sculpt->deform_modifiers_active; - - if (for_construction) { - deformed |= object->sculpt->shapekey_active != NULL; - } - 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. - */ - 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) -{ - 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) { - return; - } - - if (!subdiv_ccg) { - return; - } - - 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); - - /* 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 - * visible. */ - if (is_hidden) { - BKE_subdiv_ccg_grid_hidden_ensure(subdiv_ccg, i); - } - - BLI_bitmap *gh = subdiv_ccg->grid_hidden[i]; - if (gh) { - BLI_bitmap_set_all(gh, is_hidden, key.grid_area); - } - } -} - -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) -{ - Mesh *mesh = BKE_mesh_from_object(object); - MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, object); - - BLI_assert(object->mode == OB_MODE_SCULPT); - - /* 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); - - /* 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); - pbvh_show_mask_set(pbvh, ob->sculpt->show_mask); - pbvh_show_face_sets_set(pbvh, false); - return pbvh; -} - -static PBVH *build_pbvh_from_regular_mesh(Object *ob, Mesh *me_eval_deform, bool respect_hide) -{ - 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(); - BKE_pbvh_respect_hide_set(pbvh, respect_hide); - - MLoopTri *looptri = MEM_malloc_arrayN(looptris_num, sizeof(*looptri), __func__); - - BKE_mesh_recalc_looptri(me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, looptri); - - BKE_sculpt_sync_face_set_visibility(me, NULL); - - BKE_pbvh_build_mesh(pbvh, - me, - me->mpoly, - me->mloop, - me->mvert, - me->totvert, - &me->vdata, - &me->ldata, - &me->pdata, - looptri, - looptris_num); - - pbvh_show_mask_set(pbvh, ob->sculpt->show_mask); - 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) { - int totvert; - float(*v_cos)[3] = BKE_mesh_vert_coords_alloc(me_eval_deform, &totvert); - BKE_pbvh_vert_coords_apply(pbvh, v_cos, totvert); - MEM_freeN(v_cos); - } - - return pbvh; -} - -static PBVH *build_pbvh_from_ccg(Object *ob, SubdivCCG *subdiv_ccg, bool respect_hide) -{ - CCGKey key; - BKE_subdiv_ccg_key_top_level(&key, subdiv_ccg); - PBVH *pbvh = BKE_pbvh_new(); - 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_pbvh_build_grids(pbvh, - subdiv_ccg->grids, - subdiv_ccg->num_grids, - &key, - (void **)subdiv_ccg->grid_faces, - subdiv_ccg->grid_flag_mats, - subdiv_ccg->grid_hidden); - pbvh_show_mask_set(pbvh, ob->sculpt->show_mask); - pbvh_show_face_sets_set(pbvh, ob->sculpt->show_face_sets); - return pbvh; -} - -PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob) -{ - if (ob == NULL || ob->sculpt == NULL) { - return NULL; - } - - 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; - } - } - - PBVH *pbvh = ob->sculpt->pbvh; - if (pbvh != NULL) { - /* 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; - SubdivCCG *subdiv_ccg = mesh_eval->runtime.subdiv_ccg; - if (subdiv_ccg != NULL) { - BKE_sculpt_bvh_update_from_ccg(pbvh, subdiv_ccg); - } - } - - BKE_pbvh_update_active_vcol(pbvh, BKE_object_get_original_mesh(ob)); - BKE_pbvh_pmap_set(pbvh, ob->sculpt->pmap); - - return pbvh; - } - - if (ob->sculpt->bm != NULL) { - /* 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) { - pbvh = build_pbvh_from_ccg(ob, mesh_eval->runtime.subdiv_ccg, respect_hide); - } - else if (ob->type == OB_MESH) { - Mesh *me_eval_deform = object_eval->runtime.mesh_deform_eval; - pbvh = build_pbvh_from_regular_mesh(ob, me_eval_deform, respect_hide); - } - } - - BKE_pbvh_pmap_set(pbvh, ob->sculpt->pmap); - - ob->sculpt->pbvh = pbvh; - return pbvh; -} - -void BKE_sculpt_bvh_update_from_ccg(PBVH *pbvh, SubdivCCG *subdiv_ccg) -{ - BKE_pbvh_grids_update(pbvh, - subdiv_ccg->grids, - (void **)subdiv_ccg->grid_faces, - subdiv_ccg->grid_flag_mats, - subdiv_ccg->grid_hidden); -} - -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) { - return false; - } - - if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) { - /* Regular mesh only draws from PBVH without modifiers and shape keys. */ - - return !(ss->shapekey_active || ss->deform_modifiers_active); - } - - /* Multires and dyntopo always draw directly from the PBVH. */ - return true; -} - -/* Returns the Face Set random color for rendering in the overlay given its ID and a color seed. */ -#define GOLDEN_RATIO_CONJUGATE 0.618033988749895f -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)); - 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); - hsv_to_rgb(random_mod_hue, - 0.6f + (random_mod_sat * 0.25f), - 1.0f - (random_mod_val * 0.35f), - &rgba[0], - &rgba[1], - &rgba[2]); - rgba_float_to_uchar(r_color, rgba); -} diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc new file mode 100644 index 00000000000..0181c6e7eac --- /dev/null +++ b/source/blender/blenkernel/intern/paint.cc @@ -0,0 +1,2887 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2009 by Nicholas Bishop. All rights reserved. */ + +/** \file + * \ingroup bke + */ + +#include +#include + +#include "MEM_guardedalloc.h" + +#include "DNA_brush_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_space_types.h" +#include "DNA_view3d_types.h" +#include "DNA_workspace_types.h" + +#include "BLI_bitmap.h" +#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" +#include "BKE_context.h" +#include "BKE_crazyspace.h" +#include "BKE_deform.h" +#include "BKE_gpencil.h" +#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" +#include "BKE_mesh.h" +#include "BKE_mesh_mapping.h" +#include "BKE_mesh_runtime.h" +#include "BKE_modifier.h" +#include "BKE_multires.h" +#include "BKE_object.h" +#include "BKE_paint.h" +#include "BKE_pbvh.h" +#include "BKE_subdiv_ccg.h" +#include "BKE_subsurf.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "RNA_enum_types.h" + +#include "BLO_read_write.h" + +#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; + + BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(palette, id)); + + /* Enable fake user by default. */ + id_fake_user_set(&palette->id); +} + +static void palette_copy_data(Main *UNUSED(bmain), + ID *id_dst, + const ID *id_src, + const int UNUSED(flag)) +{ + Palette *palette_dst = (Palette *)id_dst; + const Palette *palette_src = (const Palette *)id_src; + + BLI_duplicatelist(&palette_dst->colors, &palette_src->colors); +} + +static void palette_free_data(ID *id) +{ + Palette *palette = (Palette *)id; + + BLI_freelistN(&palette->colors); +} + +static void palette_blend_write(BlendWriter *writer, ID *id, const void *id_address) +{ + Palette *palette = (Palette *)id; + + BLO_write_id_struct(writer, Palette, id_address, &palette->id); + BKE_id_blend_write(writer, &palette->id); + + BLO_write_struct_list(writer, PaletteColor, &palette->colors); +} + +static void palette_blend_read_data(BlendDataReader *reader, ID *id) +{ + Palette *palette = (Palette *)id; + BLO_read_list(reader, &palette->colors); +} + +static void palette_undo_preserve(BlendLibReader *UNUSED(reader), ID *id_new, ID *id_old) +{ + /* Whole Palette is preserved across undo-steps, and it has no extra pointer, simple. */ + /* 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(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 */ 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), + ID *id_dst, + const ID *id_src, + const int UNUSED(flag)) +{ + PaintCurve *paint_curve_dst = (PaintCurve *)id_dst; + const PaintCurve *paint_curve_src = (const PaintCurve *)id_src; + + if (paint_curve_src->tot_points != 0) { + paint_curve_dst->points = static_cast( + MEM_dupallocN(paint_curve_src->points)); + } +} + +static void paint_curve_free_data(ID *id) +{ + PaintCurve *paint_curve = (PaintCurve *)id; + + MEM_SAFE_FREE(paint_curve->points); + paint_curve->tot_points = 0; +} + +static void paint_curve_blend_write(BlendWriter *writer, ID *id, const void *id_address) +{ + PaintCurve *pc = (PaintCurve *)id; + + BLO_write_id_struct(writer, PaintCurve, id_address, &pc->id); + BKE_id_blend_write(writer, &pc->id); + + BLO_write_struct_array(writer, PaintCurvePoint, pc->tot_points, pc->points); +} + +static void paint_curve_blend_read_data(BlendDataReader *reader, ID *id) +{ + PaintCurve *pc = (PaintCurve *)id; + BLO_read_data_address(reader, &pc->points); +} + +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 */ 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 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 = (ePaintOverlayControlFlags)0; + +void BKE_paint_invalidate_overlay_tex(Scene *scene, ViewLayer *view_layer, const Tex *tex) +{ + Paint *p = BKE_paint_get_active(scene, view_layer); + if (!p) { + return; + } + + Brush *br = p->brush; + if (!br) { + return; + } + + if (br->mtex.tex == tex) { + overlay_flags |= PAINT_OVERLAY_INVALID_TEXTURE_PRIMARY; + } + if (br->mask_mtex.tex == tex) { + overlay_flags |= PAINT_OVERLAY_INVALID_TEXTURE_SECONDARY; + } +} + +void BKE_paint_invalidate_cursor_overlay(Scene *scene, ViewLayer *view_layer, CurveMapping *curve) +{ + Paint *p = BKE_paint_get_active(scene, view_layer); + if (p == nullptr) { + return; + } + + Brush *br = p->brush; + if (br && br->curve == curve) { + overlay_flags |= PAINT_OVERLAY_INVALID_CURVE; + } +} + +void BKE_paint_invalidate_overlay_all(void) +{ + overlay_flags |= (PAINT_OVERLAY_INVALID_TEXTURE_SECONDARY | + PAINT_OVERLAY_INVALID_TEXTURE_PRIMARY | PAINT_OVERLAY_INVALID_CURVE); +} + +ePaintOverlayControlFlags BKE_paint_get_overlay_flags(void) +{ + return overlay_flags; +} + +void BKE_paint_set_overlay_override(eOverlayFlags flags) +{ + if (flags & BRUSH_OVERLAY_OVERRIDE_MASK) { + if (flags & BRUSH_OVERLAY_CURSOR_OVERRIDE_ON_STROKE) { + overlay_flags |= PAINT_OVERLAY_OVERRIDE_CURSOR; + } + if (flags & BRUSH_OVERLAY_PRIMARY_OVERRIDE_ON_STROKE) { + overlay_flags |= PAINT_OVERLAY_OVERRIDE_PRIMARY; + } + if (flags & BRUSH_OVERLAY_SECONDARY_OVERRIDE_ON_STROKE) { + overlay_flags |= PAINT_OVERLAY_OVERRIDE_SECONDARY; + } + } + else { + overlay_flags &= ~(PAINT_OVERRIDE_MASK); + } +} + +void BKE_paint_reset_overlay_invalid(ePaintOverlayControlFlags flag) +{ + overlay_flags &= ~(flag); +} + +bool BKE_paint_ensure_from_paintmode(Scene *sce, ePaintMode mode) +{ + ToolSettings *ts = sce->toolsettings; + 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 = nullptr; + + switch (mode) { + case PAINT_MODE_SCULPT: + paint_ptr = (Paint **)&ts->sculpt; + break; + case PAINT_MODE_VERTEX: + paint_ptr = (Paint **)&ts->vpaint; + break; + case PAINT_MODE_WEIGHT: + paint_ptr = (Paint **)&ts->wpaint; + break; + case PAINT_MODE_TEXTURE_2D: + case PAINT_MODE_TEXTURE_3D: + paint_tmp = (Paint *)&ts->imapaint; + paint_ptr = &paint_tmp; + break; + case PAINT_MODE_SCULPT_UV: + paint_ptr = (Paint **)&ts->uvsculpt; + break; + case PAINT_MODE_GPENCIL: + paint_ptr = (Paint **)&ts->gp_paint; + break; + case PAINT_MODE_VERTEX_GPENCIL: + paint_ptr = (Paint **)&ts->gp_vertexpaint; + break; + case PAINT_MODE_SCULPT_GPENCIL: + paint_ptr = (Paint **)&ts->gp_sculptpaint; + break; + case PAINT_MODE_WEIGHT_GPENCIL: + paint_ptr = (Paint **)&ts->gp_weightpaint; + break; + case PAINT_MODE_SCULPT_CURVES: + paint_ptr = (Paint **)&ts->curves_sculpt; + break; + case PAINT_MODE_INVALID: + break; + } + if (paint_ptr) { + BKE_paint_ensure(ts, paint_ptr); + return true; + } + return false; +} + +Paint *BKE_paint_get_active_from_paintmode(Scene *sce, ePaintMode mode) +{ + if (sce) { + ToolSettings *ts = sce->toolsettings; + + switch (mode) { + case PAINT_MODE_SCULPT: + return &ts->sculpt->paint; + case PAINT_MODE_VERTEX: + return &ts->vpaint->paint; + case PAINT_MODE_WEIGHT: + return &ts->wpaint->paint; + case PAINT_MODE_TEXTURE_2D: + case PAINT_MODE_TEXTURE_3D: + return &ts->imapaint.paint; + case PAINT_MODE_SCULPT_UV: + return &ts->uvsculpt->paint; + case PAINT_MODE_GPENCIL: + return &ts->gp_paint->paint; + case PAINT_MODE_VERTEX_GPENCIL: + return &ts->gp_vertexpaint->paint; + case PAINT_MODE_SCULPT_GPENCIL: + return &ts->gp_sculptpaint->paint; + case PAINT_MODE_WEIGHT_GPENCIL: + return &ts->gp_weightpaint->paint; + case PAINT_MODE_SCULPT_CURVES: + return &ts->curves_sculpt->paint; + case PAINT_MODE_INVALID: + return nullptr; + default: + return &ts->imapaint.paint; + } + } + + return nullptr; +} + +const EnumPropertyItem *BKE_paint_get_tool_enum_from_paintmode(ePaintMode mode) +{ + switch (mode) { + case PAINT_MODE_SCULPT: + return rna_enum_brush_sculpt_tool_items; + case PAINT_MODE_VERTEX: + return rna_enum_brush_vertex_tool_items; + case PAINT_MODE_WEIGHT: + return rna_enum_brush_weight_tool_items; + case PAINT_MODE_TEXTURE_2D: + case PAINT_MODE_TEXTURE_3D: + return rna_enum_brush_image_tool_items; + case PAINT_MODE_SCULPT_UV: + return rna_enum_brush_uv_sculpt_tool_items; + case PAINT_MODE_GPENCIL: + return rna_enum_brush_gpencil_types_items; + case PAINT_MODE_VERTEX_GPENCIL: + return rna_enum_brush_gpencil_vertex_types_items; + case PAINT_MODE_SCULPT_GPENCIL: + return rna_enum_brush_gpencil_sculpt_types_items; + case PAINT_MODE_WEIGHT_GPENCIL: + return rna_enum_brush_gpencil_weight_types_items; + case PAINT_MODE_SCULPT_CURVES: + return rna_enum_brush_curves_sculpt_tool_items; + case PAINT_MODE_INVALID: + break; + } + return nullptr; +} + +const char *BKE_paint_get_tool_prop_id_from_paintmode(ePaintMode mode) +{ + switch (mode) { + case PAINT_MODE_SCULPT: + return "sculpt_tool"; + case PAINT_MODE_VERTEX: + return "vertex_tool"; + case PAINT_MODE_WEIGHT: + return "weight_tool"; + case PAINT_MODE_TEXTURE_2D: + case PAINT_MODE_TEXTURE_3D: + return "image_tool"; + case PAINT_MODE_SCULPT_UV: + return "uv_sculpt_tool"; + case PAINT_MODE_GPENCIL: + return "gpencil_tool"; + case PAINT_MODE_VERTEX_GPENCIL: + return "gpencil_vertex_tool"; + case PAINT_MODE_SCULPT_GPENCIL: + return "gpencil_sculpt_tool"; + case PAINT_MODE_WEIGHT_GPENCIL: + return "gpencil_weight_tool"; + case PAINT_MODE_SCULPT_CURVES: + return "curves_sculpt_tool"; + case PAINT_MODE_INVALID: + break; + } + + /* Invalid paint mode. */ + 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 (actob) { + switch (actob->mode) { + case OB_MODE_SCULPT: + return &ts->sculpt->paint; + case OB_MODE_VERTEX_PAINT: + return &ts->vpaint->paint; + case OB_MODE_WEIGHT_PAINT: + return &ts->wpaint->paint; + case OB_MODE_TEXTURE_PAINT: + return &ts->imapaint.paint; + case OB_MODE_PAINT_GPENCIL: + return &ts->gp_paint->paint; + case OB_MODE_VERTEX_GPENCIL: + return &ts->gp_vertexpaint->paint; + case OB_MODE_SCULPT_GPENCIL: + return &ts->gp_sculptpaint->paint; + case OB_MODE_WEIGHT_GPENCIL: + return &ts->gp_weightpaint->paint; + case OB_MODE_SCULPT_CURVES: + return &ts->curves_sculpt->paint; + case OB_MODE_EDIT: + return ts->uvsculpt ? &ts->uvsculpt->paint : nullptr; + default: + break; + } + } + + /* default to image paint */ + return &ts->imapaint.paint; + } + + return nullptr; +} + +Paint *BKE_paint_get_active_from_context(const bContext *C) +{ + Scene *sce = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + SpaceImage *sima; + + if (sce && view_layer) { + ToolSettings *ts = sce->toolsettings; + 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)) != nullptr) { + if (obact && obact->mode == OB_MODE_EDIT) { + if (sima->mode == SI_MODE_PAINT) { + return &ts->imapaint.paint; + } + if (sima->mode == SI_MODE_UV) { + return &ts->uvsculpt->paint; + } + } + else { + return &ts->imapaint.paint; + } + } + else { + return BKE_paint_get_active(sce, view_layer); + } + } + + return nullptr; +} + +ePaintMode BKE_paintmode_get_active_from_context(const bContext *C) +{ + Scene *sce = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + SpaceImage *sima; + + if (sce && view_layer) { + 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)) != nullptr) { + if (obact && obact->mode == OB_MODE_EDIT) { + if (sima->mode == SI_MODE_PAINT) { + return PAINT_MODE_TEXTURE_2D; + } + if (sima->mode == SI_MODE_UV) { + return PAINT_MODE_SCULPT_UV; + } + } + else { + return PAINT_MODE_TEXTURE_2D; + } + } + else if (obact) { + switch (obact->mode) { + case OB_MODE_SCULPT: + return PAINT_MODE_SCULPT; + case OB_MODE_VERTEX_PAINT: + return PAINT_MODE_VERTEX; + case OB_MODE_WEIGHT_PAINT: + return PAINT_MODE_WEIGHT; + case OB_MODE_TEXTURE_PAINT: + return PAINT_MODE_TEXTURE_3D; + case OB_MODE_EDIT: + return PAINT_MODE_SCULPT_UV; + case OB_MODE_SCULPT_CURVES: + return PAINT_MODE_SCULPT_CURVES; + default: + return PAINT_MODE_TEXTURE_2D; + } + } + else { + /* default to image paint */ + return PAINT_MODE_TEXTURE_2D; + } + } + + return PAINT_MODE_INVALID; +} + +ePaintMode BKE_paintmode_get_from_tool(const bToolRef *tref) +{ + if (tref->space_type == SPACE_VIEW3D) { + switch (tref->mode) { + case CTX_MODE_SCULPT: + return PAINT_MODE_SCULPT; + case CTX_MODE_PAINT_VERTEX: + return PAINT_MODE_VERTEX; + case CTX_MODE_PAINT_WEIGHT: + return PAINT_MODE_WEIGHT; + case CTX_MODE_PAINT_GPENCIL: + return PAINT_MODE_GPENCIL; + case CTX_MODE_PAINT_TEXTURE: + return PAINT_MODE_TEXTURE_3D; + case CTX_MODE_VERTEX_GPENCIL: + return PAINT_MODE_VERTEX_GPENCIL; + case CTX_MODE_SCULPT_GPENCIL: + return PAINT_MODE_SCULPT_GPENCIL; + case CTX_MODE_WEIGHT_GPENCIL: + return PAINT_MODE_WEIGHT_GPENCIL; + case CTX_MODE_SCULPT_CURVES: + return PAINT_MODE_SCULPT_CURVES; + } + } + else if (tref->space_type == SPACE_IMAGE) { + switch (tref->mode) { + case SI_MODE_PAINT: + return PAINT_MODE_TEXTURE_2D; + case SI_MODE_UV: + return PAINT_MODE_SCULPT_UV; + } + } + + return PAINT_MODE_INVALID; +} + +Brush *BKE_paint_brush(Paint *p) +{ + return (Brush *)BKE_paint_brush_for_read((const Paint *)p); +} + +const Brush *BKE_paint_brush_for_read(const Paint *p) +{ + return p ? p->brush : nullptr; +} + +void BKE_paint_brush_set(Paint *p, Brush *br) +{ + if (p) { + id_us_min((ID *)p->brush); + id_us_plus((ID *)br); + p->brush = br; + + BKE_paint_toolslots_brush_update(p); + } +} + +void BKE_paint_runtime_init(const ToolSettings *ts, Paint *paint) +{ + if (paint == &ts->imapaint.paint) { + paint->runtime.tool_offset = offsetof(Brush, imagepaint_tool); + paint->runtime.ob_mode = OB_MODE_TEXTURE_PAINT; + } + else if (ts->sculpt && paint == &ts->sculpt->paint) { + paint->runtime.tool_offset = offsetof(Brush, sculpt_tool); + paint->runtime.ob_mode = OB_MODE_SCULPT; + } + else if (ts->vpaint && paint == &ts->vpaint->paint) { + paint->runtime.tool_offset = offsetof(Brush, vertexpaint_tool); + paint->runtime.ob_mode = OB_MODE_VERTEX_PAINT; + } + else if (ts->wpaint && paint == &ts->wpaint->paint) { + paint->runtime.tool_offset = offsetof(Brush, weightpaint_tool); + paint->runtime.ob_mode = OB_MODE_WEIGHT_PAINT; + } + else if (ts->uvsculpt && paint == &ts->uvsculpt->paint) { + paint->runtime.tool_offset = offsetof(Brush, uv_sculpt_tool); + paint->runtime.ob_mode = OB_MODE_EDIT; + } + else if (ts->gp_paint && paint == &ts->gp_paint->paint) { + paint->runtime.tool_offset = offsetof(Brush, gpencil_tool); + paint->runtime.ob_mode = OB_MODE_PAINT_GPENCIL; + } + else if (ts->gp_vertexpaint && paint == &ts->gp_vertexpaint->paint) { + paint->runtime.tool_offset = offsetof(Brush, gpencil_vertex_tool); + paint->runtime.ob_mode = OB_MODE_VERTEX_GPENCIL; + } + else if (ts->gp_sculptpaint && paint == &ts->gp_sculptpaint->paint) { + paint->runtime.tool_offset = offsetof(Brush, gpencil_sculpt_tool); + paint->runtime.ob_mode = OB_MODE_SCULPT_GPENCIL; + } + else if (ts->gp_weightpaint && paint == &ts->gp_weightpaint->paint) { + paint->runtime.tool_offset = offsetof(Brush, gpencil_weight_tool); + paint->runtime.ob_mode = OB_MODE_WEIGHT_GPENCIL; + } + else if (ts->curves_sculpt && paint == &ts->curves_sculpt->paint) { + paint->runtime.tool_offset = offsetof(Brush, curves_sculpt_tool); + paint->runtime.ob_mode = OB_MODE_SCULPT_CURVES; + } + else { + BLI_assert_unreachable(); + } +} + +uint BKE_paint_get_brush_tool_offset_from_paintmode(const ePaintMode mode) +{ + switch (mode) { + case PAINT_MODE_TEXTURE_2D: + case PAINT_MODE_TEXTURE_3D: + return offsetof(Brush, imagepaint_tool); + case PAINT_MODE_SCULPT: + return offsetof(Brush, sculpt_tool); + case PAINT_MODE_VERTEX: + return offsetof(Brush, vertexpaint_tool); + case PAINT_MODE_WEIGHT: + return offsetof(Brush, weightpaint_tool); + case PAINT_MODE_SCULPT_UV: + return offsetof(Brush, uv_sculpt_tool); + case PAINT_MODE_GPENCIL: + return offsetof(Brush, gpencil_tool); + case PAINT_MODE_VERTEX_GPENCIL: + return offsetof(Brush, gpencil_vertex_tool); + case PAINT_MODE_SCULPT_GPENCIL: + return offsetof(Brush, gpencil_sculpt_tool); + case PAINT_MODE_WEIGHT_GPENCIL: + return offsetof(Brush, gpencil_weight_tool); + case PAINT_MODE_SCULPT_CURVES: + return offsetof(Brush, curves_sculpt_tool); + case PAINT_MODE_INVALID: + break; /* We don't use these yet. */ + } + return 0; +} + +PaintCurve *BKE_paint_curve_add(Main *bmain, const char *name) +{ + PaintCurve *pc = static_cast(BKE_id_new(bmain, ID_PC, name)); + return pc; +} + +Palette *BKE_paint_palette(Paint *p) +{ + return p ? p->palette : nullptr; +} + +void BKE_paint_palette_set(Paint *p, Palette *palette) +{ + if (p) { + id_us_min((ID *)p->palette); + p->palette = palette; + id_us_plus((ID *)p->palette); + } +} + +void BKE_paint_curve_set(Brush *br, PaintCurve *pc) +{ + if (br) { + id_us_min((ID *)br->paint_curve); + br->paint_curve = pc; + id_us_plus((ID *)br->paint_curve); + } +} + +void BKE_paint_curve_clamp_endpoint_add_index(PaintCurve *pc, const int add_index) +{ + pc->add_index = (add_index || pc->tot_points == 1) ? (add_index + 1) : 0; +} + +void BKE_palette_color_remove(Palette *palette, PaletteColor *color) +{ + if (BLI_listbase_count_at_most(&palette->colors, palette->active_color) == + palette->active_color) { + palette->active_color--; + } + + BLI_remlink(&palette->colors, color); + + if (palette->active_color < 0 && !BLI_listbase_is_empty(&palette->colors)) { + palette->active_color = 0; + } + + MEM_freeN(color); +} + +void BKE_palette_clear(Palette *palette) +{ + BLI_freelistN(&palette->colors); + palette->active_color = 0; +} + +Palette *BKE_palette_add(Main *bmain, const char *name) +{ + Palette *palette = static_cast(BKE_id_new(bmain, ID_PAL, name)); + return palette; +} + +PaletteColor *BKE_palette_color_add(Palette *palette) +{ + PaletteColor *color = MEM_cnew(__func__); + BLI_addtail(&palette->colors, color); + return color; +} + +bool BKE_palette_is_empty(const Palette *palette) +{ + return BLI_listbase_is_empty(&palette->colors); +} + +/* helper function to sort using qsort */ +static int palettecolor_compare_hsv(const void *a1, const void *a2) +{ + const tPaletteColorHSV *ps1 = static_cast(a1); + const tPaletteColorHSV *ps2 = static_cast(a2); + + /* Hue */ + if (ps1->h > ps2->h) { + return 1; + } + if (ps1->h < ps2->h) { + return -1; + } + + /* Saturation. */ + if (ps1->s > ps2->s) { + return 1; + } + if (ps1->s < ps2->s) { + return -1; + } + + /* Value. */ + if (1.0f - ps1->v > 1.0f - ps2->v) { + return 1; + } + if (1.0f - ps1->v < 1.0f - ps2->v) { + return -1; + } + + return 0; +} + +/* helper function to sort using qsort */ +static int palettecolor_compare_svh(const void *a1, const void *a2) +{ + const tPaletteColorHSV *ps1 = static_cast(a1); + const tPaletteColorHSV *ps2 = static_cast(a2); + + /* Saturation. */ + if (ps1->s > ps2->s) { + return 1; + } + if (ps1->s < ps2->s) { + return -1; + } + + /* Value. */ + if (1.0f - ps1->v > 1.0f - ps2->v) { + return 1; + } + if (1.0f - ps1->v < 1.0f - ps2->v) { + return -1; + } + + /* Hue */ + if (ps1->h > ps2->h) { + return 1; + } + if (ps1->h < ps2->h) { + return -1; + } + + return 0; +} + +static int palettecolor_compare_vhs(const void *a1, const void *a2) +{ + const tPaletteColorHSV *ps1 = static_cast(a1); + const tPaletteColorHSV *ps2 = static_cast(a2); + + /* Value. */ + if (1.0f - ps1->v > 1.0f - ps2->v) { + return 1; + } + if (1.0f - ps1->v < 1.0f - ps2->v) { + return -1; + } + + /* Hue */ + if (ps1->h > ps2->h) { + return 1; + } + if (ps1->h < ps2->h) { + return -1; + } + + /* Saturation. */ + if (ps1->s > ps2->s) { + return 1; + } + if (ps1->s < ps2->s) { + return -1; + } + + return 0; +} + +static int palettecolor_compare_luminance(const void *a1, const void *a2) +{ + const tPaletteColorHSV *ps1 = static_cast(a1); + const tPaletteColorHSV *ps2 = static_cast(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; + + if (lumi1 > lumi2) { + return -1; + } + if (lumi1 < lumi2) { + return 1; + } + + return 0; +} + +void BKE_palette_sort_hsv(tPaletteColorHSV *color_array, const int totcol) +{ + /* Sort by Hue, Saturation and Value. */ + qsort(color_array, totcol, sizeof(tPaletteColorHSV), palettecolor_compare_hsv); +} + +void BKE_palette_sort_svh(tPaletteColorHSV *color_array, const int totcol) +{ + /* Sort by Saturation, Value and Hue. */ + qsort(color_array, totcol, sizeof(tPaletteColorHSV), palettecolor_compare_svh); +} + +void BKE_palette_sort_vhs(tPaletteColorHSV *color_array, const int totcol) +{ + /* Sort by Saturation, Value and Hue. */ + qsort(color_array, totcol, sizeof(tPaletteColorHSV), palettecolor_compare_vhs); +} + +void BKE_palette_sort_luminance(tPaletteColorHSV *color_array, const int totcol) +{ + /* Sort by Luminance (calculated with the average, enough for sorting). */ + qsort(color_array, totcol, sizeof(tPaletteColorHSV), palettecolor_compare_luminance); +} + +bool BKE_palette_from_hash(Main *bmain, GHash *color_table, const char *name, const bool linear) +{ + tPaletteColorHSV *color_array = nullptr; + tPaletteColorHSV *col_elm = nullptr; + bool done = false; + + const int totpal = BLI_ghash_len(color_table); + + if (totpal > 0) { + color_array = static_cast( + MEM_calloc_arrayN(totpal, sizeof(tPaletteColorHSV), __func__)); + /* Put all colors in an array. */ + GHashIterator gh_iter; + int t = 0; + GHASH_ITER (gh_iter, color_table) { + const uint col = POINTER_AS_INT(BLI_ghashIterator_getValue(&gh_iter)); + float r, g, b; + float h, s, v; + cpack_to_rgb(col, &r, &g, &b); + rgb_to_hsv(r, g, b, &h, &s, &v); + + col_elm = &color_array[t]; + col_elm->rgb[0] = r; + col_elm->rgb[1] = g; + col_elm->rgb[2] = b; + col_elm->h = h; + col_elm->s = s; + col_elm->v = v; + t++; + } + } + + /* Create the Palette. */ + if (totpal > 0) { + /* Sort by Hue and saturation. */ + BKE_palette_sort_hsv(color_array, totpal); + + Palette *palette = BKE_palette_add(bmain, name); + if (palette) { + for (int i = 0; i < totpal; i++) { + col_elm = &color_array[i]; + PaletteColor *palcol = BKE_palette_color_add(palette); + if (palcol) { + copy_v3_v3(palcol->rgb, col_elm->rgb); + if (linear) { + linearrgb_to_srgb_v3_v3(palcol->rgb, palcol->rgb); + } + } + } + done = true; + } + } + else { + done = false; + } + + if (totpal > 0) { + MEM_SAFE_FREE(color_array); + } + + return done; +} + +bool BKE_paint_select_face_test(Object *ob) +{ + 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 != 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)); +} + +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 = nullptr; + CurveMap *cuma = nullptr; + + if (!p->cavity_curve) { + p->cavity_curve = BKE_curvemapping_add(1, 0, 0, 1, 1); + } + cumap = p->cavity_curve; + cumap->flag &= ~CUMA_EXTEND_EXTRAPOLATE; + cumap->preset = preset; + + cuma = cumap->cm; + BKE_curvemap_reset(cuma, &cumap->clipr, cumap->preset, CURVEMAP_SLOPE_POSITIVE); + BKE_curvemapping_changed(cumap, false); +} + +eObjectMode BKE_paint_object_mode_from_paintmode(ePaintMode mode) +{ + switch (mode) { + case PAINT_MODE_SCULPT: + return OB_MODE_SCULPT; + case PAINT_MODE_VERTEX: + return OB_MODE_VERTEX_PAINT; + case PAINT_MODE_WEIGHT: + return OB_MODE_WEIGHT_PAINT; + case PAINT_MODE_TEXTURE_2D: + case PAINT_MODE_TEXTURE_3D: + return OB_MODE_TEXTURE_PAINT; + case PAINT_MODE_SCULPT_UV: + return OB_MODE_EDIT; + case PAINT_MODE_INVALID: + default: + return OB_MODE_OBJECT; + } +} + +bool BKE_paint_ensure(ToolSettings *ts, Paint **r_paint) +{ + 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. */ + if ((*r_paint)->runtime.tool_offset == 0) { + /* Currently only image painting is initialized this way, others have to be allocated. */ + BLI_assert(ELEM(*r_paint, (Paint *)&ts->imapaint)); + + BKE_paint_runtime_init(ts, *r_paint); + } + else { + BLI_assert(ELEM(*r_paint, + /* Cast is annoying, but prevent nullptr-pointer access. */ + (Paint *)ts->gp_paint, + (Paint *)ts->gp_vertexpaint, + (Paint *)ts->gp_sculptpaint, + (Paint *)ts->gp_weightpaint, + (Paint *)ts->sculpt, + (Paint *)ts->vpaint, + (Paint *)ts->wpaint, + (Paint *)ts->uvsculpt, + (Paint *)ts->curves_sculpt, + (Paint *)&ts->imapaint)); +#ifdef DEBUG + 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); + BLI_assert(paint_test.runtime.ob_mode == (*r_paint)->runtime.ob_mode); + BLI_assert(paint_test.runtime.tool_offset == (*r_paint)->runtime.tool_offset); +#endif + } + return true; + } + + if (((VPaint **)r_paint == &ts->vpaint) || ((VPaint **)r_paint == &ts->wpaint)) { + VPaint *data = MEM_cnew(__func__); + paint = &data->paint; + } + else if ((Sculpt **)r_paint == &ts->sculpt) { + Sculpt *data = MEM_cnew(__func__); + paint = &data->paint; + + /* Turn on X plane mirror symmetry by default. */ + paint->symmetry_flags |= PAINT_SYMM_X; + + /* 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_cnew(__func__); + paint = &data->paint; + } + else if ((GpVertexPaint **)r_paint == &ts->gp_vertexpaint) { + GpVertexPaint *data = MEM_cnew(__func__); + paint = &data->paint; + } + else if ((GpSculptPaint **)r_paint == &ts->gp_sculptpaint) { + GpSculptPaint *data = MEM_cnew(__func__); + paint = &data->paint; + } + else if ((GpWeightPaint **)r_paint == &ts->gp_weightpaint) { + GpWeightPaint *data = MEM_cnew(__func__); + paint = &data->paint; + } + else if ((UvSculpt **)r_paint == &ts->uvsculpt) { + UvSculpt *data = MEM_cnew(__func__); + paint = &data->paint; + } + else if ((CurvesSculpt **)r_paint == &ts->curves_sculpt) { + CurvesSculpt *data = MEM_cnew(__func__); + paint = &data->paint; + } + else if (*r_paint == &ts->imapaint.paint) { + paint = &ts->imapaint.paint; + } + + paint->flags |= PAINT_SHOW_BRUSH; + + *r_paint = paint; + + BKE_paint_runtime_init(ts, paint); + + return false; +} + +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); + + BKE_paint_ensure_from_paintmode(sce, mode); + + /* If there's no brush, create one */ + if (PAINT_MODE_HAS_BRUSH(mode)) { + Brush *brush = BKE_paint_brush(paint); + 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. */ + } + BKE_paint_brush_set(paint, brush); + } + } + + 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); + ups->average_stroke_counter = 0; + if (!paint->cavity_curve) { + BKE_paint_cavity_curve_preset(paint, CURVE_PRESET_LINE); + } +} + +void BKE_paint_free(Paint *paint) +{ + BKE_curvemapping_free(paint->cavity_curve); + MEM_SAFE_FREE(paint->tool_slots); +} + +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 = static_cast(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 != nullptr) { + for (int i = 0; i < tar->tool_slots_len; i++) { + id_us_plus((ID *)tar->tool_slots[i].brush); + } + } + } +} + +void BKE_paint_stroke_get_average(Scene *scene, Object *ob, float stroke[3]) +{ + UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; + if (ups->last_stroke_valid && ups->average_stroke_counter > 0) { + float fac = 1.0f / ups->average_stroke_counter; + mul_v3_v3fl(stroke, ups->average_stroke_accum, fac); + } + else { + copy_v3_v3(stroke, ob->obmat[3]); + } +} + +void BKE_paint_blend_write(BlendWriter *writer, Paint *p) +{ + if (p->cavity_curve) { + BKE_curvemapping_blend_write(writer, p->cavity_curve); + } + BLO_write_struct_array(writer, PaintToolSlot, p->tool_slots_len, p->tool_slots); +} + +void BKE_paint_blend_read_data(BlendDataReader *reader, const Scene *scene, Paint *p) +{ + if (p->num_input_samples < 1) { + p->num_input_samples = 1; + } + + BLO_read_data_address(reader, &p->cavity_curve); + if (p->cavity_curve) { + BKE_curvemapping_blend_read(reader, p->cavity_curve); + } + else { + BKE_paint_cavity_curve_preset(p, CURVE_PRESET_LINE); + } + + BLO_read_data_address(reader, &p->tool_slots); + + /* Workaround for invalid data written in older versions. */ + 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 = static_cast(MEM_callocN(expected_size, "PaintToolSlot")); + } + + BKE_paint_runtime_init(scene->toolsettings, p); +} + +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 != 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 = nullptr; + + BKE_paint_runtime_init(sce->toolsettings, p); + } +} + +bool paint_is_face_hidden(const MLoopTri *lt, const bool *hide_poly) +{ + 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. */ + 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) || + BLI_BITMAP_TEST(grid_hidden, (y + 1) * gridsize + x)); +} + +bool paint_is_bmesh_face_hidden(BMFace *f) +{ + BMLoop *l_iter; + BMLoop *l_first; + + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + if (BM_elem_flag_test(l_iter->v, BM_ELEM_HIDDEN)) { + return true; + } + } while ((l_iter = l_iter->next) != l_first); + + return false; +} + +float paint_grid_paint_mask(const GridPaintMask *gpm, uint level, uint x, uint y) +{ + int factor = BKE_ccg_factor(level, gpm->level); + int gridsize = BKE_ccg_gridsize(gpm->level); + + return gpm->data[(y * factor) * gridsize + (x * factor)]; +} + +/* Threshold to move before updating the brush rotation. */ +#define RAKE_THRESHHOLD 20 + +void paint_update_brush_rake_rotation(UnifiedPaintSettings *ups, Brush *brush, float rotation) +{ + if (brush->mtex.brush_angle_mode & MTEX_ANGLE_RAKE) { + ups->brush_rotation = rotation; + } + else { + ups->brush_rotation = 0.0f; + } + + if (brush->mask_mtex.brush_angle_mode & MTEX_ANGLE_RAKE) { + ups->brush_rotation_sec = rotation; + } + else { + ups->brush_rotation_sec = 0.0f; + } +} + +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 (paint_rake_rotation_active(*brush)) { + const float r = RAKE_THRESHHOLD; + float rotation; + + float dpos[2]; + sub_v2_v2v2(dpos, ups->last_rake, mouse_pos); + + if (len_squared_v2(dpos) >= r * r) { + rotation = atan2f(dpos[0], dpos[1]); + + copy_v2_v2(ups->last_rake, mouse_pos); + + ups->last_rake_angle = rotation; + + 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. */ + else { + paint_update_brush_rake_rotation(ups, brush, ups->last_rake_angle); + ok = false; + } + } + else { + ups->brush_rotation = ups->brush_rotation_sec = 0.0f; + ok = true; + } + return ok; +} + +void BKE_sculptsession_free_deformMats(SculptSession *ss) +{ + MEM_SAFE_FREE(ss->orig_cos); + MEM_SAFE_FREE(ss->deform_cos); + MEM_SAFE_FREE(ss->deform_imats); +} + +void BKE_sculptsession_free_vwpaint_data(SculptSession *ss) +{ + SculptVertexPaintGeomMap *gmap = nullptr; + if (ss->mode_type == OB_MODE_VERTEX_PAINT) { + gmap = &ss->mode.vpaint.gmap; + } + else if (ss->mode_type == OB_MODE_WEIGHT_PAINT) { + gmap = &ss->mode.wpaint.gmap; + + MEM_SAFE_FREE(ss->mode.wpaint.alpha_weight); + 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 = nullptr; + } + } + else { + return; + } + MEM_SAFE_FREE(gmap->vert_to_loop); + MEM_SAFE_FREE(gmap->vert_map_mem); + MEM_SAFE_FREE(gmap->vert_to_poly); + MEM_SAFE_FREE(gmap->poly_map_mem); +} + +/** + * Write out the sculpt dynamic-topology #BMesh to the #Mesh. + */ +static void sculptsession_bm_to_me_update_data_only(Object *ob, bool reorder) +{ + SculptSession *ss = ob->sculpt; + + if (ss->bm) { + if (ob->data) { + BMIter iter; + BMFace *efa; + BM_ITER_MESH (efa, &iter, ss->bm, BM_FACES_OF_MESH) { + BM_elem_flag_set(efa, BM_ELEM_SMOOTH, ss->bm_smooth_shading); + } + if (reorder) { + BM_log_mesh_elems_reorder(ss->bm, ss->bm_log); + } + BMeshToMeshParams params{}; + params.calc_object_remap = false; + BM_mesh_bm_to_me(nullptr, ss->bm, static_cast(ob->data), ¶ms); + } + } +} + +void BKE_sculptsession_bm_to_me(Object *ob, bool reorder) +{ + if (ob && ob->sculpt) { + sculptsession_bm_to_me_update_data_only(ob, reorder); + + /* Ensure the objects evaluated mesh doesn't hold onto arrays + * now realloc'd in the mesh T34473. */ + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + } +} + +static void sculptsession_free_pbvh(Object *object) +{ + SculptSession *ss = object->sculpt; + + if (!ss) { + return; + } + + if (ss->pbvh) { + BKE_pbvh_free(ss->pbvh); + ss->pbvh = nullptr; + } + + MEM_SAFE_FREE(ss->pmap); + MEM_SAFE_FREE(ss->pmap_mem); + + MEM_SAFE_FREE(ss->epmap); + MEM_SAFE_FREE(ss->epmap_mem); + + MEM_SAFE_FREE(ss->vemap); + MEM_SAFE_FREE(ss->vemap_mem); + + 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); + + MEM_SAFE_FREE(ss->fake_neighbors.fake_neighbor_index); +} + +void BKE_sculptsession_bm_to_me_for_render(Object *object) +{ + if (object && object->sculpt) { + if (object->sculpt->bm) { + /* Ensure no points to old arrays are stored in DM + * + * Apparently, we could not use DEG_id_tag_update + * here because this will lead to the while object + * surface to disappear, so we'll release DM in place. + */ + BKE_object_free_derived_caches(object); + + sculptsession_bm_to_me_update_data_only(object, false); + + /* In contrast with sculptsession_bm_to_me no need in + * DAG tag update here - derived mesh was freed and + * old pointers are nowhere stored. + */ + } + } +} + +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); + } + + sculptsession_free_pbvh(ob); + + MEM_SAFE_FREE(ss->pmap); + MEM_SAFE_FREE(ss->pmap_mem); + + MEM_SAFE_FREE(ss->epmap); + MEM_SAFE_FREE(ss->epmap_mem); + + MEM_SAFE_FREE(ss->vemap); + MEM_SAFE_FREE(ss->vemap_mem); + + if (ss->bm_log) { + BM_log_free(ss->bm_log); + } + + if (ss->tex_pool) { + BKE_image_pool_free(ss->tex_pool); + } + + MEM_SAFE_FREE(ss->orig_cos); + MEM_SAFE_FREE(ss->deform_cos); + MEM_SAFE_FREE(ss->deform_imats); + + if (ss->pose_ik_chain_preview) { + for (int i = 0; i < ss->pose_ik_chain_preview->tot_segments; i++) { + MEM_SAFE_FREE(ss->pose_ik_chain_preview->segments[i].weights); + } + MEM_SAFE_FREE(ss->pose_ik_chain_preview->segments); + MEM_SAFE_FREE(ss->pose_ik_chain_preview); + } + + if (ss->boundary_preview) { + 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); + MEM_SAFE_FREE(ss->boundary_preview); + } + + BKE_sculptsession_free_vwpaint_data(ob->sculpt); + + MEM_SAFE_FREE(ss->last_paint_canvas_key); + + MEM_freeN(ss); + + ob->sculpt = nullptr; + } +} + +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 nullptr; + } + + bool need_mdisps = false; + + if (!CustomData_get_layer(&me->ldata, CD_MDISPS)) { + 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 nullptr; + } + + for (md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData); md; md = md->next) { + if (md->type == eModifierType_Multires) { + MultiresModifierData *mmd = (MultiresModifierData *)md; + + if (!BKE_modifier_is_enabled(scene, md, eModifierMode_Realtime)) { + continue; + } + + 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 nullptr; + } + } + + 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 */ +static bool sculpt_modifiers_active(Scene *scene, Sculpt *sd, Object *ob) +{ + ModifierData *md; + Mesh *me = (Mesh *)ob->data; + VirtualModifierData virtualModifierData; + + if (ob->sculpt->bm || BKE_sculpt_multires_active(scene, ob)) { + return false; + } + + /* 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. */ + for (; md; md = md->next) { + const ModifierTypeInfo *mti = BKE_modifier_get_info(static_cast(md->type)); + if (!BKE_modifier_is_enabled(scene, md, eModifierMode_Realtime)) { + continue; + } + if (md->type == eModifierType_Multires && (ob->mode & OB_MODE_SCULPT)) { + MultiresModifierData *mmd = (MultiresModifierData *)md; + if (!(mmd->flags & eMultiresModifierFlag_UseSculptBaseMesh)) { + continue; + } + } + if (md->type == eModifierType_ShapeKey) { + continue; + } + + if (mti->type == eModifierTypeType_OnlyDeform) { + return true; + } + if ((sd->flags & SCULPT_ONLY_DEFORM) == 0) { + return true; + } + } + + return false; +} + +/* 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_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; + 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); + ss->show_mask = (sd->flags & SCULPT_HIDE_MASK) == 0; + ss->show_face_sets = (sd->flags & SCULPT_HIDE_FACE_SETS) == 0; + + ss->building_vp_handle = false; + + ss->scene = scene; + + 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. */ + if (mmd) { + ss->multires.active = true; + ss->multires.modifier = mmd; + ss->multires.level = mmd->sculptlvl; + ss->totvert = me_eval->totvert; + ss->totpoly = me_eval->totpoly; + ss->totfaces = me->totpoly; + + /* 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 = 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 = 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 = nullptr; + ss->multires.level = 0; + ss->vmask = static_cast(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 = static_cast(layer->data); + } + else { + ss->mcol = static_cast(layer->data); + } + + ss->vcol_domain = domain; + ss->vcol_type = static_cast(layer->type); + } + else { + ss->vcol = nullptr; + ss->mcol = nullptr; + + ss->vcol_type = (eCustomDataType)-1; + ss->vcol_domain = ATTR_DOMAIN_POINT; + } + } + + /* Sculpt Face Sets. */ + if (use_face_sets) { + ss->face_sets = static_cast(CustomData_get_layer(&me->pdata, CD_SCULPT_FACE_SETS)); + } + else { + 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); + BLI_assert(pbvh == ss->pbvh); + UNUSED_VARS_NDEBUG(pbvh); + + 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, + 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); + } + } + + pbvh_show_mask_set(ss->pbvh, ss->show_mask); + pbvh_show_face_sets_set(ss->pbvh, ss->show_face_sets); + + if (ss->deform_modifiers_active) { + /* 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, 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); + + for (a = 0; a < me->totvert; a++) { + invert_m3(ss->deform_imats[a]); + } + } + } + else { + BKE_sculptsession_free_deformMats(ss); + } + + 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 == nullptr) { + float(*vertCos)[3] = BKE_keyblock_convert_to_vertcos(ob, ss->shapekey_active); + + if (vertCos) { + if (!pbvh_deformed) { + /* apply shape keys coordinates to PBVH */ + BKE_pbvh_vert_coords_apply(ss->pbvh, vertCos, me->totvert); + } + if (ss->deform_cos == nullptr) { + ss->deform_cos = vertCos; + } + if (vertCos != ss->deform_cos) { + MEM_freeN(vertCos); + } + } + } + } + + if (is_paint_tool) { + /* + * We should rebuild the PBVH_pixels when painting canvas changes. + * + * The relevant changes are stored/encoded in the paint canvas key. + * These include the active uv map, and resolutions. + */ + 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 == 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; + BKE_pbvh_mark_rebuild_pixels(ss->pbvh); + } + else { + MEM_freeN(paint_canvas_key); + } + } + + /* We could be more precise when we have access to the active tool. */ + const bool use_paint_slots = (ob->mode & OB_MODE_SCULPT) != 0; + if (use_paint_slots) { + BKE_texpaint_slots_refresh_object(scene, ob); + } + } +} + +void BKE_sculpt_update_object_before_eval(Object *ob_eval) +{ + /* Update before mesh evaluation in the dependency graph. */ + SculptSession *ss = ob_eval->sculpt; + + if (ss && ss->building_vp_handle == false) { + if (!ss->cache && !ss->filter_cache && !ss->expand_cache) { + /* We free pbvh on changes, except in the middle of drawing a stroke + * since it can't deal with changing PVBH node organization, we hope + * topology does not change in the meantime .. weak. */ + sculptsession_free_pbvh(ob_eval); + + BKE_sculptsession_free_deformMats(ob_eval->sculpt); + + /* In vertex/weight paint, force maps to be rebuilt. */ + BKE_sculptsession_free_vwpaint_data(ob_eval->sculpt); + } + else { + PBVHNode **nodes; + int n, totnode; + + BKE_pbvh_search_gather(ss->pbvh, nullptr, nullptr, &nodes, &totnode); + + for (n = 0; n < totnode; n++) { + BKE_pbvh_node_mark_update(nodes[n]); + } + + MEM_freeN(nodes); + } + } +} + +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); + + sculpt_update_object(depsgraph, ob_orig, ob_eval, false, false); +} + +void BKE_sculpt_color_layer_create_if_needed(Object *object) +{ + Mesh *orig_me = BKE_object_get_original_mesh(object); + + int types[] = {CD_PROP_COLOR, CD_PROP_BYTE_COLOR}; + bool has_color = false; + + for (int i = 0; i < ARRAY_SIZE(types); i++) { + has_color = CustomData_has_layer(&orig_me->vdata, types[i]) || + CustomData_has_layer(&orig_me->ldata, types[i]); + + if (has_color) { + break; + } + } + + if (has_color) { + return; + } + + 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_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); + + if (object->sculpt && object->sculpt->pbvh) { + BKE_pbvh_update_active_vcol(object->sculpt->pbvh, orig_me); + } +} + +void BKE_sculpt_update_object_for_edit( + 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); + + 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(CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS)); + } + + const AttributeAccessor attributes = mesh->attributes_for_write(); + const VArray hide_poly = attributes.lookup_or_default( + ".hide_poly", ATTR_DOMAIN_FACE, false); + + MutableSpan face_sets = { + static_cast(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( + CustomData_get_layer_named(&mesh->pdata, CD_PROP_BOOL, ".hide_poly"))) { + return hide_poly; + } + return static_cast(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) +{ + Mesh *me = static_cast(ob->data); + const Span polys = me->polys(); + const Span loops = me->loops(); + int ret = 0; + + const float *paint_mask = static_cast( + CustomData_get_layer(&me->vdata, CD_PAINT_MASK)); + + /* if multires is active, create a grid paint mask layer if there + * isn't one already */ + if (mmd && !CustomData_has_layer(&me->ldata, CD_GRID_PAINT_MASK)) { + GridPaintMask *gmask; + int level = max_ii(1, mmd->sculptlvl); + int gridsize = BKE_ccg_gridsize(level); + int gridarea = gridsize * gridsize; + int i, j; + + gmask = static_cast(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 = static_cast( + 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 = &polys[i]; + float avg = 0; + + /* mask center */ + for (j = 0; j < p->totloop; j++) { + const MLoop *l = &loops[p->loopstart + j]; + avg += paint_mask[l->v]; + } + avg /= (float)p->totloop; + + /* fill in multires mask corner */ + for (j = 0; j < p->totloop; j++) { + GridPaintMask *gpm = &gmask[p->loopstart + 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; + gpm->data[2] = (paint_mask[l->v] + paint_mask[prev->v]) * 0.5f; + gpm->data[3] = paint_mask[l->v]; + } + } + } + /* 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. */ + if (!paint_mask) { + 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(Scene *scene) +{ + BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->sculpt); + + Sculpt *sd = scene->toolsettings->sculpt; + if (!sd->detail_size) { + sd->detail_size = 12; + } + if (!sd->detail_percent) { + sd->detail_percent = 25; + } + if (sd->constant_detail == 0.0f) { + sd->constant_detail = 3.0f; + } + + /* Set sane default tiling offsets. */ + if (!sd->paint.tile_offset[0]) { + sd->paint.tile_offset[0] = 1.0f; + } + if (!sd->paint.tile_offset[1]) { + sd->paint.tile_offset[1] = 1.0f; + } + if (!sd->paint.tile_offset[2]) { + sd->paint.tile_offset[2] = 1.0f; + } +} + +static bool check_sculpt_object_deformed(Object *object, const bool for_construction) +{ + bool deformed = false; + + /* 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. */ + deformed |= object->sculpt->deform_modifiers_active; + + if (for_construction) { + 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. */ + deformed |= object->sculpt->shapekey_active && (object->shapeflag & OB_SHAPE_LOCK) == 0; + } + + return deformed; +} + +void BKE_sculpt_sync_face_visibility_to_grids(Mesh *mesh, SubdivCCG *subdiv_ccg) +{ + using namespace blender; + using namespace blender::bke; + if (!subdiv_ccg) { + return; + } + + const AttributeAccessor attributes = mesh->attributes(); + const VArray hide_poly = attributes.lookup_or_default( + ".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 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 = 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 + * visible. */ + if (is_hidden) { + BKE_subdiv_ccg_grid_hidden_ensure(subdiv_ccg, i); + } + + BLI_bitmap *gh = subdiv_ccg->grid_hidden[i]; + if (gh) { + BLI_bitmap_set_all(gh, is_hidden, key.grid_area); + } + } +} + +static PBVH *build_pbvh_for_dynamic_topology(Object *ob) +{ + PBVH *pbvh = ob->sculpt->pbvh = BKE_pbvh_new(PBVH_BMESH); + + sculptsession_bmesh_add_layers(ob); + + BKE_pbvh_build_bmesh(pbvh, + ob->sculpt->bm, + ob->sculpt->bm_smooth_shading, + ob->sculpt->bm_log, + 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; +} + +static PBVH *build_pbvh_from_regular_mesh(Object *ob, Mesh *me_eval_deform, bool respect_hide) +{ + 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_FACES); + BKE_pbvh_respect_hide_set(pbvh, respect_hide); + + MutableSpan verts = me->verts_for_write(); + const Span polys = me->polys(); + const Span loops = me->loops(); + + MLoopTri *looptri = static_cast( + MEM_malloc_arrayN(looptris_num, sizeof(*looptri), __func__)); + + BKE_mesh_recalc_looptri( + loops.data(), polys.data(), verts.data(), me->totloop, me->totpoly, looptri); + + BKE_pbvh_build_mesh(pbvh, + me, + polys.data(), + loops.data(), + verts.data(), + me->totvert, + &me->vdata, + &me->ldata, + &me->pdata, + looptri, + looptris_num); + + pbvh_show_mask_set(pbvh, ob->sculpt->show_mask); + 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 != 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); + MEM_freeN(v_cos); + } + + return pbvh; +} + +static PBVH *build_pbvh_from_ccg(Object *ob, SubdivCCG *subdiv_ccg, bool respect_hide) +{ + CCGKey key; + BKE_subdiv_ccg_key_top_level(&key, subdiv_ccg); + 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_visibility_to_grids(base_mesh, subdiv_ccg); + + BKE_pbvh_build_grids(pbvh, + subdiv_ccg->grids, + subdiv_ccg->num_grids, + &key, + (void **)subdiv_ccg->grid_faces, + subdiv_ccg->grid_flag_mats, + subdiv_ccg->grid_hidden); + pbvh_show_mask_set(pbvh, ob->sculpt->show_mask); + pbvh_show_face_sets_set(pbvh, ob->sculpt->show_face_sets); + return pbvh; +} + +PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob) +{ + if (ob == nullptr || ob->sculpt == nullptr) { + return nullptr; + } + + const bool respect_hide = true; + + PBVH *pbvh = ob->sculpt->pbvh; + 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 = static_cast(object_eval->data); + SubdivCCG *subdiv_ccg = mesh_eval->runtime.subdiv_ccg; + if (subdiv_ccg != nullptr) { + BKE_sculpt_bvh_update_from_ccg(pbvh, subdiv_ccg); + } + } + + BKE_pbvh_update_active_vcol(pbvh, BKE_object_get_original_mesh(ob)); + BKE_pbvh_pmap_set(pbvh, ob->sculpt->pmap); + + return pbvh; + } + + 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 = static_cast(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) { + Mesh *me_eval_deform = object_eval->runtime.mesh_deform_eval; + pbvh = build_pbvh_from_regular_mesh(ob, me_eval_deform, respect_hide); + } + } + + BKE_pbvh_pmap_set(pbvh, ob->sculpt->pmap); + + ob->sculpt->pbvh = pbvh; + return pbvh; +} + +void BKE_sculpt_bvh_update_from_ccg(PBVH *pbvh, SubdivCCG *subdiv_ccg) +{ + BKE_pbvh_grids_update(pbvh, + subdiv_ccg->grids, + (void **)subdiv_ccg->grid_faces, + subdiv_ccg->grid_flag_mats, + subdiv_ccg->grid_hidden); +} + +bool BKE_sculptsession_use_pbvh_draw(const Object *ob, const View3D *UNUSED(v3d)) +{ + SculptSession *ss = ob->sculpt; + if (ss == nullptr || ss->pbvh == nullptr || ss->mode_type != OB_MODE_SCULPT) { + return false; + } + + if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) { + /* Regular mesh only draws from PBVH without modifiers and shape keys. */ + + return !(ss->shapekey_active || ss->deform_modifiers_active); + } + + /* Multires and dyntopo always draw directly from the PBVH. */ + return true; +} + +/* Returns the Face Set random color for rendering in the overlay given its ID and a color seed. */ +#define GOLDEN_RATIO_CONJUGATE 0.618033988749895f +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 * (face_set + (seed % 10)); + random_mod_hue = random_mod_hue - floorf(random_mod_hue); + 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), + &rgba[0], + &rgba[1], + &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), + ¶ms, + 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), + ¶ms, + 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,16 +4165,13 @@ 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; 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 -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 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(pbvh.color_layer->data)[vertex], r_color); + to_float(static_cast(pbvh.color_layer->data)[index], r_color); } } template -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 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(pbvh.color_layer->data)[vertex]); + from_float(color, static_cast(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(*pbvh, indices[i], r_colors[i]); + blender::bke::pbvh_vertex_color_get(*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(node->pixels.node_data); - init_triangles(pbvh, node, node_data, mesh->mloop); + const Span 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(POINTCLOUD_ATTR_POSITION, + ATTR_DOMAIN_POINT); + blender::bke::SpanAttributeWriter radii = + attributes.lookup_or_add_for_write_only_span(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(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> point_cloud_bounds( const PointCloud &pointcloud) { - Span positions{reinterpret_cast(pointcloud.co), pointcloud.totpoint}; - if (pointcloud.radius) { - Span radii{pointcloud.radius, pointcloud.totpoint}; - return blender::bounds::min_max_with_radii(positions, radii); + blender::bke::AttributeAccessor attributes = pointcloud.attributes(); + blender::VArraySpan positions = attributes.lookup_or_default( + POINTCLOUD_ATTR_POSITION, ATTR_DOMAIN_POINT, float3(0)); + blender::VArray radii = attributes.lookup_or_default( + 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( - CustomData_get_layer_named(&pointcloud->pdata, CD_PROP_FLOAT3, POINTCLOUD_ATTR_POSITION)); - pointcloud->radius = static_cast( - 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(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(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(view_layer->object_bases.first); + BKE_view_layer_synced_ensure(*scene, view_layer); + *base = static_cast(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(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(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(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(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(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(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(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(); - case CURVE_TYPE_BEZIER: - return std::make_unique(); - case CURVE_TYPE_NURBS: - return std::make_unique(); - 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 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().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 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 positions, - const bool is_cyclic, - MutableSpan 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 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 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 positions, - const bool is_cyclic, - MutableSpan 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 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 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 tangents, MutableSpan 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 ¤t_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 tangents, - const bool cyclic, - MutableSpan 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 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 tangents = this->evaluated_tangents(); - MutableSpan 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 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 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 Spline::sample_uniform_index_factors(const int samples_num) const -{ - const Span lengths = this->evaluated_lengths(); - - BLI_assert(samples_num > 0); - Array 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 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 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 src_typed = src.typed(); - MutableSpan dst_typed = dst.typed(); - 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(dst); - bezier.resolution_ = resolution_; -} - -void BezierSpline::copy_data(Spline &dst) const -{ - BezierSpline &bezier = static_cast(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 BezierSpline::positions() -{ - return positions_; -} -Span BezierSpline::positions() const -{ - return positions_; -} -MutableSpan BezierSpline::radii() -{ - return radii_; -} -Span BezierSpline::radii() const -{ - return radii_; -} -MutableSpan BezierSpline::tilts() -{ - return tilts_; -} -Span BezierSpline::tilts() const -{ - return tilts_; -} -Span BezierSpline::handle_types_left() const -{ - return handle_types_left_; -} -MutableSpan BezierSpline::handle_types_left() -{ - return handle_types_left_; -} -Span BezierSpline::handle_positions_left() const -{ - this->ensure_auto_handles(); - return handle_positions_left_; -} -MutableSpan BezierSpline::handle_positions_left(const bool write_only) -{ - if (!write_only) { - this->ensure_auto_handles(); - } - return handle_positions_left_; -} - -Span BezierSpline::handle_types_right() const -{ - return handle_types_right_; -} -MutableSpan BezierSpline::handle_types_right() -{ - return handle_types_right_; -} -Span BezierSpline::handle_positions_right() const -{ - this->ensure_auto_handles(); - return handle_positions_right_; -} -MutableSpan 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 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 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(handle_types_right_[index]), - static_cast(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(handle_types_right_[index]), - static_cast(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 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 result) -{ - BLI_assert(result.size() > 0); - const float inv_len = 1.0f / static_cast(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 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 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 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 offsets, - const int size, - const int resolution, - const bool is_cyclic, - MutableSpan 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 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 mappings = evaluated_mapping_cache_; - - if (eval_num == 1) { - mappings.first() = 0.0f; - mapping_cache_dirty_ = false; - return mappings; - } - - Span 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 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 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 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 -static void interpolate_to_evaluated_impl(const BezierSpline &spline, - const blender::VArray &src, - MutableSpan dst) -{ - BLI_assert(src.size() == spline.size()); - BLI_assert(dst.size() == spline.evaluated_points_num()); - Span 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>) { - Array values(eval_num); - interpolate_to_evaluated_impl(*this, src.typed(), values); - new_varray = VArray::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(dst); - nurbs.knots_mode = knots_mode; - nurbs.resolution_ = resolution_; - nurbs.order_ = order_; -} - -void NURBSpline::copy_data(Spline &dst) const -{ - NURBSpline &nurbs = static_cast(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 NURBSpline::positions() -{ - return positions_; -} -Span NURBSpline::positions() const -{ - return positions_; -} -MutableSpan NURBSpline::radii() -{ - return radii_; -} -Span NURBSpline::radii() const -{ - return radii_; -} -MutableSpan NURBSpline::tilts() -{ - return tilts_; -} -Span NURBSpline::tilts() const -{ - return tilts_; -} -MutableSpan NURBSpline::weights() -{ - return weights_; -} -Span 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 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 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 knots, - MutableSpan 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 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 basis_weights(basis_cache_.weights); - MutableSpan basis_start_indices(basis_cache_.start_indices); - - const Span control_weights = this->weights(); - const Span 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 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 -void interpolate_to_evaluated_impl(const NURBSpline::BasisCache &basis_cache, - const int order, - const blender::VArray &src, - MutableSpan dst) -{ - const int num = src.size(); - blender::attribute_math::DefaultMixer mixer(dst); - - for (const int i : dst.index_range()) { - Span 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>) { - Array values(this->evaluated_points_num()); - interpolate_to_evaluated_impl(basis_cache, this->order(), src.typed(), values); - new_varray = VArray::ForContainer(std::move(values)); - } - }); - - return new_varray; -} - -Span 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 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(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 PolySpline::positions() -{ - return positions_; -} -Span PolySpline::positions() const -{ - return positions_; -} -MutableSpan PolySpline::radii() -{ - return radii_; -} -Span PolySpline::radii() const -{ - return radii_; -} -MutableSpan PolySpline::tilts() -{ - return tilts_; -} -Span 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 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.c deleted file mode 100644 index 433bad34479..00000000000 --- a/source/blender/blenkernel/intern/subdiv_mesh.c +++ /dev/null @@ -1,1201 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright 2018 Blender Foundation. All rights reserved. */ - -/** \file - * \ingroup bke - */ - -#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_bitmap.h" -#include "BLI_math_vector.h" - -#include "BKE_customdata.h" -#include "BKE_key.h" -#include "BKE_mesh.h" -#include "BKE_subdiv.h" -#include "BKE_subdiv_eval.h" -#include "BKE_subdiv_foreach.h" -#include "BKE_subdiv_mesh.h" - -#include "MEM_guardedalloc.h" - -/* -------------------------------------------------------------------- */ -/** \name Subdivision Context - * \{ */ - -typedef struct SubdivMeshContext { - const SubdivToMeshSettings *settings; - const Mesh *coarse_mesh; - Subdiv *subdiv; - Mesh *subdiv_mesh; - /* Cached custom data arrays for faster access. */ - int *vert_origindex; - int *edge_origindex; - int *loop_origindex; - int *poly_origindex; - /* UV layers interpolation. */ - int num_uv_layers; - MLoopUV *uv_layers[MAX_MTFACE]; - /* Original coordinates (ORCO) interpolation. */ - float (*orco)[3]; - float (*cloth_orco)[3]; - /* Per-subdivided vertex counter of averaged values. */ - int *accumulated_counters; - bool have_displacement; -} SubdivMeshContext; - -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); - } -} - -static void subdiv_mesh_ctx_cache_custom_data_layers(SubdivMeshContext *ctx) -{ - Mesh *subdiv_mesh = ctx->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); - /* 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); -} - -static void subdiv_mesh_prepare_accumulator(SubdivMeshContext *ctx, int num_vertices) -{ - if (!ctx->have_displacement) { - return; - } - ctx->accumulated_counters = MEM_calloc_arrayN( - num_vertices, sizeof(*ctx->accumulated_counters), "subdiv accumulated counters"); -} - -static void subdiv_mesh_context_free(SubdivMeshContext *ctx) -{ - MEM_SAFE_FREE(ctx->accumulated_counters); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Loop custom data copy helpers - * \{ */ - -typedef 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. */ - const MLoop *last_loop; - /* 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 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. - * - * TODO(sergey): Get rid of using module on every iteration. */ - const int last_ptex_loop_index = coarse_poly->loopstart + - (ptex_of_poly_index + coarse_poly->totloop - 1) % - coarse_poly->totloop; - loops_of_ptex->first_loop = &coarse_mloop[first_ptex_loop_index]; - loops_of_ptex->last_loop = &coarse_mloop[last_ptex_loop_index]; - if (coarse_poly->totloop == 4) { - loops_of_ptex->second_loop = loops_of_ptex->first_loop + 1; - loops_of_ptex->third_loop = loops_of_ptex->first_loop + 2; - } - else { - loops_of_ptex->second_loop = NULL; - loops_of_ptex->third_loop = NULL; - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Vertex custom data interpolation helpers - * \{ */ - -/* TODO(sergey): Somehow de-duplicate with loops storage, without too much - * exception cases all over the code. */ - -typedef 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. */ - const CustomData *vertex_data; - /* Vertices data calculated for ptex corners. There are always 4 elements - * in this custom data, aligned the following way: - * - * index 0 -> uv (0, 0) - * index 1 -> uv (0, 1) - * index 2 -> uv (1, 1) - * index 3 -> uv (1, 0) - * - * Is allocated for non-regular faces (triangles and n-gons). */ - CustomData vertex_data_storage; - bool vertex_data_storage_allocated; - /* 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; - if (coarse_poly->totloop == 4) { - vertex_interpolation->vertex_data = &coarse_mesh->vdata; - vertex_interpolation->vertex_indices[0] = coarse_mloop[coarse_poly->loopstart + 0].v; - vertex_interpolation->vertex_indices[1] = coarse_mloop[coarse_poly->loopstart + 1].v; - vertex_interpolation->vertex_indices[2] = coarse_mloop[coarse_poly->loopstart + 2].v; - vertex_interpolation->vertex_indices[3] = coarse_mloop[coarse_poly->loopstart + 3].v; - vertex_interpolation->vertex_data_storage_allocated = false; - } - else { - vertex_interpolation->vertex_data = &vertex_interpolation->vertex_data_storage; - /* Allocate storage for loops corresponding to ptex corners. */ - CustomData_copy(&ctx->coarse_mesh->vdata, - &vertex_interpolation->vertex_data_storage, - CD_MASK_EVERYTHING.vmask, - CD_CALLOC, - 4); - /* Initialize indices. */ - vertex_interpolation->vertex_indices[0] = 0; - vertex_interpolation->vertex_indices[1] = 1; - vertex_interpolation->vertex_indices[2] = 2; - vertex_interpolation->vertex_indices[3] = 3; - vertex_interpolation->vertex_data_storage_allocated = true; - /* 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); - 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, - coarse_poly->totloop, - 2); - } -} - -static void vertex_interpolation_from_corner(const SubdivMeshContext *ctx, - VerticesForInterpolation *vertex_interpolation, - const MPoly *coarse_poly, - const int corner) -{ - if (coarse_poly->totloop == 4) { - /* Nothing to do, all indices and data is already assigned. */ - } - else { - const CustomData *vertex_data = &ctx->coarse_mesh->vdata; - const Mesh *coarse_mesh = ctx->coarse_mesh; - const MLoop *coarse_mloop = coarse_mesh->mloop; - 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. */ - CustomData_copy_data(vertex_data, - &vertex_interpolation->vertex_data_storage, - coarse_mloop[coarse_poly->loopstart + corner].v, - 0, - 1); - /* Interpolate remaining ptex face corners, which hits loops - * middle points. - * - * TODO(sergey): Re-use one of interpolation results from previous - * iteration. */ - const float weights[2] = {0.5f, 0.5f}; - 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}; - const int last_indices[2] = { - coarse_mloop[first_loop_index].v, - coarse_mloop[last_loop_index].v, - }; - CustomData_interp(vertex_data, - &vertex_interpolation->vertex_data_storage, - first_indices, - weights, - NULL, - 2, - 1); - CustomData_interp(vertex_data, - &vertex_interpolation->vertex_data_storage, - last_indices, - weights, - NULL, - 2, - 3); - } -} - -static void vertex_interpolation_end(VerticesForInterpolation *vertex_interpolation) -{ - if (vertex_interpolation->vertex_data_storage_allocated) { - CustomData_free(&vertex_interpolation->vertex_data_storage, 4); - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Loop custom data interpolation helpers - * \{ */ - -typedef 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. */ - const CustomData *loop_data; - /* Loops data calculated for ptex corners. There are always 4 elements - * in this custom data, aligned the following way: - * - * index 0 -> uv (0, 0) - * index 1 -> uv (0, 1) - * index 2 -> uv (1, 1) - * index 3 -> uv (1, 0) - * - * 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 - * 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, - const MPoly *coarse_poly) -{ - const Mesh *coarse_mesh = ctx->coarse_mesh; - if (coarse_poly->totloop == 4) { - loop_interpolation->loop_data = &coarse_mesh->ldata; - loop_interpolation->loop_indices[0] = coarse_poly->loopstart + 0; - loop_interpolation->loop_indices[1] = coarse_poly->loopstart + 1; - loop_interpolation->loop_indices[2] = coarse_poly->loopstart + 2; - loop_interpolation->loop_indices[3] = coarse_poly->loopstart + 3; - loop_interpolation->loop_data_storage_allocated = false; - } - else { - loop_interpolation->loop_data = &loop_interpolation->loop_data_storage; - /* Allocate storage for loops corresponding to ptex corners. */ - CustomData_copy(&ctx->coarse_mesh->ldata, - &loop_interpolation->loop_data_storage, - CD_MASK_EVERYTHING.lmask, - CD_CALLOC, - 4); - /* Initialize indices. */ - loop_interpolation->loop_indices[0] = 0; - loop_interpolation->loop_indices[1] = 1; - loop_interpolation->loop_indices[2] = 2; - loop_interpolation->loop_indices[3] = 3; - loop_interpolation->loop_data_storage_allocated = true; - /* 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); - 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, - coarse_poly->totloop, - 2); - } -} - -static void loop_interpolation_from_corner(const SubdivMeshContext *ctx, - LoopsForInterpolation *loop_interpolation, - const MPoly *coarse_poly, - const int corner) -{ - if (coarse_poly->totloop == 4) { - /* Nothing to do, all indices and data is already assigned. */ - } - else { - const CustomData *loop_data = &ctx->coarse_mesh->ldata; - const Mesh *coarse_mesh = ctx->coarse_mesh; - const MLoop *coarse_mloop = coarse_mesh->mloop; - 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. */ - CustomData_free_elem(&loop_interpolation->loop_data_storage, 0, 1); - CustomData_copy_data( - loop_data, &loop_interpolation->loop_data_storage, coarse_poly->loopstart + corner, 0, 1); - /* Interpolate remaining ptex face corners, which hits loops - * middle points. - * - * TODO(sergey): Re-use one of interpolation results from previous - * iteration. */ - const float weights[2] = {0.5f, 0.5f}; - const int base_loop_index = coarse_poly->loopstart; - const int first_loop_index = loops_of_ptex.first_loop - coarse_mloop; - const int second_loop_index = base_loop_index + - (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, - }; - CustomData_interp( - loop_data, &loop_interpolation->loop_data_storage, first_indices, weights, NULL, 2, 1); - CustomData_interp( - loop_data, &loop_interpolation->loop_data_storage, last_indices, weights, NULL, 2, 3); - } -} - -static void loop_interpolation_end(LoopsForInterpolation *loop_interpolation) -{ - if (loop_interpolation->loop_data_storage_allocated) { - CustomData_free(&loop_interpolation->loop_data_storage, 4); - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name TLS - * \{ */ - -typedef struct SubdivMeshTLS { - bool vertex_interpolation_initialized; - VerticesForInterpolation vertex_interpolation; - const MPoly *vertex_interpolation_coarse_poly; - int vertex_interpolation_coarse_corner; - - bool loop_interpolation_initialized; - 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; - if (tls->vertex_interpolation_initialized) { - vertex_interpolation_end(&tls->vertex_interpolation); - } - if (tls->loop_interpolation_initialized) { - loop_interpolation_end(&tls->loop_interpolation); - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Evaluation helper functions - * \{ */ - -static void subdiv_vertex_orco_evaluate(const SubdivMeshContext *ctx, - const int ptex_face_index, - const float u, - const float v, - const int subdiv_vertex_index) -{ - if (ctx->orco || ctx->cloth_orco) { - float vertex_data[6]; - BKE_subdiv_eval_vertex_data(ctx->subdiv, ptex_face_index, u, v, vertex_data); - - if (ctx->orco) { - copy_v3_v3(ctx->orco[subdiv_vertex_index], vertex_data); - if (ctx->cloth_orco) { - copy_v3_v3(ctx->orco[subdiv_vertex_index], vertex_data + 3); - } - } - else if (ctx->cloth_orco) { - copy_v3_v3(ctx->orco[subdiv_vertex_index], vertex_data); - } - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Accumulation helpers - * \{ */ - -static void subdiv_accumulate_vertex_displacement(SubdivMeshContext *ctx, - const int ptex_face_index, - const float u, - const float v, - MVert *subdiv_vert) -{ - /* Accumulate displacement. */ - Subdiv *subdiv = ctx->subdiv; - const int subdiv_vertex_index = subdiv_vert - ctx->subdiv_mesh->mvert; - 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); - - /* NOTE: The subdivided mesh is allocated in this module, and its vertices are kept at zero - * locations as a default calloc(). */ - BKE_subdiv_eval_displacement(subdiv, ptex_face_index, u, v, dPdu, dPdv, D); - add_v3_v3(subdiv_vert->co, D); - - if (ctx->accumulated_counters) { - ++ctx->accumulated_counters[subdiv_vertex_index]; - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Callbacks - * \{ */ - -static bool subdiv_mesh_topology_info(const SubdivForeachContext *foreach_context, - const int num_vertices, - const int num_edges, - const int num_loops, - const int num_polygons, - const int *UNUSED(subdiv_polygon_offset)) -{ - /* Multi-resolution grid data will be applied or become invalid after subdivision, - * so don't try to preserve it and use memory. */ - CustomData_MeshMasks mask = CD_MASK_EVERYTHING; - mask.lmask &= ~CD_MASK_MULTIRES_GRIDS; - - SubdivMeshContext *subdiv_context = 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); - subdiv_mesh_prepare_accumulator(subdiv_context, num_vertices); - MEM_SAFE_FREE(subdiv_context->subdiv_mesh->runtime.subsurf_face_dot_tags); - subdiv_context->subdiv_mesh->runtime.subsurf_face_dot_tags = BLI_BITMAP_NEW(num_vertices, - __func__); - return true; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Vertex subdivision process - * \{ */ - -static void subdiv_vertex_data_copy(const SubdivMeshContext *ctx, - const MVert *coarse_vertex, - 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; - CustomData_copy_data( - &coarse_mesh->vdata, &ctx->subdiv_mesh->vdata, coarse_vertex_index, subdiv_vertex_index, 1); -} - -static void subdiv_vertex_data_interpolate(const SubdivMeshContext *ctx, - MVert *subdiv_vertex, - const VerticesForInterpolation *vertex_interpolation, - const float u, - const float v) -{ - const int subdiv_vertex_index = subdiv_vertex - ctx->subdiv_mesh->mvert; - 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, - 4, - subdiv_vertex_index); - if (ctx->vert_origindex != NULL) { - ctx->vert_origindex[subdiv_vertex_index] = ORIGINDEX_NONE; - } -} - -static void evaluate_vertex_and_apply_displacement_copy(const SubdivMeshContext *ctx, - const int ptex_face_index, - const float u, - const float v, - const MVert *coarse_vert, - MVert *subdiv_vert) -{ - const int subdiv_vertex_index = subdiv_vert - ctx->subdiv_mesh->mvert; - /* 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}; - if (ctx->have_displacement) { - const float inv_num_accumulated = 1.0f / ctx->accumulated_counters[subdiv_vertex_index]; - copy_v3_v3(D, subdiv_vert->co); - mul_v3_fl(D, inv_num_accumulated); - } - /* Copy custom data and evaluate position. */ - subdiv_vertex_data_copy(ctx, coarse_vert, subdiv_vert); - BKE_subdiv_eval_limit_point(ctx->subdiv, ptex_face_index, u, v, subdiv_vert->co); - /* Apply displacement. */ - 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. */ - BLI_BITMAP_DISABLE(ctx->subdiv_mesh->runtime.subsurf_face_dot_tags, subdiv_vertex_index); -} - -static void evaluate_vertex_and_apply_displacement_interpolate( - const SubdivMeshContext *ctx, - const int ptex_face_index, - const float u, - const float v, - VerticesForInterpolation *vertex_interpolation, - MVert *subdiv_vert) -{ - const int subdiv_vertex_index = subdiv_vert - ctx->subdiv_mesh->mvert; - /* 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}; - if (ctx->have_displacement) { - const float inv_num_accumulated = 1.0f / ctx->accumulated_counters[subdiv_vertex_index]; - copy_v3_v3(D, subdiv_vert->co); - mul_v3_fl(D, inv_num_accumulated); - } - /* Interpolate custom data and evaluate position. */ - subdiv_vertex_data_interpolate(ctx, subdiv_vert, vertex_interpolation, u, v); - BKE_subdiv_eval_limit_point(ctx->subdiv, ptex_face_index, u, v, subdiv_vert->co); - /* Apply displacement. */ - add_v3_v3(subdiv_vert->co, D); - /* Evaluate undeformed texture coordinate. */ - subdiv_vertex_orco_evaluate(ctx, ptex_face_index, u, v, subdiv_vertex_index); -} - -static void subdiv_mesh_vertex_displacement_every_corner_or_edge( - const SubdivForeachContext *foreach_context, - void *UNUSED(tls), - const int ptex_face_index, - const float u, - 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]; - subdiv_accumulate_vertex_displacement(ctx, ptex_face_index, u, v, subdiv_vert); -} - -static void subdiv_mesh_vertex_displacement_every_corner( - const SubdivForeachContext *foreach_context, - void *tls, - const int ptex_face_index, - const float u, - const float v, - const int UNUSED(coarse_vertex_index), - const int UNUSED(coarse_poly_index), - const int UNUSED(coarse_corner), - const int subdiv_vertex_index) -{ - subdiv_mesh_vertex_displacement_every_corner_or_edge( - foreach_context, tls, ptex_face_index, u, v, subdiv_vertex_index); -} - -static void subdiv_mesh_vertex_displacement_every_edge(const SubdivForeachContext *foreach_context, - void *tls, - const int ptex_face_index, - const float u, - const float v, - const int UNUSED(coarse_edge_index), - const int UNUSED(coarse_poly_index), - const int UNUSED(coarse_corner), - const int subdiv_vertex_index) -{ - subdiv_mesh_vertex_displacement_every_corner_or_edge( - foreach_context, tls, ptex_face_index, u, v, subdiv_vertex_index); -} - -static void subdiv_mesh_vertex_corner(const SubdivForeachContext *foreach_context, - void *UNUSED(tls), - const int ptex_face_index, - const float u, - const float v, - const int coarse_vertex_index, - const int UNUSED(coarse_poly_index), - const int UNUSED(coarse_corner), - 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]; - evaluate_vertex_and_apply_displacement_copy( - ctx, ptex_face_index, u, v, coarse_vert, subdiv_vert); -} - -static void subdiv_mesh_ensure_vertex_interpolation(SubdivMeshContext *ctx, - SubdivMeshTLS *tls, - const MPoly *coarse_poly, - const int coarse_corner) -{ - /* Check whether we've moved to another corner or polygon. */ - if (tls->vertex_interpolation_initialized) { - if (tls->vertex_interpolation_coarse_poly != coarse_poly || - tls->vertex_interpolation_coarse_corner != coarse_corner) { - vertex_interpolation_end(&tls->vertex_interpolation); - tls->vertex_interpolation_initialized = false; - } - } - /* Initialize the interpolation. */ - if (!tls->vertex_interpolation_initialized) { - vertex_interpolation_init(ctx, &tls->vertex_interpolation, coarse_poly); - } - /* Update it for a new corner if needed. */ - if (!tls->vertex_interpolation_initialized || - tls->vertex_interpolation_coarse_corner != coarse_corner) { - vertex_interpolation_from_corner(ctx, &tls->vertex_interpolation, coarse_poly, coarse_corner); - } - /* Store settings used for the current state of interpolator. */ - tls->vertex_interpolation_initialized = true; - tls->vertex_interpolation_coarse_poly = coarse_poly; - tls->vertex_interpolation_coarse_corner = coarse_corner; -} - -static void subdiv_mesh_vertex_edge(const SubdivForeachContext *foreach_context, - void *tls_v, - const int ptex_face_index, - const float u, - const float v, - const int UNUSED(coarse_edge_index), - const int coarse_poly_index, - 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]; - 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); -} - -static bool subdiv_mesh_is_center_vertex(const MPoly *coarse_poly, const float u, const float v) -{ - if (coarse_poly->totloop == 4) { - if (u == 0.5f && v == 0.5f) { - return true; - } - } - else { - if (u == 1.0f && v == 1.0f) { - return true; - } - } - return false; -} - -static void subdiv_mesh_tag_center_vertex(const MPoly *coarse_poly, - const int subdiv_vertex_index, - const float u, - const float v, - Mesh *subdiv_mesh) -{ - if (subdiv_mesh_is_center_vertex(coarse_poly, u, v)) { - BLI_BITMAP_ENABLE(subdiv_mesh->runtime.subsurf_face_dot_tags, subdiv_vertex_index); - } -} - -static void subdiv_mesh_vertex_inner(const SubdivForeachContext *foreach_context, - void *tls_v, - const int ptex_face_index, - const float u, - const float v, - const int coarse_poly_index, - const int coarse_corner, - const int subdiv_vertex_index) -{ - SubdivMeshContext *ctx = foreach_context->user_data; - SubdivMeshTLS *tls = 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]; - Mesh *subdiv_mesh = ctx->subdiv_mesh; - MVert *subdiv_mvert = subdiv_mesh->mvert; - MVert *subdiv_vert = &subdiv_mvert[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); - subdiv_mesh_tag_center_vertex(coarse_poly, subdiv_vertex_index, u, v, subdiv_mesh); - subdiv_vertex_orco_evaluate(ctx, ptex_face_index, u, v, subdiv_vertex_index); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Edge subdivision process - * \{ */ - -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) { - 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) { - ctx->edge_origindex[subdiv_edge_index] = ORIGINDEX_NONE; - } - return; - } - const int coarse_edge_index = coarse_edge - ctx->coarse_mesh->medge; - CustomData_copy_data( - &ctx->coarse_mesh->edata, &ctx->subdiv_mesh->edata, coarse_edge_index, subdiv_edge_index, 1); - subdiv_edge->flag |= ME_EDGERENDER; -} - -static void subdiv_mesh_edge(const SubdivForeachContext *foreach_context, - void *UNUSED(tls), - const int coarse_edge_index, - const int subdiv_edge_index, - const bool UNUSED(is_loose), - 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; - MEdge *subdiv_edge = &subdiv_medge[subdiv_edge_index]; - const MEdge *coarse_edge = NULL; - if (coarse_edge_index != ORIGINDEX_NONE) { - const Mesh *coarse_mesh = ctx->coarse_mesh; - const MEdge *coarse_medge = coarse_mesh->medge; - coarse_edge = &coarse_medge[coarse_edge_index]; - } - subdiv_copy_edge_data(ctx, subdiv_edge, coarse_edge); - subdiv_edge->v1 = subdiv_v1; - subdiv_edge->v2 = subdiv_v2; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Loops creation/interpolation - * \{ */ - -static void subdiv_interpolate_loop_data(const SubdivMeshContext *ctx, - MLoop *subdiv_loop, - const LoopsForInterpolation *loop_interpolation, - const float u, - const float v) -{ - const int subdiv_loop_index = subdiv_loop - ctx->subdiv_mesh->mloop; - 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, - 4, - subdiv_loop_index); - /* TODO(sergey): Set ORIGINDEX. */ -} - -static void subdiv_eval_uv_layer(SubdivMeshContext *ctx, - MLoop *subdiv_loop, - const int ptex_face_index, - const float u, - const float v) -{ - if (ctx->num_uv_layers == 0) { - return; - } - Subdiv *subdiv = ctx->subdiv; - const int mloop_index = subdiv_loop - ctx->subdiv_mesh->mloop; - 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); - } -} - -static void subdiv_mesh_ensure_loop_interpolation(SubdivMeshContext *ctx, - SubdivMeshTLS *tls, - const MPoly *coarse_poly, - const int coarse_corner) -{ - /* Check whether we've moved to another corner or polygon. */ - if (tls->loop_interpolation_initialized) { - if (tls->loop_interpolation_coarse_poly != coarse_poly || - tls->loop_interpolation_coarse_corner != coarse_corner) { - loop_interpolation_end(&tls->loop_interpolation); - tls->loop_interpolation_initialized = false; - } - } - /* Initialize the interpolation. */ - if (!tls->loop_interpolation_initialized) { - loop_interpolation_init(ctx, &tls->loop_interpolation, coarse_poly); - } - /* Update it for a new corner if needed. */ - if (!tls->loop_interpolation_initialized || - tls->loop_interpolation_coarse_corner != coarse_corner) { - loop_interpolation_from_corner(ctx, &tls->loop_interpolation, coarse_poly, coarse_corner); - } - /* Store settings used for the current state of interpolator. */ - tls->loop_interpolation_initialized = true; - tls->loop_interpolation_coarse_poly = coarse_poly; - tls->loop_interpolation_coarse_corner = coarse_corner; -} - -static void subdiv_mesh_loop(const SubdivForeachContext *foreach_context, - void *tls_v, - const int ptex_face_index, - const float u, - const float v, - const int UNUSED(coarse_loop_index), - const int coarse_poly_index, - const int coarse_corner, - const int subdiv_loop_index, - 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; - 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]; - 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); - subdiv_loop->v = subdiv_vertex_index; - subdiv_loop->e = subdiv_edge_index; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Polygons subdivision process - * \{ */ - -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; - CustomData_copy_data( - &ctx->coarse_mesh->pdata, &ctx->subdiv_mesh->pdata, coarse_poly_index, subdiv_poly_index, 1); -} - -static void subdiv_mesh_poly(const SubdivForeachContext *foreach_context, - void *UNUSED(tls), - const int coarse_poly_index, - const int subdiv_poly_index, - const int start_loop_index, - 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]; - subdiv_copy_poly_data(ctx, subdiv_poly, coarse_poly); - subdiv_poly->loopstart = start_loop_index; - subdiv_poly->totloop = num_loops; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Loose elements subdivision process - * \{ */ - -static void subdiv_mesh_vertex_loose(const SubdivForeachContext *foreach_context, - void *UNUSED(tls), - 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]; - 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, - const MEdge *neighbors[2]) -{ - const MEdge *coarse_medge = coarse_mesh->medge; - neighbors[0] = NULL; - neighbors[1] = NULL; - 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) { - continue; - } - if (ELEM(edge->v1, current_edge->v1, current_edge->v2)) { - neighbors[0] = current_edge; - ++neighbor_counters[0]; - } - if (ELEM(edge->v2, current_edge->v1, current_edge->v2)) { - neighbors[1] = current_edge; - ++neighbor_counters[1]; - } - } - /* Vertices which has more than one neighbor are considered infinitely - * 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; - } - if (neighbor_counters[1] > 1) { - neighbors[1] = NULL; - } -} - -static void points_for_loose_edges_interpolation_get(const Mesh *coarse_mesh, - 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]->v1 == coarse_edge->v1) { - copy_v3_v3(points_r[0], coarse_mvert[neighbors[0]->v2].co); - } - else { - copy_v3_v3(points_r[0], coarse_mvert[neighbors[0]->v1].co); - } - } - else { - sub_v3_v3v3(points_r[0], points_r[1], points_r[2]); - 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]->v1 == coarse_edge->v2) { - copy_v3_v3(points_r[3], coarse_mvert[neighbors[1]->v2].co); - } - else { - copy_v3_v3(points_r[3], coarse_mvert[neighbors[1]->v1].co); - } - } - else { - sub_v3_v3v3(points_r[3], points_r[2], points_r[1]); - add_v3_v3(points_r[3], points_r[2]); - } -} - -void BKE_subdiv_mesh_interpolate_position_on_edge(const Mesh *coarse_mesh, - const MEdge *coarse_edge, - const bool is_simple, - const float u, - float pos_r[3]) -{ - 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]; - 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); - float points[4][3]; - points_for_loose_edges_interpolation_get(coarse_mesh, 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); - } -} - -static void subdiv_mesh_vertex_of_loose_edge_interpolate(SubdivMeshContext *ctx, - const MEdge *coarse_edge, - const float u, - const int subdiv_vertex_index) -{ - const Mesh *coarse_mesh = ctx->coarse_mesh; - Mesh *subdiv_mesh = ctx->subdiv_mesh; - /* This is never used for end-points (which are copied from the original). */ - 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}; - CustomData_interp(&coarse_mesh->vdata, - &subdiv_mesh->vdata, - coarse_vertex_indices, - interpolation_weights, - NULL, - 2, - subdiv_vertex_index); - if (ctx->vert_origindex != NULL) { - ctx->vert_origindex[subdiv_vertex_index] = ORIGINDEX_NONE; - } -} - -static void subdiv_mesh_vertex_of_loose_edge(const struct 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; - 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 bool is_simple = ctx->subdiv->settings.is_simple; - /* 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); - /* 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; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Initialization - * \{ */ - -static void setup_foreach_callbacks(const SubdivMeshContext *subdiv_context, - SubdivForeachContext *foreach_context) -{ - memset(foreach_context, 0, sizeof(*foreach_context)); - /* General information. */ - foreach_context->topology_info = subdiv_mesh_topology_info; - /* Every boundary geometry. Used for displacement averaging. */ - if (subdiv_context->have_displacement) { - foreach_context->vertex_every_corner = subdiv_mesh_vertex_displacement_every_corner; - foreach_context->vertex_every_edge = subdiv_mesh_vertex_displacement_every_edge; - } - foreach_context->vertex_corner = subdiv_mesh_vertex_corner; - foreach_context->vertex_edge = subdiv_mesh_vertex_edge; - foreach_context->vertex_inner = subdiv_mesh_vertex_inner; - foreach_context->edge = subdiv_mesh_edge; - foreach_context->loop = subdiv_mesh_loop; - foreach_context->poly = subdiv_mesh_poly; - foreach_context->vertex_loose = subdiv_mesh_vertex_loose; - foreach_context->vertex_of_loose_edge = subdiv_mesh_vertex_of_loose_edge; - foreach_context->user_data_tls_free = subdiv_mesh_tls_free; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Public entry point - * \{ */ - -Mesh *BKE_subdiv_to_mesh(Subdiv *subdiv, - const SubdivToMeshSettings *settings, - const Mesh *coarse_mesh) -{ - BKE_subdiv_stats_begin(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_MESH); - /* 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)) { - /* This could happen in two situations: - * - OpenSubdiv is disabled. - * - Something totally bad happened, and OpenSubdiv rejected our - * topology. - * 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; - } - } - /* Initialize subdivision mesh creation context. */ - SubdivMeshContext subdiv_context = {0}; - subdiv_context.settings = settings; - subdiv_context.coarse_mesh = coarse_mesh; - subdiv_context.subdiv = subdiv; - subdiv_context.have_displacement = (subdiv->displacement_evaluator != NULL); - /* Multi-threaded traversal/evaluation. */ - BKE_subdiv_stats_begin(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_MESH_GEOMETRY); - SubdivForeachContext foreach_context; - setup_foreach_callbacks(&subdiv_context, &foreach_context); - SubdivMeshTLS tls = {0}; - foreach_context.user_data = &subdiv_context; - foreach_context.user_data_tls_size = sizeof(SubdivMeshTLS); - foreach_context.user_data_tls = &tls; - BKE_subdiv_foreach_subdiv_geometry(subdiv, &foreach_context, settings, coarse_mesh); - BKE_subdiv_stats_end(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_MESH_GEOMETRY); - Mesh *result = subdiv_context.subdiv_mesh; - // BKE_mesh_validate(result, true, true); - BKE_subdiv_stats_end(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_MESH); - /* Using normals from the limit surface gives different results than Blender's vertex normal - * calculation. Since vertex normals are supposed to be a consistent cache, don't bother - * calculating them here. The work may have been pointless anyway if the mesh is deformed or - * changed afterwards. */ - BLI_assert(BKE_mesh_vertex_normals_are_dirty(result) || BKE_mesh_poly_normals_are_dirty(result)); - /* Free used memory. */ - subdiv_mesh_context_free(&subdiv_context); - return result; -} - -/** \} */ diff --git a/source/blender/blenkernel/intern/subdiv_mesh.cc b/source/blender/blenkernel/intern/subdiv_mesh.cc new file mode 100644 index 00000000000..44bdd6e6d06 --- /dev/null +++ b/source/blender/blenkernel/intern/subdiv_mesh.cc @@ -0,0 +1,1230 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2018 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup bke + */ + +#include + +#include "atomic_ops.h" + +#include "DNA_key_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.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" +#include "BKE_subdiv_mesh.h" + +#include "MEM_guardedalloc.h" + +using blender::Span; + +/* -------------------------------------------------------------------- */ +/** \name Subdivision Context + * \{ */ + +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; + int *loop_origindex; + int *poly_origindex; + /* UV layers interpolation. */ + int num_uv_layers; + MLoopUV *uv_layers[MAX_MTFACE]; + /* Original coordinates (ORCO) interpolation. */ + float (*orco)[3]; + float (*cloth_orco)[3]; + /* Per-subdivided vertex counter of averaged values. */ + int *accumulated_counters; + bool have_displacement; + + /* 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] = static_cast( + 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 = static_cast( + CustomData_get_layer(&subdiv_mesh->vdata, CD_ORIGINDEX)); + ctx->edge_origindex = static_cast( + CustomData_get_layer(&subdiv_mesh->edata, CD_ORIGINDEX)); + ctx->loop_origindex = static_cast( + CustomData_get_layer(&subdiv_mesh->ldata, CD_ORIGINDEX)); + ctx->poly_origindex = static_cast( + CustomData_get_layer(&subdiv_mesh->pdata, CD_ORIGINDEX)); + /* UV layers interpolation. */ + subdiv_mesh_ctx_cache_uv_layers(ctx); + /* Orco interpolation. */ + ctx->orco = static_cast(CustomData_get_layer(&subdiv_mesh->vdata, CD_ORCO)); + ctx->cloth_orco = static_cast( + CustomData_get_layer(&subdiv_mesh->vdata, CD_CLOTH_ORCO)); +} + +static void subdiv_mesh_prepare_accumulator(SubdivMeshContext *ctx, int num_vertices) +{ + if (!ctx->have_displacement) { + return; + } + ctx->accumulated_counters = static_cast( + 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); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Loop custom data copy helpers + * \{ */ + +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. */ + const MLoop *last_loop; + /* For quad coarse faces only. */ + const MLoop *second_loop; + const MLoop *third_loop; +}; + +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_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. + * + * TODO(sergey): Get rid of using module on every iteration. */ + const int last_ptex_loop_index = coarse_poly->loopstart + + (ptex_of_poly_index + coarse_poly->totloop - 1) % + coarse_poly->totloop; + loops_of_ptex->first_loop = &coarse_mloop[first_ptex_loop_index]; + loops_of_ptex->last_loop = &coarse_mloop[last_ptex_loop_index]; + if (coarse_poly->totloop == 4) { + loops_of_ptex->second_loop = loops_of_ptex->first_loop + 1; + loops_of_ptex->third_loop = loops_of_ptex->first_loop + 2; + } + else { + loops_of_ptex->second_loop = nullptr; + loops_of_ptex->third_loop = nullptr; + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vertex custom data interpolation helpers + * \{ */ + +/* TODO(sergey): Somehow de-duplicate with loops storage, without too much + * exception cases all over the code. */ + +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. */ + const CustomData *vertex_data; + /* Vertices data calculated for ptex corners. There are always 4 elements + * in this custom data, aligned the following way: + * + * index 0 -> uv (0, 0) + * index 1 -> uv (0, 1) + * index 2 -> uv (1, 1) + * index 3 -> uv (1, 0) + * + * Is allocated for non-regular faces (triangles and n-gons). */ + CustomData vertex_data_storage; + bool vertex_data_storage_allocated; + /* 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]; +}; + +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 = 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; + vertex_interpolation->vertex_indices[1] = coarse_mloop[coarse_poly->loopstart + 1].v; + vertex_interpolation->vertex_indices[2] = coarse_mloop[coarse_poly->loopstart + 2].v; + vertex_interpolation->vertex_indices[3] = coarse_mloop[coarse_poly->loopstart + 3].v; + vertex_interpolation->vertex_data_storage_allocated = false; + } + else { + vertex_interpolation->vertex_data = &vertex_interpolation->vertex_data_storage; + /* Allocate storage for loops corresponding to ptex corners. */ + CustomData_copy(&ctx->coarse_mesh->vdata, + &vertex_interpolation->vertex_data_storage, + CD_MASK_EVERYTHING.vmask, + CD_SET_DEFAULT, + 4); + /* Initialize indices. */ + vertex_interpolation->vertex_indices[0] = 0; + vertex_interpolation->vertex_indices[1] = 1; + vertex_interpolation->vertex_indices[2] = 2; + vertex_interpolation->vertex_indices[3] = 3; + vertex_interpolation->vertex_data_storage_allocated = true; + /* Interpolate center of poly right away, it stays unchanged for all + * ptex faces. */ + const float weight = 1.0f / (float)coarse_poly->totloop; + blender::Array weights(coarse_poly->totloop); + blender::Array 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.data(), + weights.data(), + nullptr, + coarse_poly->totloop, + 2); + } +} + +static void vertex_interpolation_from_corner(const SubdivMeshContext *ctx, + VerticesForInterpolation *vertex_interpolation, + const MPoly *coarse_poly, + const int corner) +{ + if (coarse_poly->totloop == 4) { + /* Nothing to do, all indices and data is already assigned. */ + } + else { + const CustomData *vertex_data = &ctx->coarse_mesh->vdata; + 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. */ + CustomData_copy_data(vertex_data, + &vertex_interpolation->vertex_data_storage, + coarse_mloop[coarse_poly->loopstart + corner].v, + 0, + 1); + /* Interpolate remaining ptex face corners, which hits loops + * middle points. + * + * TODO(sergey): Re-use one of interpolation results from previous + * iteration. */ + const float weights[2] = {0.5f, 0.5f}; + 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] = { + static_cast(coarse_mloop[first_loop_index].v), + static_cast( + coarse_mloop[coarse_poly->loopstart + + (first_loop_index - coarse_poly->loopstart + 1) % coarse_poly->totloop] + .v)}; + const int last_indices[2] = { + static_cast(coarse_mloop[first_loop_index].v), + static_cast(coarse_mloop[last_loop_index].v), + }; + CustomData_interp(vertex_data, + &vertex_interpolation->vertex_data_storage, + first_indices, + weights, + nullptr, + 2, + 1); + CustomData_interp(vertex_data, + &vertex_interpolation->vertex_data_storage, + last_indices, + weights, + nullptr, + 2, + 3); + } +} + +static void vertex_interpolation_end(VerticesForInterpolation *vertex_interpolation) +{ + if (vertex_interpolation->vertex_data_storage_allocated) { + CustomData_free(&vertex_interpolation->vertex_data_storage, 4); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Loop custom data interpolation helpers + * \{ */ + +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. */ + const CustomData *loop_data; + /* Loops data calculated for ptex corners. There are always 4 elements + * in this custom data, aligned the following way: + * + * index 0 -> uv (0, 0) + * index 1 -> uv (0, 1) + * index 2 -> uv (1, 1) + * index 3 -> uv (1, 0) + * + * Is allocated for non-regular faces (triangles and n-gons). */ + CustomData loop_data_storage; + bool loop_data_storage_allocated; + /* 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]; +}; + +static void loop_interpolation_init(const SubdivMeshContext *ctx, + LoopsForInterpolation *loop_interpolation, + const MPoly *coarse_poly) +{ + const Mesh *coarse_mesh = ctx->coarse_mesh; + if (coarse_poly->totloop == 4) { + loop_interpolation->loop_data = &coarse_mesh->ldata; + loop_interpolation->loop_indices[0] = coarse_poly->loopstart + 0; + loop_interpolation->loop_indices[1] = coarse_poly->loopstart + 1; + loop_interpolation->loop_indices[2] = coarse_poly->loopstart + 2; + loop_interpolation->loop_indices[3] = coarse_poly->loopstart + 3; + loop_interpolation->loop_data_storage_allocated = false; + } + else { + loop_interpolation->loop_data = &loop_interpolation->loop_data_storage; + /* Allocate storage for loops corresponding to ptex corners. */ + CustomData_copy(&ctx->coarse_mesh->ldata, + &loop_interpolation->loop_data_storage, + CD_MASK_EVERYTHING.lmask, + CD_SET_DEFAULT, + 4); + /* Initialize indices. */ + loop_interpolation->loop_indices[0] = 0; + loop_interpolation->loop_indices[1] = 1; + loop_interpolation->loop_indices[2] = 2; + loop_interpolation->loop_indices[3] = 3; + loop_interpolation->loop_data_storage_allocated = true; + /* Interpolate center of poly right away, it stays unchanged for all + * ptex faces. */ + const float weight = 1.0f / (float)coarse_poly->totloop; + blender::Array weights(coarse_poly->totloop); + blender::Array 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.data(), + weights.data(), + nullptr, + coarse_poly->totloop, + 2); + } +} + +static void loop_interpolation_from_corner(const SubdivMeshContext *ctx, + LoopsForInterpolation *loop_interpolation, + const MPoly *coarse_poly, + const int corner) +{ + if (coarse_poly->totloop == 4) { + /* Nothing to do, all indices and data is already assigned. */ + } + else { + const CustomData *loop_data = &ctx->coarse_mesh->ldata; + 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. */ + CustomData_free_elem(&loop_interpolation->loop_data_storage, 0, 1); + CustomData_copy_data( + loop_data, &loop_interpolation->loop_data_storage, coarse_poly->loopstart + corner, 0, 1); + /* Interpolate remaining ptex face corners, which hits loops + * middle points. + * + * TODO(sergey): Re-use one of interpolation results from previous + * iteration. */ + const float weights[2] = {0.5f, 0.5f}; + const int base_loop_index = coarse_poly->loopstart; + const int first_loop_index = loops_of_ptex.first_loop - coarse_mloop; + const int second_loop_index = base_loop_index + + (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] = { + static_cast(loops_of_ptex.last_loop - coarse_mloop), + static_cast(loops_of_ptex.first_loop - coarse_mloop), + }; + CustomData_interp( + 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, nullptr, 2, 3); + } +} + +static void loop_interpolation_end(LoopsForInterpolation *loop_interpolation) +{ + if (loop_interpolation->loop_data_storage_allocated) { + CustomData_free(&loop_interpolation->loop_data_storage, 4); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name TLS + * \{ */ + +struct SubdivMeshTLS { + bool vertex_interpolation_initialized; + VerticesForInterpolation vertex_interpolation; + const MPoly *vertex_interpolation_coarse_poly; + int vertex_interpolation_coarse_corner; + + bool loop_interpolation_initialized; + LoopsForInterpolation loop_interpolation; + const MPoly *loop_interpolation_coarse_poly; + int loop_interpolation_coarse_corner; +}; + +static void subdiv_mesh_tls_free(void *tls_v) +{ + SubdivMeshTLS *tls = static_cast(tls_v); + if (tls->vertex_interpolation_initialized) { + vertex_interpolation_end(&tls->vertex_interpolation); + } + if (tls->loop_interpolation_initialized) { + loop_interpolation_end(&tls->loop_interpolation); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Evaluation helper functions + * \{ */ + +static void subdiv_vertex_orco_evaluate(const SubdivMeshContext *ctx, + const int ptex_face_index, + const float u, + const float v, + const int subdiv_vertex_index) +{ + if (ctx->orco || ctx->cloth_orco) { + float vertex_data[6]; + BKE_subdiv_eval_vertex_data(ctx->subdiv, ptex_face_index, u, v, vertex_data); + + if (ctx->orco) { + copy_v3_v3(ctx->orco[subdiv_vertex_index], vertex_data); + if (ctx->cloth_orco) { + copy_v3_v3(ctx->orco[subdiv_vertex_index], vertex_data + 3); + } + } + else if (ctx->cloth_orco) { + copy_v3_v3(ctx->orco[subdiv_vertex_index], vertex_data); + } + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Accumulation helpers + * \{ */ + +static void subdiv_accumulate_vertex_displacement(SubdivMeshContext *ctx, + const int ptex_face_index, + const float u, + const float v, + MVert *subdiv_vert) +{ + /* Accumulate displacement. */ + Subdiv *subdiv = ctx->subdiv; + 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); + + /* NOTE: The subdivided mesh is allocated in this module, and its vertices are kept at zero + * locations as a default calloc(). */ + BKE_subdiv_eval_displacement(subdiv, ptex_face_index, u, v, dPdu, dPdv, D); + add_v3_v3(subdiv_vert->co, D); + + if (ctx->accumulated_counters) { + ++ctx->accumulated_counters[subdiv_vertex_index]; + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Callbacks + * \{ */ + +static bool subdiv_mesh_topology_info(const SubdivForeachContext *foreach_context, + const int num_vertices, + const int num_edges, + const int num_loops, + const int num_polygons, + const int *UNUSED(subdiv_polygon_offset)) +{ + /* Multi-resolution grid data will be applied or become invalid after subdivision, + * so don't try to preserve it and use memory. */ + CustomData_MeshMasks mask = CD_MASK_EVERYTHING; + mask.lmask &= ~CD_MASK_MULTIRES_GRIDS; + + SubdivMeshContext *subdiv_context = static_cast(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); + subdiv_mesh_prepare_accumulator(subdiv_context, num_vertices); + MEM_SAFE_FREE(subdiv_context->subdiv_mesh->runtime.subsurf_face_dot_tags); + subdiv_context->subdiv_mesh->runtime.subsurf_face_dot_tags = BLI_BITMAP_NEW(num_vertices, + __func__); + return true; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vertex subdivision process + * \{ */ + +static void subdiv_vertex_data_copy(const SubdivMeshContext *ctx, + const MVert *coarse_vertex, + MVert *subdiv_vertex) +{ + const Mesh *coarse_mesh = ctx->coarse_mesh; + 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); +} + +static void subdiv_vertex_data_interpolate(const SubdivMeshContext *ctx, + MVert *subdiv_vertex, + const VerticesForInterpolation *vertex_interpolation, + const float u, + const float v) +{ + 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, + nullptr, + 4, + subdiv_vertex_index); + if (ctx->vert_origindex != nullptr) { + ctx->vert_origindex[subdiv_vertex_index] = ORIGINDEX_NONE; + } +} + +static void evaluate_vertex_and_apply_displacement_copy(const SubdivMeshContext *ctx, + const int ptex_face_index, + const float u, + const float v, + const MVert *coarse_vert, + MVert *subdiv_vert) +{ + 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}; + if (ctx->have_displacement) { + const float inv_num_accumulated = 1.0f / ctx->accumulated_counters[subdiv_vertex_index]; + copy_v3_v3(D, subdiv_vert->co); + mul_v3_fl(D, inv_num_accumulated); + } + /* Copy custom data and evaluate position. */ + subdiv_vertex_data_copy(ctx, coarse_vert, subdiv_vert); + BKE_subdiv_eval_limit_point(ctx->subdiv, ptex_face_index, u, v, subdiv_vert->co); + /* Apply displacement. */ + 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 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); +} + +static void evaluate_vertex_and_apply_displacement_interpolate( + const SubdivMeshContext *ctx, + const int ptex_face_index, + const float u, + const float v, + VerticesForInterpolation *vertex_interpolation, + MVert *subdiv_vert) +{ + 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}; + if (ctx->have_displacement) { + const float inv_num_accumulated = 1.0f / ctx->accumulated_counters[subdiv_vertex_index]; + copy_v3_v3(D, subdiv_vert->co); + mul_v3_fl(D, inv_num_accumulated); + } + /* Interpolate custom data and evaluate position. */ + subdiv_vertex_data_interpolate(ctx, subdiv_vert, vertex_interpolation, u, v); + BKE_subdiv_eval_limit_point(ctx->subdiv, ptex_face_index, u, v, subdiv_vert->co); + /* Apply displacement. */ + add_v3_v3(subdiv_vert->co, D); + /* Evaluate undeformed texture coordinate. */ + subdiv_vertex_orco_evaluate(ctx, ptex_face_index, u, v, subdiv_vertex_index); +} + +static void subdiv_mesh_vertex_displacement_every_corner_or_edge( + const SubdivForeachContext *foreach_context, + void *UNUSED(tls), + const int ptex_face_index, + const float u, + const float v, + const int subdiv_vertex_index) +{ + SubdivMeshContext *ctx = static_cast(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); +} + +static void subdiv_mesh_vertex_displacement_every_corner( + const SubdivForeachContext *foreach_context, + void *tls, + const int ptex_face_index, + const float u, + const float v, + const int UNUSED(coarse_vertex_index), + const int UNUSED(coarse_poly_index), + const int UNUSED(coarse_corner), + const int subdiv_vertex_index) +{ + subdiv_mesh_vertex_displacement_every_corner_or_edge( + foreach_context, tls, ptex_face_index, u, v, subdiv_vertex_index); +} + +static void subdiv_mesh_vertex_displacement_every_edge(const SubdivForeachContext *foreach_context, + void *tls, + const int ptex_face_index, + const float u, + const float v, + const int UNUSED(coarse_edge_index), + const int UNUSED(coarse_poly_index), + const int UNUSED(coarse_corner), + const int subdiv_vertex_index) +{ + subdiv_mesh_vertex_displacement_every_corner_or_edge( + foreach_context, tls, ptex_face_index, u, v, subdiv_vertex_index); +} + +static void subdiv_mesh_vertex_corner(const SubdivForeachContext *foreach_context, + void *UNUSED(tls), + const int ptex_face_index, + const float u, + const float v, + const int coarse_vertex_index, + const int UNUSED(coarse_poly_index), + const int UNUSED(coarse_corner), + const int subdiv_vertex_index) +{ + BLI_assert(coarse_vertex_index != ORIGINDEX_NONE); + SubdivMeshContext *ctx = static_cast(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); +} + +static void subdiv_mesh_ensure_vertex_interpolation(SubdivMeshContext *ctx, + SubdivMeshTLS *tls, + const MPoly *coarse_poly, + const int coarse_corner) +{ + /* Check whether we've moved to another corner or polygon. */ + if (tls->vertex_interpolation_initialized) { + if (tls->vertex_interpolation_coarse_poly != coarse_poly || + tls->vertex_interpolation_coarse_corner != coarse_corner) { + vertex_interpolation_end(&tls->vertex_interpolation); + tls->vertex_interpolation_initialized = false; + } + } + /* Initialize the interpolation. */ + if (!tls->vertex_interpolation_initialized) { + vertex_interpolation_init(ctx, &tls->vertex_interpolation, coarse_poly); + } + /* Update it for a new corner if needed. */ + if (!tls->vertex_interpolation_initialized || + tls->vertex_interpolation_coarse_corner != coarse_corner) { + vertex_interpolation_from_corner(ctx, &tls->vertex_interpolation, coarse_poly, coarse_corner); + } + /* Store settings used for the current state of interpolator. */ + tls->vertex_interpolation_initialized = true; + tls->vertex_interpolation_coarse_poly = coarse_poly; + tls->vertex_interpolation_coarse_corner = coarse_corner; +} + +static void subdiv_mesh_vertex_edge(const SubdivForeachContext *foreach_context, + void *tls_v, + const int ptex_face_index, + const float u, + const float v, + const int UNUSED(coarse_edge_index), + const int coarse_poly_index, + const int coarse_corner, + const int subdiv_vertex_index) +{ + SubdivMeshContext *ctx = static_cast(foreach_context->user_data); + SubdivMeshTLS *tls = static_cast(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); +} + +static bool subdiv_mesh_is_center_vertex(const MPoly *coarse_poly, const float u, const float v) +{ + if (coarse_poly->totloop == 4) { + if (u == 0.5f && v == 0.5f) { + return true; + } + } + else { + if (u == 1.0f && v == 1.0f) { + return true; + } + } + return false; +} + +static void subdiv_mesh_tag_center_vertex(const MPoly *coarse_poly, + const int subdiv_vertex_index, + const float u, + const float v, + Mesh *subdiv_mesh) +{ + if (subdiv_mesh_is_center_vertex(coarse_poly, u, v)) { + BLI_BITMAP_ENABLE(subdiv_mesh->runtime.subsurf_face_dot_tags, subdiv_vertex_index); + } +} + +static void subdiv_mesh_vertex_inner(const SubdivForeachContext *foreach_context, + void *tls_v, + const int ptex_face_index, + const float u, + const float v, + const int coarse_poly_index, + const int coarse_corner, + const int subdiv_vertex_index) +{ + SubdivMeshContext *ctx = static_cast(foreach_context->user_data); + SubdivMeshTLS *tls = static_cast(tls_v); + Subdiv *subdiv = ctx->subdiv; + const MPoly *coarse_poly = &ctx->coarse_polys[coarse_poly_index]; + Mesh *subdiv_mesh = ctx->subdiv_mesh; + 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); + subdiv_mesh_tag_center_vertex(coarse_poly, subdiv_vertex_index, u, v, subdiv_mesh); + subdiv_vertex_orco_evaluate(ctx, ptex_face_index, u, v, subdiv_vertex_index); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Edge subdivision process + * \{ */ + +static void subdiv_copy_edge_data(SubdivMeshContext *ctx, + MEdge *subdiv_edge, + const MEdge *coarse_edge) +{ + const int subdiv_edge_index = subdiv_edge - ctx->subdiv_edges; + if (coarse_edge == nullptr) { + subdiv_edge->crease = 0; + subdiv_edge->flag = 0; + if (!ctx->settings->use_optimal_display) { + subdiv_edge->flag |= ME_EDGERENDER; + } + if (ctx->edge_origindex != nullptr) { + ctx->edge_origindex[subdiv_edge_index] = ORIGINDEX_NONE; + } + return; + } + 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; +} + +static void subdiv_mesh_edge(const SubdivForeachContext *foreach_context, + void *UNUSED(tls), + const int coarse_edge_index, + const int subdiv_edge_index, + const bool UNUSED(is_loose), + const int subdiv_v1, + const int subdiv_v2) +{ + SubdivMeshContext *ctx = static_cast(foreach_context->user_data); + MEdge *subdiv_medge = ctx->subdiv_edges; + MEdge *subdiv_edge = &subdiv_medge[subdiv_edge_index]; + const MEdge *coarse_edge = nullptr; + if (coarse_edge_index != ORIGINDEX_NONE) { + const MEdge *coarse_medge = ctx->coarse_edges; + coarse_edge = &coarse_medge[coarse_edge_index]; + } + subdiv_copy_edge_data(ctx, subdiv_edge, coarse_edge); + subdiv_edge->v1 = subdiv_v1; + subdiv_edge->v2 = subdiv_v2; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Loops creation/interpolation + * \{ */ + +static void subdiv_interpolate_loop_data(const SubdivMeshContext *ctx, + MLoop *subdiv_loop, + const LoopsForInterpolation *loop_interpolation, + const float u, + const float v) +{ + 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, + nullptr, + 4, + subdiv_loop_index); + /* TODO(sergey): Set ORIGINDEX. */ +} + +static void subdiv_eval_uv_layer(SubdivMeshContext *ctx, + MLoop *subdiv_loop, + const int ptex_face_index, + const float u, + const float v) +{ + if (ctx->num_uv_layers == 0) { + return; + } + Subdiv *subdiv = ctx->subdiv; + 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); + } +} + +static void subdiv_mesh_ensure_loop_interpolation(SubdivMeshContext *ctx, + SubdivMeshTLS *tls, + const MPoly *coarse_poly, + const int coarse_corner) +{ + /* Check whether we've moved to another corner or polygon. */ + if (tls->loop_interpolation_initialized) { + if (tls->loop_interpolation_coarse_poly != coarse_poly || + tls->loop_interpolation_coarse_corner != coarse_corner) { + loop_interpolation_end(&tls->loop_interpolation); + tls->loop_interpolation_initialized = false; + } + } + /* Initialize the interpolation. */ + if (!tls->loop_interpolation_initialized) { + loop_interpolation_init(ctx, &tls->loop_interpolation, coarse_poly); + } + /* Update it for a new corner if needed. */ + if (!tls->loop_interpolation_initialized || + tls->loop_interpolation_coarse_corner != coarse_corner) { + loop_interpolation_from_corner(ctx, &tls->loop_interpolation, coarse_poly, coarse_corner); + } + /* Store settings used for the current state of interpolator. */ + tls->loop_interpolation_initialized = true; + tls->loop_interpolation_coarse_poly = coarse_poly; + tls->loop_interpolation_coarse_corner = coarse_corner; +} + +static void subdiv_mesh_loop(const SubdivForeachContext *foreach_context, + void *tls_v, + const int ptex_face_index, + const float u, + const float v, + const int UNUSED(coarse_loop_index), + const int coarse_poly_index, + const int coarse_corner, + const int subdiv_loop_index, + const int subdiv_vertex_index, + const int subdiv_edge_index) +{ + SubdivMeshContext *ctx = static_cast(foreach_context->user_data); + SubdivMeshTLS *tls = static_cast(tls_v); + const MPoly *coarse_mpoly = ctx->coarse_polys; + const MPoly *coarse_poly = &coarse_mpoly[coarse_poly_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); + subdiv_loop->v = subdiv_vertex_index; + subdiv_loop->e = subdiv_edge_index; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Polygons subdivision process + * \{ */ + +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_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); +} + +static void subdiv_mesh_poly(const SubdivForeachContext *foreach_context, + void *UNUSED(tls), + const int coarse_poly_index, + const int subdiv_poly_index, + const int start_loop_index, + const int num_loops) +{ + BLI_assert(coarse_poly_index != ORIGINDEX_NONE); + SubdivMeshContext *ctx = static_cast(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; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Loose elements subdivision process + * \{ */ + +static void subdiv_mesh_vertex_loose(const SubdivForeachContext *foreach_context, + void *UNUSED(tls), + const int coarse_vertex_index, + const int subdiv_vertex_index) +{ + SubdivMeshContext *ctx = static_cast(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 MEdge *coarse_edges, + const MeshElemMap *vert_to_edge_map, + const int edge_index, + const MEdge *neighbors[2]) +{ + const MEdge *edge = &coarse_edges[edge_index]; + neighbors[0] = nullptr; + neighbors[1] = nullptr; + int neighbor_counters[2] = {0, 0}; + 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, coarse_edges[i].v1, coarse_edges[i].v2)) { + neighbors[0] = &coarse_edges[i]; + ++neighbor_counters[0]; + } + } + 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]; + } + } + /* Vertices which has more than one neighbor are considered infinitely + * 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] = nullptr; + } + if (neighbor_counters[1] > 1) { + neighbors[1] = nullptr; + } +} + +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]) +{ + /* 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] != nullptr) { + if (neighbors[0]->v1 == coarse_edge->v1) { + copy_v3_v3(points_r[0], coarse_mvert[neighbors[0]->v2].co); + } + else { + copy_v3_v3(points_r[0], coarse_mvert[neighbors[0]->v1].co); + } + } + else { + sub_v3_v3v3(points_r[0], points_r[1], points_r[2]); + add_v3_v3(points_r[0], points_r[1]); + } + /* End point, duplicate from edge end if no neighbor. */ + if (neighbors[1] != nullptr) { + if (neighbors[1]->v1 == coarse_edge->v2) { + copy_v3_v3(points_r[3], coarse_mvert[neighbors[1]->v2].co); + } + else { + copy_v3_v3(points_r[3], coarse_mvert[neighbors[1]->v1].co); + } + } + else { + sub_v3_v3v3(points_r[3], points_r[2], points_r[1]); + add_v3_v3(points_r[3], points_r[2]); + } +} + +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 *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_edges, vert_to_edge_map, coarse_edge_index, neighbors); + float points[4][3]; + 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); + } +} + +static void subdiv_mesh_vertex_of_loose_edge_interpolate(SubdivMeshContext *ctx, + const MEdge *coarse_edge, + const float u, + const int subdiv_vertex_index) +{ + const Mesh *coarse_mesh = ctx->coarse_mesh; + Mesh *subdiv_mesh = ctx->subdiv_mesh; + /* This is never used for end-points (which are copied from the original). */ + 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] = {static_cast(coarse_edge->v1), + static_cast(coarse_edge->v2)}; + CustomData_interp(&coarse_mesh->vdata, + &subdiv_mesh->vdata, + coarse_vertex_indices, + interpolation_weights, + nullptr, + 2, + subdiv_vertex_index); + if (ctx->vert_origindex != nullptr) { + ctx->vert_origindex[subdiv_vertex_index] = ORIGINDEX_NONE; + } +} + +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 = static_cast(foreach_context->user_data); + const Mesh *coarse_mesh = ctx->coarse_mesh; + 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 = &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; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Initialization + * \{ */ + +static void setup_foreach_callbacks(const SubdivMeshContext *subdiv_context, + SubdivForeachContext *foreach_context) +{ + memset(foreach_context, 0, sizeof(*foreach_context)); + /* General information. */ + foreach_context->topology_info = subdiv_mesh_topology_info; + /* Every boundary geometry. Used for displacement averaging. */ + if (subdiv_context->have_displacement) { + foreach_context->vertex_every_corner = subdiv_mesh_vertex_displacement_every_corner; + foreach_context->vertex_every_edge = subdiv_mesh_vertex_displacement_every_edge; + } + foreach_context->vertex_corner = subdiv_mesh_vertex_corner; + foreach_context->vertex_edge = subdiv_mesh_vertex_edge; + foreach_context->vertex_inner = subdiv_mesh_vertex_inner; + foreach_context->edge = subdiv_mesh_edge; + foreach_context->loop = subdiv_mesh_loop; + foreach_context->poly = subdiv_mesh_poly; + foreach_context->vertex_loose = subdiv_mesh_vertex_loose; + foreach_context->vertex_of_loose_edge = subdiv_mesh_vertex_of_loose_edge; + foreach_context->user_data_tls_free = subdiv_mesh_tls_free; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Public entry point + * \{ */ + +Mesh *BKE_subdiv_to_mesh(Subdiv *subdiv, + const SubdivToMeshSettings *settings, + const Mesh *coarse_mesh) +{ + BKE_subdiv_stats_begin(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_MESH); + /* 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, nullptr, SUBDIV_EVALUATOR_TYPE_CPU, nullptr)) { + /* This could happen in two situations: + * - OpenSubdiv is disabled. + * - Something totally bad happened, and OpenSubdiv rejected our + * topology. + * In either way, we can't safely continue. */ + if (coarse_mesh->totpoly) { + BKE_subdiv_stats_end(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_MESH); + 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 != nullptr); + /* Multi-threaded traversal/evaluation. */ + BKE_subdiv_stats_begin(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_MESH_GEOMETRY); + SubdivForeachContext foreach_context; + setup_foreach_callbacks(&subdiv_context, &foreach_context); + SubdivMeshTLS tls = {0}; + foreach_context.user_data = &subdiv_context; + foreach_context.user_data_tls_size = sizeof(SubdivMeshTLS); + foreach_context.user_data_tls = &tls; + BKE_subdiv_foreach_subdiv_geometry(subdiv, &foreach_context, settings, coarse_mesh); + BKE_subdiv_stats_end(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_MESH_GEOMETRY); + Mesh *result = subdiv_context.subdiv_mesh; + // BKE_mesh_validate(result, true, true); + BKE_subdiv_stats_end(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_MESH); + /* Using normals from the limit surface gives different results than Blender's vertex normal + * calculation. Since vertex normals are supposed to be a consistent cache, don't bother + * calculating them here. The work may have been pointless anyway if the mesh is deformed or + * changed afterwards. */ + BLI_assert(BKE_mesh_vertex_normals_are_dirty(result) || BKE_mesh_poly_normals_are_dirty(result)); + /* Free used memory. */ + subdiv_mesh_context_free(&subdiv_context); + return result; +} + +/** \} */ 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, Vector{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 void operator()() + { + static_cast(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()) { @@ -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( + std::make_shared(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::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.c deleted file mode 100644 index 0265dd037b1..00000000000 --- a/source/blender/blenkernel/intern/workspace.c +++ /dev/null @@ -1,596 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/** \file - * \ingroup bke - */ - -#include -#include -#include - -#include "BLI_listbase.h" -#include "BLI_string.h" -#include "BLI_string_utils.h" -#include "BLI_utildefines.h" - -#include "BLT_translation.h" - -#include "BKE_asset.h" -#include "BKE_global.h" -#include "BKE_idprop.h" -#include "BKE_idtype.h" -#include "BKE_lib_id.h" -#include "BKE_lib_query.h" -#include "BKE_main.h" -#include "BKE_object.h" -#include "BKE_scene.h" -#include "BKE_workspace.h" - -#include "DNA_object_types.h" -#include "DNA_scene_types.h" -#include "DNA_screen_types.h" -#include "DNA_windowmanager_types.h" -#include "DNA_workspace_types.h" - -#include "DEG_depsgraph.h" - -#include "MEM_guardedalloc.h" - -#include "BLO_read_write.h" - -/* -------------------------------------------------------------------- */ - -static void workspace_init_data(ID *id) -{ - WorkSpace *workspace = (WorkSpace *)id; - - BKE_asset_library_reference_init_default(&workspace->asset_library_ref); -} - -static void workspace_free_data(ID *id) -{ - WorkSpace *workspace = (WorkSpace *)id; - - BKE_workspace_relations_free(&workspace->hook_layout_relations); - - BLI_freelistN(&workspace->owner_ids); - BLI_freelistN(&workspace->layouts); - - while (!BLI_listbase_is_empty(&workspace->tools)) { - BKE_workspace_tool_remove(workspace, workspace->tools.first); - } - - MEM_SAFE_FREE(workspace->status_text); -} - -static void workspace_foreach_id(ID *id, LibraryForeachIDData *data) -{ - WorkSpace *workspace = (WorkSpace *)id; - - BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, workspace->pin_scene, IDWALK_CB_NOP); - - LISTBASE_FOREACH (WorkSpaceLayout *, layout, &workspace->layouts) { - BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, layout->screen, IDWALK_CB_USER); - } -} - -static void workspace_blend_write(BlendWriter *writer, ID *id, const void *id_address) -{ - WorkSpace *workspace = (WorkSpace *)id; - - BLO_write_id_struct(writer, WorkSpace, id_address, &workspace->id); - BKE_id_blend_write(writer, &workspace->id); - BLO_write_struct_list(writer, WorkSpaceLayout, &workspace->layouts); - BLO_write_struct_list(writer, WorkSpaceDataRelation, &workspace->hook_layout_relations); - BLO_write_struct_list(writer, wmOwnerID, &workspace->owner_ids); - BLO_write_struct_list(writer, bToolRef, &workspace->tools); - LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) { - if (tref->properties) { - IDP_BlendWrite(writer, tref->properties); - } - } -} - -static void workspace_blend_read_data(BlendDataReader *reader, ID *id) -{ - WorkSpace *workspace = (WorkSpace *)id; - - BLO_read_list(reader, &workspace->layouts); - BLO_read_list(reader, &workspace->hook_layout_relations); - BLO_read_list(reader, &workspace->owner_ids); - BLO_read_list(reader, &workspace->tools); - - LISTBASE_FOREACH (WorkSpaceDataRelation *, relation, &workspace->hook_layout_relations) { - /* Parent pointer does not belong to workspace data and is therefore restored in lib_link step - * of window manager. */ - BLO_read_data_address(reader, &relation->value); - } - - LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) { - tref->runtime = NULL; - BLO_read_data_address(reader, &tref->properties); - IDP_BlendDataRead(reader, &tref->properties); - } - - workspace->status_text = NULL; - - id_us_ensure_real(&workspace->id); -} - -static void workspace_blend_read_lib(BlendLibReader *reader, ID *id) -{ - WorkSpace *workspace = (WorkSpace *)id; - Main *bmain = BLO_read_lib_get_main(reader); - - /* 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; - } - else { - BLO_read_id_address(reader, NULL, &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; - LISTBASE_FOREACH (wmWindowManager *, wm, &bmain->wm) { - LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { - if (win->winid == relation->parentid) { - relation->parent = win->workspace_hook; - } - } - } - if (relation->parent == NULL) { - BLI_freelinkN(&workspace->hook_layout_relations, relation); - } - } - - LISTBASE_FOREACH_MUTABLE (WorkSpaceLayout *, layout, &workspace->layouts) { - BLO_read_id_address(reader, id->lib, &layout->screen); - - if (layout->screen) { - if (ID_IS_LINKED(id)) { - layout->screen->winid = 0; - if (layout->screen->temp) { - /* delete temp layouts when appending */ - BKE_workspace_layout_remove(bmain, workspace, layout); - } - } - } - else { - /* If we're reading a layout without screen stored, it's useless and we shouldn't keep it - * around. */ - BKE_workspace_layout_remove(bmain, workspace, layout); - } - } -} - -static void workspace_blend_read_expand(BlendExpander *expander, ID *id) -{ - WorkSpace *workspace = (WorkSpace *)id; - - LISTBASE_FOREACH (WorkSpaceLayout *, layout, &workspace->layouts) { - BLO_expand(expander, BKE_workspace_layout_screen_get(layout)); - } -} - -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, -}; - -/* -------------------------------------------------------------------- */ -/** \name Internal Utils - * \{ */ - -static void workspace_layout_name_set(WorkSpace *workspace, - WorkSpaceLayout *layout, - const char *new_name) -{ - BLI_strncpy(layout->name, new_name, sizeof(layout->name)); - BLI_uniquename(&workspace->layouts, - layout, - "Layout", - '.', - offsetof(WorkSpaceLayout, name), - sizeof(layout->name)); -} - -/** - * This should only be used directly when it is to be expected that there isn't - * a layout within \a workspace that wraps \a screen. Usually - especially outside - * of BKE_workspace - #BKE_workspace_layout_find should be used! - */ -static WorkSpaceLayout *workspace_layout_find_exec(const WorkSpace *workspace, - const bScreen *screen) -{ - return BLI_findptr(&workspace->layouts, screen, offsetof(WorkSpaceLayout, screen)); -} - -static void workspace_relation_add(ListBase *relation_list, - void *parent, - const int parentid, - void *data) -{ - WorkSpaceDataRelation *relation = MEM_callocN(sizeof(*relation), __func__); - relation->parent = parent; - relation->parentid = parentid; - relation->value = data; - /* add to head, if we switch back to it soon we find it faster. */ - BLI_addhead(relation_list, relation); -} -static void workspace_relation_remove(ListBase *relation_list, WorkSpaceDataRelation *relation) -{ - BLI_remlink(relation_list, relation); - MEM_freeN(relation); -} - -static void workspace_relation_ensure_updated(ListBase *relation_list, - void *parent, - const int parentid, - void *data) -{ - WorkSpaceDataRelation *relation = BLI_listbase_bytes_find( - relation_list, &parentid, sizeof(parentid), offsetof(WorkSpaceDataRelation, parentid)); - if (relation != NULL) { - relation->parent = parent; - relation->value = data; - /* reinsert at the head of the list, so that more commonly used relations are found faster. */ - BLI_remlink(relation_list, relation); - BLI_addhead(relation_list, relation); - } - else { - /* no matching relation found, add new one */ - workspace_relation_add(relation_list, parent, parentid, data); - } -} - -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) { - return relation->value; - } - - return NULL; -} - -/** - * Checks if \a screen is already used within any workspace. A screen should never be assigned to - * multiple WorkSpaceLayouts, but that should be ensured outside of the BKE_workspace module - * and without such checks. - * Hence, this should only be used as assert check before assigning a screen to a workspace. - */ -#ifndef NDEBUG -static bool workspaces_is_screen_used -#else -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) { - if (workspace_layout_find_exec(workspace, screen)) { - return true; - } - } - - return false; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Create, Delete, Init - * \{ */ - -WorkSpace *BKE_workspace_add(Main *bmain, const char *name) -{ - WorkSpace *new_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; - layout = layout_next) { - layout_next = layout->next; - BKE_workspace_layout_remove(bmain, workspace, layout); - } - BKE_id_free(bmain, workspace); -} - -WorkSpaceInstanceHook *BKE_workspace_instance_hook_create(const Main *bmain, const int winid) -{ - WorkSpaceInstanceHook *hook = MEM_callocN(sizeof(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); - } - - return hook; -} -void BKE_workspace_instance_hook_free(const Main *bmain, WorkSpaceInstanceHook *hook) -{ - /* workspaces should never be freed before wm (during which we call this function). - * However, when running in background mode, loading a blend file may allocate windows (that need - * to be freed) without creating workspaces. This happens in BlendfileLoadingBaseTest. */ - 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; - relation; - relation = relation_next) { - relation_next = relation->next; - if (relation->parent == hook) { - workspace_relation_remove(&workspace->hook_layout_relations, relation); - } - } - } - - MEM_freeN(hook); -} - -WorkSpaceLayout *BKE_workspace_layout_add(Main *bmain, - WorkSpace *workspace, - bScreen *screen, - const char *name) -{ - WorkSpaceLayout *layout = MEM_callocN(sizeof(*layout), __func__); - - BLI_assert(!workspaces_is_screen_used(bmain, screen)); -#ifndef DEBUG - UNUSED_VARS(bmain); -#endif - layout->screen = screen; - id_us_plus(&layout->screen->id); - workspace_layout_name_set(workspace, layout, name); - BLI_addtail(&workspace->layouts, layout); - - return layout; -} - -void BKE_workspace_layout_remove(Main *bmain, WorkSpace *workspace, WorkSpaceLayout *layout) -{ - /* Screen should usually be set, but we call this from file reading to get rid of invalid - * layouts. */ - if (layout->screen) { - id_us_min(&layout->screen->id); - BKE_id_free(bmain, layout->screen); - } - BLI_freelinkN(&workspace->layouts, layout); -} - -void BKE_workspace_relations_free(ListBase *relation_list) -{ - for (WorkSpaceDataRelation *relation = relation_list->first, *relation_next; relation; - relation = relation_next) { - relation_next = relation->next; - workspace_relation_remove(relation_list, relation); - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name General Utils - * \{ */ - -WorkSpaceLayout *BKE_workspace_layout_find(const WorkSpace *workspace, const bScreen *screen) -{ - WorkSpaceLayout *layout = workspace_layout_find_exec(workspace, screen); - if (layout) { - return layout; - } - - printf( - "%s: Couldn't find layout in this workspace: '%s' screen: '%s'. " - "This should not happen!\n", - __func__, - workspace->id.name + 2, - screen->id.name + 2); - - return NULL; -} - -WorkSpaceLayout *BKE_workspace_layout_find_global(const Main *bmain, - const bScreen *screen, - WorkSpace **r_workspace) -{ - WorkSpaceLayout *layout; - - if (r_workspace) { - *r_workspace = NULL; - } - - for (WorkSpace *workspace = bmain->workspaces.first; workspace; workspace = workspace->id.next) { - if ((layout = workspace_layout_find_exec(workspace, screen))) { - if (r_workspace) { - *r_workspace = workspace; - } - - return layout; - } - } - - return NULL; -} - -WorkSpaceLayout *BKE_workspace_layout_iter_circular(const WorkSpace *workspace, - WorkSpaceLayout *start, - bool (*callback)(const WorkSpaceLayout *layout, - void *arg), - void *arg, - const bool iter_backward) -{ - WorkSpaceLayout *iter_layout; - - if (iter_backward) { - LISTBASE_CIRCULAR_BACKWARD_BEGIN (&workspace->layouts, iter_layout, start) { - if (!callback(iter_layout, arg)) { - return iter_layout; - } - } - LISTBASE_CIRCULAR_BACKWARD_END(&workspace->layouts, iter_layout, start); - } - else { - LISTBASE_CIRCULAR_FORWARD_BEGIN (&workspace->layouts, iter_layout, start) { - if (!callback(iter_layout, arg)) { - return iter_layout; - } - } - LISTBASE_CIRCULAR_FORWARD_END(&workspace->layouts, iter_layout, start); - } - - return NULL; -} - -void BKE_workspace_tool_remove(struct WorkSpace *workspace, struct bToolRef *tref) -{ - if (tref->runtime) { - MEM_freeN(tref->runtime); - } - if (tref->properties) { - IDP_FreeProperty(tref->properties); - } - BLI_remlink(&workspace->tools, tref); - MEM_freeN(tref); -} - -bool BKE_workspace_owner_id_check(const WorkSpace *workspace, const char *owner_id) -{ - if ((*owner_id == '\0') || ((workspace->flags & WORKSPACE_USE_FILTER_BY_ORIGIN) == 0)) { - return true; - } - - /* 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; -} - -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; - LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { - WorkSpace *workspace = BKE_workspace_active_get(win->workspace_hook); - workspace->id.tag |= tag; - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Getters/Setters - * \{ */ - -WorkSpace *BKE_workspace_active_get(WorkSpaceInstanceHook *hook) -{ - return hook->active; -} -void BKE_workspace_active_set(WorkSpaceInstanceHook *hook, WorkSpace *workspace) -{ - /* DO NOT check for `hook->active == workspace` here. Caller code is supposed to do it if - * 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 - * 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); - if (layout) { - hook->act_layout = layout; - } - } -} - -WorkSpaceLayout *BKE_workspace_active_layout_get(const WorkSpaceInstanceHook *hook) -{ - return hook->act_layout; -} - -WorkSpaceLayout *BKE_workspace_active_layout_for_workspace_get(const WorkSpaceInstanceHook *hook, - const WorkSpace *workspace) -{ - /* If the workspace is active, the active layout can be returned, no need for a lookup. */ - if (hook->active == workspace) { - return hook->act_layout; - } - - /* Inactive workspace */ - return workspace_relation_get_data_matching_parent(&workspace->hook_layout_relations, hook); -} - -void BKE_workspace_active_layout_set(WorkSpaceInstanceHook *hook, - const int winid, - WorkSpace *workspace, - WorkSpaceLayout *layout) -{ - hook->act_layout = layout; - workspace_relation_ensure_updated(&workspace->hook_layout_relations, hook, winid, layout); -} - -bScreen *BKE_workspace_active_screen_get(const WorkSpaceInstanceHook *hook) -{ - return hook->act_layout->screen; -} -void BKE_workspace_active_screen_set(WorkSpaceInstanceHook *hook, - const int winid, - WorkSpace *workspace, - bScreen *screen) -{ - /* we need to find the WorkspaceLayout that wraps this screen */ - WorkSpaceLayout *layout = BKE_workspace_layout_find(hook->active, screen); - BKE_workspace_active_layout_set(hook, winid, workspace, layout); -} - -const char *BKE_workspace_layout_name_get(const WorkSpaceLayout *layout) -{ - return layout->name; -} -void BKE_workspace_layout_name_set(WorkSpace *workspace, - WorkSpaceLayout *layout, - const char *new_name) -{ - workspace_layout_name_set(workspace, layout, new_name); -} - -bScreen *BKE_workspace_layout_screen_get(const WorkSpaceLayout *layout) -{ - return layout->screen; -} - -/** \} */ diff --git a/source/blender/blenkernel/intern/workspace.cc b/source/blender/blenkernel/intern/workspace.cc new file mode 100644 index 00000000000..8008dacc853 --- /dev/null +++ b/source/blender/blenkernel/intern/workspace.cc @@ -0,0 +1,610 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup bke + */ + +#include +#include +#include + +#include "BLI_listbase.h" +#include "BLI_string.h" +#include "BLI_string_utils.h" +#include "BLI_utildefines.h" + +#include "BLT_translation.h" + +#include "BKE_asset.h" +#include "BKE_global.h" +#include "BKE_idprop.h" +#include "BKE_idtype.h" +#include "BKE_lib_id.h" +#include "BKE_lib_query.h" +#include "BKE_main.h" +#include "BKE_object.h" +#include "BKE_scene.h" +#include "BKE_workspace.h" + +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_windowmanager_types.h" +#include "DNA_workspace_types.h" + +#include "DEG_depsgraph.h" + +#include "MEM_guardedalloc.h" + +#include "BLO_read_write.h" + +/* -------------------------------------------------------------------- */ + +static void workspace_init_data(ID *id) +{ + WorkSpace *workspace = (WorkSpace *)id; + + BKE_asset_library_reference_init_default(&workspace->asset_library_ref); +} + +static void workspace_free_data(ID *id) +{ + WorkSpace *workspace = (WorkSpace *)id; + + BKE_workspace_relations_free(&workspace->hook_layout_relations); + + BLI_freelistN(&workspace->owner_ids); + BLI_freelistN(&workspace->layouts); + + while (!BLI_listbase_is_empty(&workspace->tools)) { + BKE_workspace_tool_remove(workspace, static_cast(workspace->tools.first)); + } + + MEM_SAFE_FREE(workspace->status_text); +} + +static void workspace_foreach_id(ID *id, LibraryForeachIDData *data) +{ + WorkSpace *workspace = (WorkSpace *)id; + + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, workspace->pin_scene, IDWALK_CB_NOP); + + LISTBASE_FOREACH (WorkSpaceLayout *, layout, &workspace->layouts) { + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, layout->screen, IDWALK_CB_USER); + } +} + +static void workspace_blend_write(BlendWriter *writer, ID *id, const void *id_address) +{ + WorkSpace *workspace = (WorkSpace *)id; + + BLO_write_id_struct(writer, WorkSpace, id_address, &workspace->id); + BKE_id_blend_write(writer, &workspace->id); + BLO_write_struct_list(writer, WorkSpaceLayout, &workspace->layouts); + BLO_write_struct_list(writer, WorkSpaceDataRelation, &workspace->hook_layout_relations); + BLO_write_struct_list(writer, wmOwnerID, &workspace->owner_ids); + BLO_write_struct_list(writer, bToolRef, &workspace->tools); + LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) { + if (tref->properties) { + IDP_BlendWrite(writer, tref->properties); + } + } +} + +static void workspace_blend_read_data(BlendDataReader *reader, ID *id) +{ + WorkSpace *workspace = (WorkSpace *)id; + + BLO_read_list(reader, &workspace->layouts); + BLO_read_list(reader, &workspace->hook_layout_relations); + BLO_read_list(reader, &workspace->owner_ids); + BLO_read_list(reader, &workspace->tools); + + LISTBASE_FOREACH (WorkSpaceDataRelation *, relation, &workspace->hook_layout_relations) { + /* Parent pointer does not belong to workspace data and is therefore restored in lib_link step + * of window manager. */ + BLO_read_data_address(reader, &relation->value); + } + + LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) { + tref->runtime = nullptr; + BLO_read_data_address(reader, &tref->properties); + IDP_BlendDataRead(reader, &tref->properties); + } + + workspace->status_text = nullptr; + + id_us_ensure_real(&workspace->id); +} + +static void workspace_blend_read_lib(BlendLibReader *reader, ID *id) +{ + WorkSpace *workspace = (WorkSpace *)id; + Main *bmain = BLO_read_lib_get_main(reader); + + /* 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 = nullptr; + } + else { + 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 = nullptr; + LISTBASE_FOREACH (wmWindowManager *, wm, &bmain->wm) { + LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { + if (win->winid == relation->parentid) { + relation->parent = win->workspace_hook; + } + } + } + if (relation->parent == nullptr) { + BLI_freelinkN(&workspace->hook_layout_relations, relation); + } + } + + LISTBASE_FOREACH_MUTABLE (WorkSpaceLayout *, layout, &workspace->layouts) { + BLO_read_id_address(reader, id->lib, &layout->screen); + + if (layout->screen) { + if (ID_IS_LINKED(id)) { + layout->screen->winid = 0; + if (layout->screen->temp) { + /* delete temp layouts when appending */ + BKE_workspace_layout_remove(bmain, workspace, layout); + } + } + } + else { + /* If we're reading a layout without screen stored, it's useless and we shouldn't keep it + * around. */ + BKE_workspace_layout_remove(bmain, workspace, layout); + } + } +} + +static void workspace_blend_read_expand(BlendExpander *expander, ID *id) +{ + WorkSpace *workspace = (WorkSpace *)id; + + LISTBASE_FOREACH (WorkSpaceLayout *, layout, &workspace->layouts) { + BLO_expand(expander, BKE_workspace_layout_screen_get(layout)); + } +} + +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 */ 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, +}; + +/* -------------------------------------------------------------------- */ +/** \name Internal Utils + * \{ */ + +static void workspace_layout_name_set(WorkSpace *workspace, + WorkSpaceLayout *layout, + const char *new_name) +{ + BLI_strncpy(layout->name, new_name, sizeof(layout->name)); + BLI_uniquename(&workspace->layouts, + layout, + "Layout", + '.', + offsetof(WorkSpaceLayout, name), + sizeof(layout->name)); +} + +/** + * This should only be used directly when it is to be expected that there isn't + * a layout within \a workspace that wraps \a screen. Usually - especially outside + * of BKE_workspace - #BKE_workspace_layout_find should be used! + */ +static WorkSpaceLayout *workspace_layout_find_exec(const WorkSpace *workspace, + const bScreen *screen) +{ + return static_cast( + BLI_findptr(&workspace->layouts, screen, offsetof(WorkSpaceLayout, screen))); +} + +static void workspace_relation_add(ListBase *relation_list, + void *parent, + const int parentid, + void *data) +{ + WorkSpaceDataRelation *relation = MEM_cnew(__func__); + relation->parent = parent; + relation->parentid = parentid; + relation->value = data; + /* add to head, if we switch back to it soon we find it faster. */ + BLI_addhead(relation_list, relation); +} +static void workspace_relation_remove(ListBase *relation_list, WorkSpaceDataRelation *relation) +{ + BLI_remlink(relation_list, relation); + MEM_freeN(relation); +} + +static void workspace_relation_ensure_updated(ListBase *relation_list, + void *parent, + const int parentid, + void *data) +{ + WorkSpaceDataRelation *relation = static_cast(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. */ + BLI_remlink(relation_list, relation); + BLI_addhead(relation_list, relation); + } + else { + /* no matching relation found, add new one */ + workspace_relation_add(relation_list, parent, parentid, data); + } +} + +static void *workspace_relation_get_data_matching_parent(const ListBase *relation_list, + const void *parent) +{ + WorkSpaceDataRelation *relation = static_cast( + BLI_findptr(relation_list, parent, offsetof(WorkSpaceDataRelation, parent))); + if (relation != nullptr) { + return relation->value; + } + + return nullptr; +} + +/** + * Checks if \a screen is already used within any workspace. A screen should never be assigned to + * multiple WorkSpaceLayouts, but that should be ensured outside of the BKE_workspace module + * and without such checks. + * Hence, this should only be used as assert check before assigning a screen to a workspace. + */ +#ifndef NDEBUG +static bool workspaces_is_screen_used +#else +static bool UNUSED_FUNCTION(workspaces_is_screen_used) +#endif + (const Main *bmain, bScreen *screen) +{ + for (WorkSpace *workspace = static_cast(bmain->workspaces.first); workspace; + workspace = static_cast(workspace->id.next)) { + if (workspace_layout_find_exec(workspace, screen)) { + return true; + } + } + + return false; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Create, Delete, Init + * \{ */ + +WorkSpace *BKE_workspace_add(Main *bmain, const char *name) +{ + WorkSpace *new_workspace = static_cast(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 = static_cast(workspace->layouts.first), + *layout_next; + layout; + layout = layout_next) { + layout_next = layout->next; + BKE_workspace_layout_remove(bmain, workspace, layout); + } + BKE_id_free(bmain, workspace); +} + +WorkSpaceInstanceHook *BKE_workspace_instance_hook_create(const Main *bmain, const int winid) +{ + WorkSpaceInstanceHook *hook = MEM_cnew(__func__); + + /* set an active screen-layout for each possible window/workspace combination */ + for (WorkSpace *workspace = static_cast(bmain->workspaces.first); workspace; + workspace = static_cast(workspace->id.next)) { + BKE_workspace_active_layout_set( + hook, winid, workspace, static_cast(workspace->layouts.first)); + } + + return hook; +} +void BKE_workspace_instance_hook_free(const Main *bmain, WorkSpaceInstanceHook *hook) +{ + /* workspaces should never be freed before wm (during which we call this function). + * However, when running in background mode, loading a blend file may allocate windows (that need + * to be freed) without creating workspaces. This happens in BlendfileLoadingBaseTest. */ + BLI_assert(!BLI_listbase_is_empty(&bmain->workspaces) || G.background); + + /* Free relations for this hook */ + for (WorkSpace *workspace = static_cast(bmain->workspaces.first); workspace; + workspace = static_cast(workspace->id.next)) { + for (WorkSpaceDataRelation *relation = static_cast( + workspace->hook_layout_relations.first), + *relation_next; + relation; + relation = relation_next) { + relation_next = relation->next; + if (relation->parent == hook) { + workspace_relation_remove(&workspace->hook_layout_relations, relation); + } + } + } + + MEM_freeN(hook); +} + +WorkSpaceLayout *BKE_workspace_layout_add(Main *bmain, + WorkSpace *workspace, + bScreen *screen, + const char *name) +{ + WorkSpaceLayout *layout = MEM_cnew(__func__); + + BLI_assert(!workspaces_is_screen_used(bmain, screen)); +#ifndef DEBUG + UNUSED_VARS(bmain); +#endif + layout->screen = screen; + id_us_plus(&layout->screen->id); + workspace_layout_name_set(workspace, layout, name); + BLI_addtail(&workspace->layouts, layout); + + return layout; +} + +void BKE_workspace_layout_remove(Main *bmain, WorkSpace *workspace, WorkSpaceLayout *layout) +{ + /* Screen should usually be set, but we call this from file reading to get rid of invalid + * layouts. */ + if (layout->screen) { + id_us_min(&layout->screen->id); + BKE_id_free(bmain, layout->screen); + } + BLI_freelinkN(&workspace->layouts, layout); +} + +void BKE_workspace_relations_free(ListBase *relation_list) +{ + for (WorkSpaceDataRelation * + relation = static_cast(relation_list->first), + *relation_next; + relation; + relation = relation_next) { + relation_next = relation->next; + workspace_relation_remove(relation_list, relation); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name General Utils + * \{ */ + +WorkSpaceLayout *BKE_workspace_layout_find(const WorkSpace *workspace, const bScreen *screen) +{ + WorkSpaceLayout *layout = workspace_layout_find_exec(workspace, screen); + if (layout) { + return layout; + } + + printf( + "%s: Couldn't find layout in this workspace: '%s' screen: '%s'. " + "This should not happen!\n", + __func__, + workspace->id.name + 2, + screen->id.name + 2); + + return nullptr; +} + +WorkSpaceLayout *BKE_workspace_layout_find_global(const Main *bmain, + const bScreen *screen, + WorkSpace **r_workspace) +{ + WorkSpaceLayout *layout; + + if (r_workspace) { + *r_workspace = nullptr; + } + + for (WorkSpace *workspace = static_cast(bmain->workspaces.first); workspace; + workspace = static_cast(workspace->id.next)) { + if ((layout = workspace_layout_find_exec(workspace, screen))) { + if (r_workspace) { + *r_workspace = workspace; + } + + return layout; + } + } + + return nullptr; +} + +WorkSpaceLayout *BKE_workspace_layout_iter_circular(const WorkSpace *workspace, + WorkSpaceLayout *start, + bool (*callback)(const WorkSpaceLayout *layout, + void *arg), + void *arg, + const bool iter_backward) +{ + WorkSpaceLayout *iter_layout; + + if (iter_backward) { + LISTBASE_CIRCULAR_BACKWARD_BEGIN (WorkSpaceLayout *, &workspace->layouts, iter_layout, start) { + if (!callback(iter_layout, arg)) { + return iter_layout; + } + } + LISTBASE_CIRCULAR_BACKWARD_END(WorkSpaceLayout *, &workspace->layouts, iter_layout, start); + } + else { + LISTBASE_CIRCULAR_FORWARD_BEGIN (WorkSpaceLayout *, &workspace->layouts, iter_layout, start) { + if (!callback(iter_layout, arg)) { + return iter_layout; + } + } + LISTBASE_CIRCULAR_FORWARD_END(WorkSpaceLayout *, &workspace->layouts, iter_layout, start); + } + + return nullptr; +} + +void BKE_workspace_tool_remove(struct WorkSpace *workspace, struct bToolRef *tref) +{ + if (tref->runtime) { + MEM_freeN(tref->runtime); + } + if (tref->properties) { + IDP_FreeProperty(tref->properties); + } + BLI_remlink(&workspace->tools, tref); + MEM_freeN(tref); +} + +bool BKE_workspace_owner_id_check(const WorkSpace *workspace, const char *owner_id) +{ + if ((*owner_id == '\0') || ((workspace->flags & WORKSPACE_USE_FILTER_BY_ORIGIN) == 0)) { + return true; + } + + /* 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)) != nullptr; +} + +void BKE_workspace_id_tag_all_visible(Main *bmain, int tag) +{ + BKE_main_id_tag_listbase(&bmain->workspaces, tag, false); + wmWindowManager *wm = static_cast(bmain->wm.first); + LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { + WorkSpace *workspace = BKE_workspace_active_get(win->workspace_hook); + workspace->id.tag |= tag; + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Getters/Setters + * \{ */ + +WorkSpace *BKE_workspace_active_get(WorkSpaceInstanceHook *hook) +{ + return hook->active; +} +void BKE_workspace_active_set(WorkSpaceInstanceHook *hook, WorkSpace *workspace) +{ + /* DO NOT check for `hook->active == workspace` here. Caller code is supposed to do it if + * 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 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 = static_cast( + workspace_relation_get_data_matching_parent(&workspace->hook_layout_relations, hook)); + if (layout) { + hook->act_layout = layout; + } + } +} + +WorkSpaceLayout *BKE_workspace_active_layout_get(const WorkSpaceInstanceHook *hook) +{ + return hook->act_layout; +} + +WorkSpaceLayout *BKE_workspace_active_layout_for_workspace_get(const WorkSpaceInstanceHook *hook, + const WorkSpace *workspace) +{ + /* If the workspace is active, the active layout can be returned, no need for a lookup. */ + if (hook->active == workspace) { + return hook->act_layout; + } + + /* Inactive workspace */ + return static_cast( + workspace_relation_get_data_matching_parent(&workspace->hook_layout_relations, hook)); +} + +void BKE_workspace_active_layout_set(WorkSpaceInstanceHook *hook, + const int winid, + WorkSpace *workspace, + WorkSpaceLayout *layout) +{ + hook->act_layout = layout; + workspace_relation_ensure_updated(&workspace->hook_layout_relations, hook, winid, layout); +} + +bScreen *BKE_workspace_active_screen_get(const WorkSpaceInstanceHook *hook) +{ + return hook->act_layout->screen; +} +void BKE_workspace_active_screen_set(WorkSpaceInstanceHook *hook, + const int winid, + WorkSpace *workspace, + bScreen *screen) +{ + /* we need to find the WorkspaceLayout that wraps this screen */ + WorkSpaceLayout *layout = BKE_workspace_layout_find(hook->active, screen); + BKE_workspace_active_layout_set(hook, winid, workspace, layout); +} + +const char *BKE_workspace_layout_name_get(const WorkSpaceLayout *layout) +{ + return layout->name; +} +void BKE_workspace_layout_name_set(WorkSpace *workspace, + WorkSpaceLayout *layout, + const char *new_name) +{ + workspace_layout_name_set(workspace, layout, new_name); +} + +bScreen *BKE_workspace_layout_screen_get(const WorkSpaceLayout *layout) +{ + return layout->screen; +} + +/** \} */ 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 struct AnyTypeInfo { * Used when #T is stored directly in the inline buffer of the #Any. */ template -static constexpr AnyTypeInfo info_for_inline = { +inline constexpr AnyTypeInfo info_for_inline = { is_trivially_copy_constructible_extended_v ? nullptr : +[](void *dst, const void *src) { new (dst) T(*(const T *)src); }, @@ -57,7 +57,7 @@ static constexpr AnyTypeInfo info_for_inline = { */ template using Ptr = std::unique_ptr; template -static constexpr AnyTypeInfo info_for_unique_ptr = { +inline constexpr AnyTypeInfo info_for_unique_ptr = { [](void *dst, const void *src) { new (dst) Ptr(new T(**(const Ptr *)src)); }, [](void *dst, void *src) { new (dst) Ptr(new T(std::move(**(Ptr *)src))); }, [](void *src) { std::destroy_at((Ptr *)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 +inline void copy(const Span src, + const IndexMask selection, + MutableSpan 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 +inline void gather(const VArray &src, + const IndexMask indices, + MutableSpan 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 +inline void gather(const Span src, + const IndexMask indices, + MutableSpan 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 +inline void gather(const Span src, + const Span indices, + MutableSpan 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 +inline void gather(const VArray &src, + const Span indices, + MutableSpan 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`. + * + * 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`: + * - `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 + +#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(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 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(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( + 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 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( + 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. @@ -53,6 +53,11 @@ typedef unsigned int BLI_bitmap; (CHECK_TYPE_INLINE(_mem, MemArena *), \ ((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'. */ @@ -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> 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 void push(Args &&...args) + { + const ComputeContext *current = this->current(); + destruct_ptr context = allocator_.construct(current, std::forward(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 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(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(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(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(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(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(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(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(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 inline GVArray::GVArray(const VArray &varray) * #this is destructed. */ if (info.type == CommonVArrayInfo::Type::Span && !info.may_have_ownership) { *this = GVArray::ForSpan(GSpan(CPPType::get(), 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. @@ -197,6 +201,16 @@ class IndexRange { return start_ + size_ - 1 - n; } + /** + * 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. */ @@ -237,6 +251,19 @@ class IndexRange { return this->slice(range.start(), range.size()); } + /** + * 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 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; + +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(userdata); + fn(index, *ray, *hit); + }, + &fn); +} + using BVHTree_RangeQuery_CPP = FunctionRef; 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 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 values, const bool cyclic, MutableSpan -inline void linear_interpolation(const Span src, - const Span indices, - const Span factors, - MutableSpan dst) +inline void interpolate_to_masked(const Span src, + const Span indices, + const Span factors, + const IndexMask dst_mask, + MutableSpan 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 +inline void interpolate(const Span src, + const Span indices, + const Span factors, + MutableSpan 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 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 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 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(current_->next); return *this; } @@ -50,7 +50,7 @@ template 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 ¢er, + 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'). * */ -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 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 void initialized_reversed_move_n(T *src, int64_t n, T *dst) +template 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(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 void uninitialized_move_n(T *src, int64_t n, T *dst) +template 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(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 using destruct_ptr = std::unique_ptr 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 @@ -114,6 +114,14 @@ template class MultiValueMap { return {}; } + /** + * Get the number of keys. + */ + int64_t size() const + { + return map_.size(); + } + /** * NOTE: This signature will change when the implementation changes. */ @@ -137,6 +145,11 @@ template 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 @@ -35,16 +35,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. * @@ -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 class Pool : NonCopyable { + private: + using Chunk = TypedBuffer; + + /** Allocated item buffer. */ + Vector> 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 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 T &construct(ForwardT &&...value) + { + if (free_list_.is_empty()) { + values_.append(std::make_unique()); + 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(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 + /** \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::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 @@ -110,7 +109,6 @@ using ArrayValue = ContainerValue>, 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(range.first(), range.one_after_last(), grain_size), [&](const tbb::blocked_range &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(range.first(), range.one_after_last(), grain_size), identity, @@ -105,6 +108,23 @@ template 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 +void parallel_invoke(const bool use_threading, Functions &&...functions) +{ + if (use_threading) { + lazy_threading::send_hint(); + parallel_invoke(std::forward(functions)...); + } + else { + (functions(), ...); + } +} + /** See #BLI_task_isolate for a description of what isolating a task means. */ template 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 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 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 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 + #include "BLI_any.hh" #include "BLI_array.hh" #include "BLI_index_mask.hh" @@ -106,25 +108,7 @@ template class VArrayImpl { */ virtual void materialize(IndexMask mask, MutableSpan 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(info.data); - mask.foreach_index([&](const int64_t i) { dst[i] = src[i]; }); - break; - } - case CommonVArrayInfo::Type::Single: { - const T single = *static_cast(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 class VArrayImpl { virtual void materialize_to_uninitialized(IndexMask mask, MutableSpan 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(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(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 class VArrayImpl_For_Span : public VMutableArrayImpl { return data_ == static_cast(other_info.data); } + void materialize(IndexMask mask, MutableSpan r_span) const override + { + mask.foreach_index([&](const int64_t i) { r_span[i] = data_[i]; }); + } + + void materialize_to_uninitialized(IndexMask mask, MutableSpan 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 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 class VArrayImpl_For_Span : public VMutableArrayImpl { void materialize_compressed_to_uninitialized(IndexMask mask, MutableSpan 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 class VArrayImpl_For_Span_final final : public VArrayImpl_F public: using VArrayImpl_For_Span::VArrayImpl_For_Span; + VArrayImpl_For_Span_final(const Span data) + /* Cast const away, because the implementation for const and non const spans is shared. */ + : VArrayImpl_For_Span({const_cast(data.data()), data.size()}) + { + } + private: CommonVArrayInfo common_info() const final { @@ -370,6 +356,17 @@ template class VArrayImpl_For_Single final : public VArrayImpl { return CommonVArrayInfo(CommonVArrayInfo::Type::Single, true, &value_); } + void materialize(IndexMask mask, MutableSpan r_span) const override + { + r_span.fill_indices(mask, value_); + } + + void materialize_to_uninitialized(IndexMask mask, MutableSpan 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 r_span) const override { BLI_assert(mask.size() == r_span.size()); @@ -796,6 +793,18 @@ template class VArrayCommon { return *static_cast(info.data); } + /** + * Return the value that is returned for every index, if the array is stored as a single value. + */ + std::optional get_if_single() const + { + const CommonVArrayInfo info = impl_->common_info(); + if (info.type != CommonVArrayInfo::Type::Single) { + return std::nullopt; + } + return *static_cast(info.data); + } + /** * Return true when the other virtual references the same underlying memory. */ @@ -898,10 +907,7 @@ template class VArray : public VArrayCommon { VArray(varray_tag::span /* tag */, Span span) { - /* Cast const away, because the virtual array implementation for const and non const spans is - * shared. */ - MutableSpan mutable_span{const_cast(span.data()), span.size()}; - this->template emplace>(mutable_span); + this->template emplace>(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 IndexRange::as_span_internal() const return Span(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 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 #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(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 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 ¤t_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, 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> &local_sub_masks : sub_masks) { for (Vector &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, 0> hint_receivers; + +void send_hint() +{ + for (const FunctionRef &fn : hint_receivers) { + fn(); + } +} + +HintReceiver::HintReceiver(const FunctionRef 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 ¢er, + 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 &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 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 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 +# if defined(HAVE_EXECINFO_H) +# include +# endif # include #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 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 &>(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 factors(4); sample_uniform(lengths, true, indices, factors); Array results(4); - linear_interpolation(values, indices, factors, results); + interpolate(values, indices, factors, results); Array expected({ 0.0f, 1.33333f, @@ -54,7 +54,7 @@ TEST(length_parameterize, Float) Array factors(20); sample_uniform(lengths, true, indices, factors); Array results(20); - linear_interpolation(values, indices, factors, results); + interpolate(values, indices, factors, results); Array 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 factors(12); sample_uniform(lengths, true, indices, factors); Array results(12); - linear_interpolation(values, indices, factors, results); + interpolate(values, indices, factors, results); Array expected({ {0.0f, 0.0f}, {0.272727f, 0.0f}, @@ -105,7 +105,7 @@ TEST(length_parameterize, Float2Cyclic) Array factors(12); sample_uniform(lengths, false, indices, factors); Array results(12); - linear_interpolation(values, indices, factors, results); + interpolate(values, indices, factors, results); Array expected({ {0.0f, 0.0f}, {0.333333f, 0.0f}, @@ -135,7 +135,7 @@ TEST(length_parameterize, LineMany) Array factors(5007); sample_uniform(lengths, true, indices, factors); Array results(5007); - linear_interpolation(values, indices, factors, results); + interpolate(values, indices, factors, results); Array 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 factors(5007); sample_uniform(lengths, false, indices, factors); Array results(5007); - linear_interpolation(values, indices, factors, results); + interpolate(values, indices, factors, results); Array 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 factors(10); sample_uniform(lengths, false, indices, factors); Array results(10); - linear_interpolation(colors, indices, factors, results); + interpolate(colors, indices, factors, results); Array expected({ {0, 0, 0, 1}, {0.4, 0, 0, 1}, @@ -209,8 +209,7 @@ TEST(length_parameterize, ArbitraryFloatSimple) Array factors(4); sample_at_lengths(lengths, sample_lengths, indices, factors); Array results(4); - linear_interpolation(values, indices, factors, results); - results.as_span().print_as_lines("results"); + interpolate(values, indices, factors, results); Array expected({ 0.5f, 1.5f, @@ -233,8 +232,7 @@ TEST(length_parameterize, ArbitraryFloat2) Array factors(12); sample_at_lengths(lengths, sample_lengths, indices, factors); Array results(12); - linear_interpolation(values, indices, factors, results); - results.as_span().print_as_lines("results"); + interpolate(values, indices, factors, results); Array 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 /* 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 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 pool; + EXPECT_EQ(pool.size(), 0); +} + +TEST(pool, Allocation) +{ + Vector ptrs; + Pool 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 ptrs; + Pool 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::iterator iter1 = set.begin(); int value1 = *iter1; Set::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::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({2}).begin())); vec.remove(0); - EXPECT_TRUE(std::equal(vec.begin(), vec.end(), Span({}).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.c deleted file mode 100644 index e1527201e22..00000000000 --- a/source/blender/blenloader/intern/blend_validate.c +++ /dev/null @@ -1,200 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/** \file - * \ingroup blenloader - * - * Utils to check/validate a Main is in sane state, - * only checks relations between data-blocks and libraries for now. - * - * \note Does not *fix* anything, only reports found errors. - */ - -#include /* for #strrchr #strncmp #strstr */ - -#include "BLI_utildefines.h" - -#include "BLI_blenlib.h" -#include "BLI_linklist.h" - -#include "MEM_guardedalloc.h" - -#include "DNA_key_types.h" -#include "DNA_sdna_types.h" -#include "DNA_windowmanager_types.h" - -#include "BKE_key.h" -#include "BKE_lib_id.h" -#include "BKE_library.h" -#include "BKE_main.h" -#include "BKE_report.h" - -#include "BLO_blend_validate.h" -#include "BLO_readfile.h" - -#include "readfile.h" - -bool BLO_main_validate_libraries(Main *bmain, ReportList *reports) -{ - ListBase mainlist; - bool is_valid = true; - - BKE_main_lock(bmain); - - blo_split_main(&mainlist, bmain); - - ListBase *lbarray[INDEX_ID_MAX]; - int i = set_listbasepointers(bmain, lbarray); - while (i--) { - for (ID *id = lbarray[i]->first; id != NULL; id = id->next) { - if (ID_IS_LINKED(id)) { - is_valid = false; - BKE_reportf(reports, - RPT_ERROR, - "ID %s is in local database while being linked from library %s!", - id->name, - id->lib->filepath); - } - } - } - - for (Main *curmain = bmain->next; curmain != NULL; curmain = curmain->next) { - Library *curlib = curmain->curlib; - if (curlib == NULL) { - BKE_report(reports, RPT_ERROR, "Library database with NULL library data-block!"); - continue; - } - - BKE_library_filepath_set(bmain, curlib, curlib->filepath); - BlendFileReadReport bf_reports = {.reports = reports}; - BlendHandle *bh = BLO_blendhandle_from_file(curlib->filepath_abs, &bf_reports); - - if (bh == NULL) { - BKE_reportf(reports, - RPT_ERROR, - "Library ID %s not found at expected path %s!", - curlib->id.name, - curlib->filepath_abs); - continue; - } - - i = set_listbasepointers(curmain, lbarray); - while (i--) { - ID *id = lbarray[i]->first; - if (id == NULL) { - continue; - } - - if (GS(id->name) == ID_LI) { - is_valid = false; - BKE_reportf(reports, - RPT_ERROR, - "Library ID %s in library %s, this should not happen!", - id->name, - curlib->filepath); - continue; - } - - int totnames = 0; - LinkNode *names = BLO_blendhandle_get_datablock_names(bh, GS(id->name), false, &totnames); - for (; id != NULL; 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->name, - curlib->filepath); - continue; - } - if (id->lib != curlib) { - is_valid = false; - BKE_reportf(reports, RPT_ERROR, "ID %s has mismatched lib pointer!", id->name); - continue; - } - - LinkNode *name = names; - for (; name; name = name->next) { - char *str_name = (char *)name->link; - if (id->name[2] == str_name[0] && STREQ(str_name, id->name + 2)) { - break; - } - } - - if (name == NULL) { - is_valid = false; - BKE_reportf(reports, - RPT_ERROR, - "ID %s not found in library %s anymore!", - id->name, - id->lib->filepath); - continue; - } - } - - BLI_linklist_freeN(names); - } - - BLO_blendhandle_close(bh); - } - - blo_join_main(&mainlist); - - BLI_assert(BLI_listbase_is_single(&mainlist)); - BLI_assert(mainlist.first == (void *)bmain); - - BKE_main_unlock(bmain); - - return is_valid; -} - -bool BLO_main_validate_shapekeys(Main *bmain, ReportList *reports) -{ - ListBase *lb; - ID *id; - bool is_valid = true; - - BKE_main_lock(bmain); - - FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) { - FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id) { - if (!BKE_key_idtype_support(GS(id->name))) { - break; - } - if (!ID_IS_LINKED(id)) { - /* We assume lib data is valid... */ - Key *shapekey = BKE_key_from_id(id); - if (shapekey != NULL && shapekey->from != id) { - is_valid = false; - BKE_reportf(reports, - RPT_ERROR, - "ID %s uses shapekey %s, but its 'from' pointer is invalid (%p), fixing...", - id->name, - shapekey->id.name, - shapekey->from); - shapekey->from = id; - } - } - } - FOREACH_MAIN_LISTBASE_ID_END; - } - FOREACH_MAIN_LISTBASE_END; - - BKE_main_unlock(bmain); - - /* 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) { - continue; - } - - BKE_reportf(reports, - RPT_ERROR, - "Shapekey %s has an invalid 'from' pointer (%p), it will be deleted", - shapekey->id.name, - shapekey->from); - BKE_id_delete(bmain, shapekey); - } - - return is_valid; -} diff --git a/source/blender/blenloader/intern/blend_validate.cc b/source/blender/blenloader/intern/blend_validate.cc new file mode 100644 index 00000000000..96314ab4324 --- /dev/null +++ b/source/blender/blenloader/intern/blend_validate.cc @@ -0,0 +1,202 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup blenloader + * + * Utils to check/validate a Main is in sane state, + * only checks relations between data-blocks and libraries for now. + * + * \note Does not *fix* anything, only reports found errors. + */ + +#include /* for #strrchr #strncmp #strstr */ + +#include "BLI_utildefines.h" + +#include "BLI_blenlib.h" +#include "BLI_linklist.h" + +#include "MEM_guardedalloc.h" + +#include "DNA_key_types.h" +#include "DNA_sdna_types.h" +#include "DNA_windowmanager_types.h" + +#include "BKE_key.h" +#include "BKE_lib_id.h" +#include "BKE_library.h" +#include "BKE_main.h" +#include "BKE_report.h" + +#include "BLO_blend_validate.h" +#include "BLO_readfile.h" + +#include "readfile.h" + +bool BLO_main_validate_libraries(Main *bmain, ReportList *reports) +{ + ListBase mainlist; + bool is_valid = true; + + BKE_main_lock(bmain); + + blo_split_main(&mainlist, bmain); + + ListBase *lbarray[INDEX_ID_MAX]; + int i = set_listbasepointers(bmain, lbarray); + while (i--) { + for (ID *id = static_cast(lbarray[i]->first); id != nullptr; + id = static_cast(id->next)) { + if (ID_IS_LINKED(id)) { + is_valid = false; + BKE_reportf(reports, + RPT_ERROR, + "ID %s is in local database while being linked from library %s!", + id->name, + id->lib->filepath); + } + } + } + + for (Main *curmain = bmain->next; curmain != nullptr; curmain = curmain->next) { + Library *curlib = curmain->curlib; + 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{}; + bf_reports.reports = reports; + BlendHandle *bh = BLO_blendhandle_from_file(curlib->filepath_abs, &bf_reports); + + if (bh == nullptr) { + BKE_reportf(reports, + RPT_ERROR, + "Library ID %s not found at expected path %s!", + curlib->id.name, + curlib->filepath_abs); + continue; + } + + i = set_listbasepointers(curmain, lbarray); + while (i--) { + ID *id = static_cast(lbarray[i]->first); + if (id == nullptr) { + continue; + } + + if (GS(id->name) == ID_LI) { + is_valid = false; + BKE_reportf(reports, + RPT_ERROR, + "Library ID %s in library %s, this should not happen!", + id->name, + curlib->filepath); + continue; + } + + int totnames = 0; + LinkNode *names = BLO_blendhandle_get_datablock_names(bh, GS(id->name), false, &totnames); + for (; id != nullptr; id = static_cast(id->next)) { + if (!ID_IS_LINKED(id)) { + is_valid = false; + BKE_reportf(reports, + RPT_ERROR, + "ID %s has nullptr lib pointer while being in library %s!", + id->name, + curlib->filepath); + continue; + } + if (id->lib != curlib) { + is_valid = false; + BKE_reportf(reports, RPT_ERROR, "ID %s has mismatched lib pointer!", id->name); + continue; + } + + LinkNode *name = names; + for (; name; name = name->next) { + char *str_name = (char *)name->link; + if (id->name[2] == str_name[0] && STREQ(str_name, id->name + 2)) { + break; + } + } + + if (name == nullptr) { + is_valid = false; + BKE_reportf(reports, + RPT_ERROR, + "ID %s not found in library %s anymore!", + id->name, + id->lib->filepath); + continue; + } + } + + BLI_linklist_freeN(names); + } + + BLO_blendhandle_close(bh); + } + + blo_join_main(&mainlist); + + BLI_assert(BLI_listbase_is_single(&mainlist)); + BLI_assert(mainlist.first == (void *)bmain); + + BKE_main_unlock(bmain); + + return is_valid; +} + +bool BLO_main_validate_shapekeys(Main *bmain, ReportList *reports) +{ + ListBase *lb; + ID *id; + bool is_valid = true; + + BKE_main_lock(bmain); + + FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) { + FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id) { + if (!BKE_key_idtype_support(GS(id->name))) { + break; + } + if (!ID_IS_LINKED(id)) { + /* We assume lib data is valid... */ + Key *shapekey = BKE_key_from_id(id); + if (shapekey != nullptr && shapekey->from != id) { + is_valid = false; + BKE_reportf(reports, + RPT_ERROR, + "ID %s uses shapekey %s, but its 'from' pointer is invalid (%p), fixing...", + id->name, + shapekey->id.name, + shapekey->from); + shapekey->from = id; + } + } + } + FOREACH_MAIN_LISTBASE_ID_END; + } + FOREACH_MAIN_LISTBASE_END; + + BKE_main_unlock(bmain); + + /* 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 != nullptr) { + continue; + } + + BKE_reportf(reports, + RPT_ERROR, + "Shapekey %s has an invalid 'from' pointer (%p), it will be deleted", + shapekey->id.name, + shapekey->from); + BKE_id_delete(bmain, shapekey); + } + + return is_valid; +} diff --git a/source/blender/blenloader/intern/readblenentry.c b/source/blender/blenloader/intern/readblenentry.c deleted file mode 100644 index 1bfa3b0e2bb..00000000000 --- a/source/blender/blenloader/intern/readblenentry.c +++ /dev/null @@ -1,453 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright 2001-2002 NaN Holding BV. All rights reserved. */ - -/** \file - * \ingroup blenloader - * `.blend` file reading entry point. - */ - -#include - -#include -#include -#include -#include - -#include "MEM_guardedalloc.h" - -#include "BLI_ghash.h" -#include "BLI_linklist.h" -#include "BLI_listbase.h" -#include "BLI_string.h" -#include "BLI_utildefines.h" - -#include "DNA_genfile.h" -#include "DNA_sdna_types.h" - -#include "BKE_icons.h" -#include "BKE_idtype.h" -#include "BKE_main.h" - -#include "BLO_blend_defs.h" -#include "BLO_readfile.h" -#include "BLO_undofile.h" - -#include "readfile.h" - -#include "BLI_sys_types.h" /* Needed for `intptr_t`. */ - -#ifdef WIN32 -# include "BLI_winstuff.h" -#endif - -/* local prototypes --------------------- */ -void BLO_blendhandle_print_sizes(BlendHandle *bh, void *fp); - -/* Access routines used by filesel. */ - -BlendHandle *BLO_blendhandle_from_file(const char *filepath, BlendFileReadReport *reports) -{ - BlendHandle *bh; - - bh = (BlendHandle *)blo_filedata_from_file(filepath, reports); - - return bh; -} - -BlendHandle *BLO_blendhandle_from_memory(const void *mem, - int memsize, - BlendFileReadReport *reports) -{ - BlendHandle *bh; - - bh = (BlendHandle *)blo_filedata_from_memory(mem, memsize, reports); - - return bh; -} - -void BLO_blendhandle_print_sizes(BlendHandle *bh, void *fp) -{ - FileData *fd = (FileData *)bh; - BHead *bhead; - - fprintf(fp, "[\n"); - for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) { - if (bhead->code == ENDB) { - break; - } - - const SDNA_Struct *struct_info = fd->filesdna->structs[bhead->SDNAnr]; - const char *name = fd->filesdna->types[struct_info->type]; - char buf[4]; - - buf[0] = (bhead->code >> 24) & 0xFF; - buf[1] = (bhead->code >> 16) & 0xFF; - buf[2] = (bhead->code >> 8) & 0xFF; - buf[3] = (bhead->code >> 0) & 0xFF; - - buf[0] = buf[0] ? buf[0] : ' '; - buf[1] = buf[1] ? buf[1] : ' '; - buf[2] = buf[2] ? buf[2] : ' '; - buf[3] = buf[3] ? buf[3] : ' '; - - fprintf(fp, - "['%.4s', '%s', %d, %ld ],\n", - buf, - name, - bhead->nr, - (long int)(bhead->len + sizeof(BHead))); - } - fprintf(fp, "]\n"); -} - -LinkNode *BLO_blendhandle_get_datablock_names(BlendHandle *bh, - int ofblocktype, - const bool use_assets_only, - int *r_tot_names) -{ - FileData *fd = (FileData *)bh; - LinkNode *names = NULL; - BHead *bhead; - int tot = 0; - - for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) { - if (bhead->code == ofblocktype) { - const char *idname = blo_bhead_id_name(fd, bhead); - if (use_assets_only && blo_bhead_id_asset_data_address(fd, bhead) == NULL) { - continue; - } - - BLI_linklist_prepend(&names, BLI_strdup(idname + 2)); - tot++; - } - else if (bhead->code == ENDB) { - break; - } - } - - *r_tot_names = tot; - return names; -} - -LinkNode *BLO_blendhandle_get_datablock_info(BlendHandle *bh, - int ofblocktype, - const bool use_assets_only, - int *r_tot_info_items) -{ - FileData *fd = (FileData *)bh; - LinkNode *infos = NULL; - BHead *bhead; - int tot = 0; - - for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) { - if (bhead->code == ENDB) { - break; - } - if (bhead->code == ofblocktype) { - const char *name = blo_bhead_id_name(fd, bhead) + 2; - AssetMetaData *asset_meta_data = blo_bhead_id_asset_data_address(fd, bhead); - - const bool is_asset = asset_meta_data != NULL; - const bool skip_datablock = use_assets_only && !is_asset; - if (skip_datablock) { - continue; - } - struct BLODataBlockInfo *info = MEM_mallocN(sizeof(*info), __func__); - - /* Lastly, read asset data from the following blocks. */ - if (asset_meta_data) { - bhead = blo_read_asset_data_block(fd, bhead, &asset_meta_data); - /* blo_read_asset_data_block() reads all DATA heads and already advances bhead to the - * next non-DATA one. Go back, so the loop doesn't skip the non-DATA head. */ - bhead = blo_bhead_prev(fd, bhead); - } - - STRNCPY(info->name, name); - info->asset_data = asset_meta_data; - - BLI_linklist_prepend(&infos, info); - tot++; - } - } - - *r_tot_info_items = tot; - return infos; -} - -/** - * Read the preview rects and store in `result`. - * - * `bhead` should point to the block that sourced the `preview_from_file` - * parameter. - * `bhead` parameter is consumed. The correct bhead pointing to the next bhead in the file after - * the preview rects is returned by this function. - * \param fd: The filedata to read the data from. - * \param bhead: should point to the block that sourced the `preview_from_file parameter`. - * bhead is consumed. the new bhead is returned by this function. - * \param result: the Preview Image where the preview rect will be stored. - * \param preview_from_file: The read PreviewImage where the bhead points to. The rects of this - * \return PreviewImage or NULL when no preview Images have been found. Caller owns the returned - */ -static BHead *blo_blendhandle_read_preview_rects(FileData *fd, - BHead *bhead, - PreviewImage *result, - const PreviewImage *preview_from_file) -{ - for (int preview_index = 0; preview_index < NUM_ICON_SIZES; preview_index++) { - if (preview_from_file->rect[preview_index] && preview_from_file->w[preview_index] && - preview_from_file->h[preview_index]) { - 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"); - } - else { - /* This should not be needed, but can happen in 'broken' .blend files, - * better handle this gracefully than crashing. */ - BLI_assert(preview_from_file->rect[preview_index] == NULL && - preview_from_file->w[preview_index] == 0 && - preview_from_file->h[preview_index] == 0); - result->rect[preview_index] = NULL; - result->w[preview_index] = result->h[preview_index] = 0; - } - BKE_previewimg_finish(result, preview_index); - } - - return bhead; -} - -PreviewImage *BLO_blendhandle_get_preview_for_id(BlendHandle *bh, - int ofblocktype, - const char *name) -{ - FileData *fd = (FileData *)bh; - bool looking = false; - const int sdna_preview_image = DNA_struct_find_nr(fd->filesdna, "PreviewImage"); - - 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"); - - if (preview_from_file == NULL) { - break; - } - - PreviewImage *result = MEM_dupallocN(preview_from_file); - bhead = blo_blendhandle_read_preview_rects(fd, bhead, result, preview_from_file); - MEM_freeN(preview_from_file); - return result; - } - } - else if (looking || bhead->code == ENDB) { - /* We were looking for a preview image, but didn't find any belonging to block. So it doesn't - * exist. */ - break; - } - else if (bhead->code == ofblocktype) { - const char *idname = blo_bhead_id_name(fd, bhead); - if (STREQ(&idname[2], name)) { - looking = true; - } - } - } - - return NULL; -} - -LinkNode *BLO_blendhandle_get_previews(BlendHandle *bh, int ofblocktype, int *r_tot_prev) -{ - FileData *fd = (FileData *)bh; - LinkNode *previews = NULL; - BHead *bhead; - int looking = 0; - PreviewImage *prv = NULL; - PreviewImage *new_prv = NULL; - int tot = 0; - - for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) { - if (bhead->code == ofblocktype) { - const char *idname = blo_bhead_id_name(fd, bhead); - switch (GS(idname)) { - case ID_MA: /* fall through */ - case ID_TE: /* fall through */ - case ID_IM: /* fall through */ - case ID_WO: /* fall through */ - case ID_LA: /* fall through */ - case ID_OB: /* fall through */ - case ID_GR: /* fall through */ - case ID_SCE: /* fall through */ - case ID_AC: /* fall through */ - case ID_NT: /* fall through */ - new_prv = MEM_callocN(sizeof(PreviewImage), "newpreview"); - BLI_linklist_prepend(&previews, new_prv); - tot++; - looking = 1; - break; - default: - break; - } - } - 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"); - - if (prv) { - memcpy(new_prv, prv, sizeof(PreviewImage)); - bhead = blo_blendhandle_read_preview_rects(fd, bhead, new_prv, prv); - MEM_freeN(prv); - } - } - } - } - else if (bhead->code == ENDB) { - break; - } - else { - looking = 0; - new_prv = NULL; - prv = NULL; - } - } - - *r_tot_prev = tot; - return previews; -} - -LinkNode *BLO_blendhandle_get_linkable_groups(BlendHandle *bh) -{ - FileData *fd = (FileData *)bh; - GSet *gathered = BLI_gset_ptr_new("linkable_groups gh"); - LinkNode *names = NULL; - BHead *bhead; - - for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) { - if (bhead->code == ENDB) { - break; - } - if (BKE_idtype_idcode_is_valid(bhead->code)) { - if (BKE_idtype_idcode_is_linkable(bhead->code)) { - const char *str = BKE_idtype_idcode_to_name(bhead->code); - - if (BLI_gset_add(gathered, (void *)str)) { - BLI_linklist_prepend(&names, BLI_strdup(str)); - } - } - } - } - - BLI_gset_free(gathered, NULL); - - return names; -} - -void BLO_blendhandle_close(BlendHandle *bh) -{ - FileData *fd = (FileData *)bh; - - blo_filedata_free(fd); -} - -/**********/ - -BlendFileData *BLO_read_from_file(const char *filepath, - eBLOReadSkip skip_flags, - BlendFileReadReport *reports) -{ - BlendFileData *bfd = NULL; - FileData *fd; - - fd = blo_filedata_from_file(filepath, reports); - if (fd) { - fd->skip_flags = skip_flags; - bfd = blo_read_file_internal(fd, filepath); - blo_filedata_free(fd); - } - - return bfd; -} - -BlendFileData *BLO_read_from_memory(const void *mem, - int memsize, - eBLOReadSkip skip_flags, - ReportList *reports) -{ - BlendFileData *bfd = NULL; - FileData *fd; - BlendFileReadReport bf_reports = {.reports = reports}; - - fd = blo_filedata_from_memory(mem, memsize, &bf_reports); - if (fd) { - fd->skip_flags = skip_flags; - bfd = blo_read_file_internal(fd, ""); - blo_filedata_free(fd); - } - - return bfd; -} - -BlendFileData *BLO_read_from_memfile(Main *oldmain, - const char *filepath, - MemFile *memfile, - const struct BlendFileReadParams *params, - ReportList *reports) -{ - BlendFileData *bfd = NULL; - FileData *fd; - ListBase old_mainlist; - BlendFileReadReport bf_reports = {.reports = reports}; - - fd = blo_filedata_from_memfile(memfile, params, &bf_reports); - if (fd) { - fd->skip_flags = params->skip_flags; - BLI_strncpy(fd->relabase, filepath, sizeof(fd->relabase)); - - /* separate libraries from old main */ - blo_split_main(&old_mainlist, oldmain); - /* add the library pointers in oldmap lookup */ - blo_add_library_pointer_map(&old_mainlist, fd); - - 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); - } - - /* removed packed data from this trick - it's internal data that needs saves */ - - /* Store all existing ID caches pointers into a mapping, to allow restoring them into newly - * read IDs whenever possible. */ - blo_cache_storage_init(fd, oldmain); - - bfd = blo_read_file_internal(fd, filepath); - - /* Ensure relinked caches are not freed together with their old IDs. */ - blo_cache_storage_old_bmain_clear(fd, oldmain); - - /* Still in-use libraries have already been moved from oldmain to new mainlist, - * but oldmain itself shall *never* be 'transferred' to new mainlist! */ - BLI_assert(old_mainlist.first == oldmain); - - /* That way, libs (aka mains) we did not reuse in new undone/redone state - * will be cleared together with oldmain... */ - blo_join_main(&old_mainlist); - - blo_filedata_free(fd); - } - - return bfd; -} - -void BLO_blendfiledata_free(BlendFileData *bfd) -{ - if (bfd->main) { - BKE_main_free(bfd->main); - } - - if (bfd->user) { - MEM_freeN(bfd->user); - } - - MEM_freeN(bfd); -} diff --git a/source/blender/blenloader/intern/readblenentry.cc b/source/blender/blenloader/intern/readblenentry.cc new file mode 100644 index 00000000000..e3d6864b962 --- /dev/null +++ b/source/blender/blenloader/intern/readblenentry.cc @@ -0,0 +1,458 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2001-2002 NaN Holding BV. All rights reserved. */ + +/** \file + * \ingroup blenloader + * `.blend` file reading entry point. + */ + +#include + +#include +#include +#include +#include + +#include "MEM_guardedalloc.h" + +#include "BLI_ghash.h" +#include "BLI_linklist.h" +#include "BLI_listbase.h" +#include "BLI_string.h" +#include "BLI_utildefines.h" + +#include "DNA_genfile.h" +#include "DNA_sdna_types.h" + +#include "BKE_icons.h" +#include "BKE_idtype.h" +#include "BKE_main.h" + +#include "BLO_blend_defs.h" +#include "BLO_readfile.h" +#include "BLO_undofile.h" + +#include "readfile.h" + +#include "BLI_sys_types.h" /* Needed for `intptr_t`. */ + +#ifdef WIN32 +# include "BLI_winstuff.h" +#endif + +/* local prototypes --------------------- */ +void BLO_blendhandle_print_sizes(BlendHandle *bh, void *fp); + +/* Access routines used by filesel. */ + +BlendHandle *BLO_blendhandle_from_file(const char *filepath, BlendFileReadReport *reports) +{ + BlendHandle *bh; + + bh = (BlendHandle *)blo_filedata_from_file(filepath, reports); + + return bh; +} + +BlendHandle *BLO_blendhandle_from_memory(const void *mem, + int memsize, + BlendFileReadReport *reports) +{ + BlendHandle *bh; + + bh = (BlendHandle *)blo_filedata_from_memory(mem, memsize, reports); + + return bh; +} + +void BLO_blendhandle_print_sizes(BlendHandle *bh, void *fp) +{ + FileData *fd = (FileData *)bh; + BHead *bhead; + + fprintf(static_cast(fp), "[\n"); + for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) { + if (bhead->code == ENDB) { + break; + } + + const SDNA_Struct *struct_info = fd->filesdna->structs[bhead->SDNAnr]; + const char *name = fd->filesdna->types[struct_info->type]; + char buf[4]; + + buf[0] = (bhead->code >> 24) & 0xFF; + buf[1] = (bhead->code >> 16) & 0xFF; + buf[2] = (bhead->code >> 8) & 0xFF; + buf[3] = (bhead->code >> 0) & 0xFF; + + buf[0] = buf[0] ? buf[0] : ' '; + buf[1] = buf[1] ? buf[1] : ' '; + buf[2] = buf[2] ? buf[2] : ' '; + buf[3] = buf[3] ? buf[3] : ' '; + + fprintf(static_cast(fp), + "['%.4s', '%s', %d, %ld ],\n", + buf, + name, + bhead->nr, + (long int)(bhead->len + sizeof(BHead))); + } + fprintf(static_cast(fp), "]\n"); +} + +LinkNode *BLO_blendhandle_get_datablock_names(BlendHandle *bh, + int ofblocktype, + const bool use_assets_only, + int *r_tot_names) +{ + FileData *fd = (FileData *)bh; + LinkNode *names = NULL; + BHead *bhead; + int tot = 0; + + for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) { + if (bhead->code == ofblocktype) { + const char *idname = blo_bhead_id_name(fd, bhead); + if (use_assets_only && blo_bhead_id_asset_data_address(fd, bhead) == NULL) { + continue; + } + + BLI_linklist_prepend(&names, BLI_strdup(idname + 2)); + tot++; + } + else if (bhead->code == ENDB) { + break; + } + } + + *r_tot_names = tot; + return names; +} + +LinkNode *BLO_blendhandle_get_datablock_info(BlendHandle *bh, + int ofblocktype, + const bool use_assets_only, + int *r_tot_info_items) +{ + FileData *fd = (FileData *)bh; + LinkNode *infos = NULL; + BHead *bhead; + int tot = 0; + + for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) { + if (bhead->code == ENDB) { + break; + } + if (bhead->code == ofblocktype) { + const char *name = blo_bhead_id_name(fd, bhead) + 2; + AssetMetaData *asset_meta_data = blo_bhead_id_asset_data_address(fd, bhead); + + const bool is_asset = asset_meta_data != NULL; + const bool skip_datablock = use_assets_only && !is_asset; + if (skip_datablock) { + continue; + } + struct BLODataBlockInfo *info = static_cast( + MEM_mallocN(sizeof(*info), __func__)); + + /* Lastly, read asset data from the following blocks. */ + if (asset_meta_data) { + bhead = blo_read_asset_data_block(fd, bhead, &asset_meta_data); + /* blo_read_asset_data_block() reads all DATA heads and already advances bhead to the + * next non-DATA one. Go back, so the loop doesn't skip the non-DATA head. */ + bhead = blo_bhead_prev(fd, bhead); + } + + STRNCPY(info->name, name); + info->asset_data = asset_meta_data; + + BLI_linklist_prepend(&infos, info); + tot++; + } + } + + *r_tot_info_items = tot; + return infos; +} + +/** + * Read the preview rects and store in `result`. + * + * `bhead` should point to the block that sourced the `preview_from_file` + * parameter. + * `bhead` parameter is consumed. The correct bhead pointing to the next bhead in the file after + * the preview rects is returned by this function. + * \param fd: The filedata to read the data from. + * \param bhead: should point to the block that sourced the `preview_from_file parameter`. + * bhead is consumed. the new bhead is returned by this function. + * \param result: the Preview Image where the preview rect will be stored. + * \param preview_from_file: The read PreviewImage where the bhead points to. The rects of this + * \return PreviewImage or NULL when no preview Images have been found. Caller owns the returned + */ +static BHead *blo_blendhandle_read_preview_rects(FileData *fd, + BHead *bhead, + PreviewImage *result, + const PreviewImage *preview_from_file) +{ + for (int preview_index = 0; preview_index < NUM_ICON_SIZES; preview_index++) { + if (preview_from_file->rect[preview_index] && preview_from_file->w[preview_index] && + preview_from_file->h[preview_index]) { + 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] = static_cast( + BLO_library_read_struct(fd, bhead, "PreviewImage Icon Rect")); + } + else { + /* This should not be needed, but can happen in 'broken' .blend files, + * better handle this gracefully than crashing. */ + BLI_assert(preview_from_file->rect[preview_index] == NULL && + preview_from_file->w[preview_index] == 0 && + preview_from_file->h[preview_index] == 0); + result->rect[preview_index] = NULL; + result->w[preview_index] = result->h[preview_index] = 0; + } + BKE_previewimg_finish(result, preview_index); + } + + return bhead; +} + +PreviewImage *BLO_blendhandle_get_preview_for_id(BlendHandle *bh, + int ofblocktype, + const char *name) +{ + FileData *fd = (FileData *)bh; + bool looking = false; + const int sdna_preview_image = DNA_struct_find_nr(fd->filesdna, "PreviewImage"); + + 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 = static_cast( + BLO_library_read_struct(fd, bhead, "PreviewImage")); + + if (preview_from_file == NULL) { + break; + } + + PreviewImage *result = static_cast(MEM_dupallocN(preview_from_file)); + bhead = blo_blendhandle_read_preview_rects(fd, bhead, result, preview_from_file); + MEM_freeN(preview_from_file); + return result; + } + } + else if (looking || bhead->code == ENDB) { + /* We were looking for a preview image, but didn't find any belonging to block. So it doesn't + * exist. */ + break; + } + else if (bhead->code == ofblocktype) { + const char *idname = blo_bhead_id_name(fd, bhead); + if (STREQ(&idname[2], name)) { + looking = true; + } + } + } + + return NULL; +} + +LinkNode *BLO_blendhandle_get_previews(BlendHandle *bh, int ofblocktype, int *r_tot_prev) +{ + FileData *fd = (FileData *)bh; + LinkNode *previews = NULL; + BHead *bhead; + int looking = 0; + PreviewImage *prv = NULL; + PreviewImage *new_prv = NULL; + int tot = 0; + + for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) { + if (bhead->code == ofblocktype) { + const char *idname = blo_bhead_id_name(fd, bhead); + switch (GS(idname)) { + case ID_MA: /* fall through */ + case ID_TE: /* fall through */ + case ID_IM: /* fall through */ + case ID_WO: /* fall through */ + case ID_LA: /* fall through */ + case ID_OB: /* fall through */ + case ID_GR: /* fall through */ + case ID_SCE: /* fall through */ + case ID_AC: /* fall through */ + case ID_NT: /* fall through */ + new_prv = static_cast(MEM_callocN(sizeof(PreviewImage), "newpreview")); + BLI_linklist_prepend(&previews, new_prv); + tot++; + looking = 1; + break; + default: + break; + } + } + else if (bhead->code == DATA) { + if (looking) { + if (bhead->SDNAnr == DNA_struct_find_nr(fd->filesdna, "PreviewImage")) { + prv = static_cast(BLO_library_read_struct(fd, bhead, "PreviewImage")); + + if (prv) { + memcpy(new_prv, prv, sizeof(PreviewImage)); + bhead = blo_blendhandle_read_preview_rects(fd, bhead, new_prv, prv); + MEM_freeN(prv); + } + } + } + } + else if (bhead->code == ENDB) { + break; + } + else { + looking = 0; + new_prv = NULL; + prv = NULL; + } + } + + *r_tot_prev = tot; + return previews; +} + +LinkNode *BLO_blendhandle_get_linkable_groups(BlendHandle *bh) +{ + FileData *fd = (FileData *)bh; + GSet *gathered = BLI_gset_ptr_new("linkable_groups gh"); + LinkNode *names = NULL; + BHead *bhead; + + for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) { + if (bhead->code == ENDB) { + break; + } + if (BKE_idtype_idcode_is_valid(bhead->code)) { + if (BKE_idtype_idcode_is_linkable(bhead->code)) { + const char *str = BKE_idtype_idcode_to_name(bhead->code); + + if (BLI_gset_add(gathered, (void *)str)) { + BLI_linklist_prepend(&names, BLI_strdup(str)); + } + } + } + } + + BLI_gset_free(gathered, NULL); + + return names; +} + +void BLO_blendhandle_close(BlendHandle *bh) +{ + FileData *fd = (FileData *)bh; + + blo_filedata_free(fd); +} + +/**********/ + +BlendFileData *BLO_read_from_file(const char *filepath, + eBLOReadSkip skip_flags, + BlendFileReadReport *reports) +{ + BlendFileData *bfd = NULL; + FileData *fd; + + fd = blo_filedata_from_file(filepath, reports); + if (fd) { + fd->skip_flags = skip_flags; + bfd = blo_read_file_internal(fd, filepath); + blo_filedata_free(fd); + } + + return bfd; +} + +BlendFileData *BLO_read_from_memory(const void *mem, + int memsize, + eBLOReadSkip skip_flags, + ReportList *reports) +{ + BlendFileData *bfd = NULL; + FileData *fd; + BlendFileReadReport bf_reports{}; + bf_reports.reports = reports; + + fd = blo_filedata_from_memory(mem, memsize, &bf_reports); + if (fd) { + fd->skip_flags = skip_flags; + bfd = blo_read_file_internal(fd, ""); + blo_filedata_free(fd); + } + + return bfd; +} + +BlendFileData *BLO_read_from_memfile(Main *oldmain, + const char *filepath, + MemFile *memfile, + const struct BlendFileReadParams *params, + ReportList *reports) +{ + BlendFileData *bfd = NULL; + FileData *fd; + ListBase old_mainlist; + BlendFileReadReport bf_reports{}; + bf_reports.reports = reports; + + fd = blo_filedata_from_memfile(memfile, params, &bf_reports); + if (fd) { + fd->skip_flags = eBLOReadSkip(params->skip_flags); + BLI_strncpy(fd->relabase, filepath, sizeof(fd->relabase)); + + /* separate libraries from old main */ + blo_split_main(&old_mainlist, oldmain); + /* add the library pointers in oldmap lookup */ + blo_add_library_pointer_map(&old_mainlist, fd); + + 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, static_cast
(old_mainlist.first)); + } + + /* removed packed data from this trick - it's internal data that needs saves */ + + /* Store all existing ID caches pointers into a mapping, to allow restoring them into newly + * read IDs whenever possible. */ + blo_cache_storage_init(fd, oldmain); + + bfd = blo_read_file_internal(fd, filepath); + + /* Ensure relinked caches are not freed together with their old IDs. */ + blo_cache_storage_old_bmain_clear(fd, oldmain); + + /* Still in-use libraries have already been moved from oldmain to new mainlist, + * but oldmain itself shall *never* be 'transferred' to new mainlist! */ + BLI_assert(old_mainlist.first == oldmain); + + /* That way, libs (aka mains) we did not reuse in new undone/redone state + * will be cleared together with oldmain... */ + blo_join_main(&old_mainlist); + + blo_filedata_free(fd); + } + + return bfd; +} + +void BLO_blendfiledata_free(BlendFileData *bfd) +{ + if (bfd->main) { + BKE_main_free(bfd->main); + } + + if (bfd->user) { + MEM_freeN(bfd->user); + } + + MEM_freeN(bfd); +} diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c deleted file mode 100644 index f0d390677bb..00000000000 --- a/source/blender/blenloader/intern/readfile.c +++ /dev/null @@ -1,5152 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright 2001-2002 NaN Holding BV. All rights reserved. */ - -/** \file - * \ingroup blenloader - */ - -#include /* for isdigit. */ -#include /* for open flags (O_BINARY, O_RDONLY). */ -#include -#include /* for va_start/end. */ -#include /* for offsetof. */ -#include /* for atoi. */ -#include /* for gmtime. */ - -#include "BLI_utildefines.h" -#ifndef WIN32 -# include /* for read close */ -#else -# include "BLI_winstuff.h" -# include "winsock2.h" -# include /* for open close read */ -#endif - -#include "CLG_log.h" - -/* allow readfile to use deprecated functionality */ -#define DNA_DEPRECATED_ALLOW - -#include "DNA_anim_types.h" -#include "DNA_asset_types.h" -#include "DNA_cachefile_types.h" -#include "DNA_collection_types.h" -#include "DNA_fileglobal_types.h" -#include "DNA_genfile.h" -#include "DNA_key_types.h" -#include "DNA_layer_types.h" -#include "DNA_node_types.h" -#include "DNA_packedFile_types.h" -#include "DNA_sdna_types.h" -#include "DNA_sound_types.h" -#include "DNA_vfont_types.h" -#include "DNA_volume_types.h" -#include "DNA_workspace_types.h" - -#include "MEM_guardedalloc.h" - -#include "BLI_blenlib.h" -#include "BLI_endian_defines.h" -#include "BLI_endian_switch.h" -#include "BLI_ghash.h" -#include "BLI_linklist.h" -#include "BLI_math.h" -#include "BLI_memarena.h" -#include "BLI_mempool.h" -#include "BLI_threads.h" - -#include "PIL_time.h" - -#include "BLT_translation.h" - -#include "BKE_anim_data.h" -#include "BKE_animsys.h" -#include "BKE_asset.h" -#include "BKE_collection.h" -#include "BKE_global.h" /* for G */ -#include "BKE_idprop.h" -#include "BKE_idtype.h" -#include "BKE_layer.h" -#include "BKE_lib_id.h" -#include "BKE_lib_override.h" -#include "BKE_lib_query.h" -#include "BKE_main.h" /* for Main */ -#include "BKE_main_idmap.h" -#include "BKE_material.h" -#include "BKE_modifier.h" -#include "BKE_node.h" /* for tree type defines */ -#include "BKE_object.h" -#include "BKE_packedFile.h" -#include "BKE_report.h" -#include "BKE_scene.h" -#include "BKE_screen.h" -#include "BKE_undo_system.h" -#include "BKE_workspace.h" - -#include "DRW_engine.h" - -#include "DEG_depsgraph.h" - -#include "BLO_blend_defs.h" -#include "BLO_blend_validate.h" -#include "BLO_read_write.h" -#include "BLO_readfile.h" -#include "BLO_undofile.h" - -#include "SEQ_clipboard.h" -#include "SEQ_iterator.h" -#include "SEQ_modifier.h" -#include "SEQ_sequencer.h" -#include "SEQ_utils.h" - -#include "readfile.h" - -#include - -/* Make preferences read-only. */ -#define U (*((const UserDef *)&U)) - -/** - * READ - * ==== - * - * - Existing Library (#Main) push or free - * - allocate new #Main - * - load file - * - read #SDNA - * - for each LibBlock - * - read LibBlock - * - if a Library - * - make a new #Main - * - attach ID's to it - * - else - * - read associated 'direct data' - * - link direct data (internal and to LibBlock) - * - read #FileGlobal - * - read #USER data, only when indicated (file is `~/.config/blender/X.XX/config/userpref.blend`) - * - free file - * - per Library (per #Main) - * - read file - * - read #SDNA - * - find LibBlocks and attach #ID's to #Main - * - if external LibBlock - * - search all #Main's - * - or it's already read, - * - or not read yet - * - or make new #Main - * - per LibBlock - * - read recursive - * - read associated direct data - * - link direct data (internal and to LibBlock) - * - free file - * - per Library with unread LibBlocks - * - read file - * - read #SDNA - * - per LibBlock - * - read recursive - * - read associated direct data - * - link direct data (internal and to LibBlock) - * - free file - * - join all #Main's - * - link all LibBlocks and indirect pointers to libblocks - * - initialize #FileGlobal and copy pointers to #Global - * - * \note Still a weak point is the new-address function, that doesn't solve reading from - * multiple files at the same time. - * (added remark: oh, i thought that was solved? will look at that... (ton). - */ - -/** - * Delay reading blocks we might not use (especially applies to library linking). - * which keeps large arrays in memory from data-blocks we may not even use. - * - * \note This is disabled when using compression, - * while ZLIB supports seek it's unusably slow, see: T61880. - */ -#define USE_BHEAD_READ_ON_DEMAND - -/** Use #GHash for #BHead name-based lookups (speeds up linking). */ -#define USE_GHASH_BHEAD - -/** Use #GHash for restoring pointers by name. */ -#define USE_GHASH_RESTORE_POINTER - -static CLG_LogRef LOG = {"blo.readfile"}; -static CLG_LogRef LOG_UNDO = {"blo.readfile.undo"}; - -/* local prototypes */ -static void read_libraries(FileData *basefd, ListBase *mainlist); -static void *read_struct(FileData *fd, BHead *bh, const char *blockname); -static BHead *find_bhead_from_code_name(FileData *fd, const short idcode, const char *name); -static BHead *find_bhead_from_idname(FileData *fd, const char *idname); - -typedef struct BHeadN { - struct BHeadN *next, *prev; -#ifdef USE_BHEAD_READ_ON_DEMAND - /** Use to read the data from the file directly into memory as needed. */ - off64_t file_offset; - /** When set, the remainder of this allocation is the data, otherwise it needs to be read. */ - bool has_data; -#endif - bool is_memchunk_identical; - struct BHead bhead; -} BHeadN; - -#define BHEADN_FROM_BHEAD(bh) ((BHeadN *)POINTER_OFFSET(bh, -(int)offsetof(BHeadN, bhead))) - -/** - * We could change this in the future, for now it's simplest if only data is delayed - * because ID names are used in lookup tables. - */ -#define BHEAD_USE_READ_ON_DEMAND(bhead) ((bhead)->code == DATA) - -void BLO_reportf_wrap(BlendFileReadReport *reports, eReportType type, const char *format, ...) -{ - char fixed_buf[1024]; /* should be long enough */ - - va_list args; - - va_start(args, format); - vsnprintf(fixed_buf, sizeof(fixed_buf), format, args); - va_end(args); - - fixed_buf[sizeof(fixed_buf) - 1] = '\0'; - - BKE_report(reports->reports, type, fixed_buf); - - if (G.background == 0) { - printf("%s: %s\n", BKE_report_type_str(type), fixed_buf); - } -} - -/* for reporting linking messages */ -static const char *library_parent_filepath(Library *lib) -{ - return lib->parent ? lib->parent->filepath_abs : ""; -} - -/* -------------------------------------------------------------------- */ -/** \name OldNewMap API - * \{ */ - -typedef struct OldNew { - const void *oldp; - void *newp; - /* `nr` is "user count" for data, and ID code for libdata. */ - int nr; -} OldNew; - -typedef struct OldNewMap { - /* Array that stores the actual entries. */ - OldNew *entries; - int nentries; - /* Hash-map that stores indices into the `entries` array. */ - int32_t *map; - - int capacity_exp; -} OldNewMap; - -#define ENTRIES_CAPACITY(onm) (1ll << (onm)->capacity_exp) -#define MAP_CAPACITY(onm) (1ll << ((onm)->capacity_exp + 1)) -#define SLOT_MASK(onm) (MAP_CAPACITY(onm) - 1) -#define DEFAULT_SIZE_EXP 6 -#define PERTURB_SHIFT 5 - -/* based on the probing algorithm used in Python dicts. */ -#define ITER_SLOTS(onm, KEY, SLOT_NAME, INDEX_NAME) \ - uint32_t hash = BLI_ghashutil_ptrhash(KEY); \ - uint32_t mask = SLOT_MASK(onm); \ - uint perturb = hash; \ - int SLOT_NAME = mask & hash; \ - int INDEX_NAME = onm->map[SLOT_NAME]; \ - for (;; SLOT_NAME = mask & ((5 * SLOT_NAME) + 1 + perturb), \ - perturb >>= PERTURB_SHIFT, \ - INDEX_NAME = onm->map[SLOT_NAME]) - -static void oldnewmap_insert_index_in_map(OldNewMap *onm, const void *ptr, int index) -{ - ITER_SLOTS (onm, ptr, slot, stored_index) { - if (stored_index == -1) { - onm->map[slot] = index; - break; - } - } -} - -static void oldnewmap_insert_or_replace(OldNewMap *onm, OldNew entry) -{ - ITER_SLOTS (onm, entry.oldp, slot, index) { - if (index == -1) { - onm->entries[onm->nentries] = entry; - onm->map[slot] = onm->nentries; - onm->nentries++; - break; - } - if (onm->entries[index].oldp == entry.oldp) { - onm->entries[index] = entry; - break; - } - } -} - -static OldNew *oldnewmap_lookup_entry(const OldNewMap *onm, const void *addr) -{ - ITER_SLOTS (onm, addr, slot, index) { - if (index >= 0) { - OldNew *entry = &onm->entries[index]; - if (entry->oldp == addr) { - return entry; - } - } - else { - return NULL; - } - } -} - -static void oldnewmap_clear_map(OldNewMap *onm) -{ - memset(onm->map, 0xFF, MAP_CAPACITY(onm) * sizeof(*onm->map)); -} - -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)); - oldnewmap_clear_map(onm); - for (int i = 0; i < onm->nentries; i++) { - oldnewmap_insert_index_in_map(onm, onm->entries[i].oldp, i); - } -} - -/* Public OldNewMap API */ - -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"); - oldnewmap_clear_map(onm); -} - -static OldNewMap *oldnewmap_new(void) -{ - OldNewMap *onm = MEM_mallocN(sizeof(*onm), "OldNewMap"); - - oldnewmap_init_data(onm, DEFAULT_SIZE_EXP); - - return onm; -} - -static void oldnewmap_insert(OldNewMap *onm, const void *oldaddr, void *newaddr, int nr) -{ - if (oldaddr == NULL || newaddr == NULL) { - return; - } - - if (UNLIKELY(onm->nentries == ENTRIES_CAPACITY(onm))) { - oldnewmap_increase_size(onm); - } - - OldNew entry; - entry.oldp = oldaddr; - entry.newp = newaddr; - entry.nr = nr; - oldnewmap_insert_or_replace(onm, entry); -} - -void blo_do_versions_oldnewmap_insert(OldNewMap *onm, const void *oldaddr, void *newaddr, int nr) -{ - oldnewmap_insert(onm, oldaddr, newaddr, nr); -} - -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 (increase_users) { - entry->nr++; - } - return entry->newp; -} - -/* 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; - } - - ID *id = oldnewmap_lookup_and_inc(onm, addr, false); - if (id == NULL) { - return NULL; - } - if (!lib || id->lib) { - return id; - } - return NULL; -} - -static void oldnewmap_clear(OldNewMap *onm) -{ - /* Free unused data. */ - for (int i = 0; i < onm->nentries; i++) { - OldNew *entry = &onm->entries[i]; - if (entry->nr == 0) { - MEM_freeN(entry->newp); - entry->newp = NULL; - } - } - - MEM_freeN(onm->entries); - MEM_freeN(onm->map); - - oldnewmap_init_data(onm, DEFAULT_SIZE_EXP); -} - -static void oldnewmap_free(OldNewMap *onm) -{ - MEM_freeN(onm->entries); - MEM_freeN(onm->map); - MEM_freeN(onm); -} - -#undef ENTRIES_CAPACITY -#undef MAP_CAPACITY -#undef SLOT_MASK -#undef DEFAULT_SIZE_EXP -#undef PERTURB_SHIFT -#undef ITER_SLOTS - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Helper Functions - * \{ */ - -static void add_main_to_main(Main *mainvar, Main *from) -{ - ListBase *lbarray[INDEX_ID_MAX], *fromarray[INDEX_ID_MAX]; - int a; - - set_listbasepointers(mainvar, lbarray); - a = set_listbasepointers(from, fromarray); - while (a--) { - BLI_movelisttolist(lbarray[a], fromarray[a]); - } -} - -void blo_join_main(ListBase *mainlist) -{ - Main *tojoin, *mainl; - - mainl = mainlist->first; - - if (mainl->id_map != NULL) { - /* Cannot keep this since we add some IDs from joined mains. */ - BKE_main_idmap_destroy(mainl->id_map); - mainl->id_map = NULL; - } - - while ((tojoin = mainl->next)) { - add_main_to_main(mainl, tojoin); - BLI_remlink(mainlist, tojoin); - BKE_main_free(tojoin); - } -} - -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; - - if (id->lib) { - if (((uint)id->lib->temp_index < lib_main_array_len) && - /* this check should never fail, just in case 'id->lib' is a dangling pointer. */ - (lib_main_array[id->lib->temp_index]->curlib == id->lib)) { - Main *mainvar = lib_main_array[id->lib->temp_index]; - ListBase *lb_dst = which_libbase(mainvar, GS(id->name)); - BLI_remlink(lb_src, id); - BLI_addtail(lb_dst, id); - } - else { - CLOG_ERROR(&LOG, "Invalid library for '%s'", id->name); - } - } - } -} - -void blo_split_main(ListBase *mainlist, Main *main) -{ - mainlist->first = mainlist->last = main; - main->next = NULL; - - if (BLI_listbase_is_empty(&main->libraries)) { - return; - } - - if (main->id_map != NULL) { - /* Cannot keep this since we remove some IDs from given main. */ - BKE_main_idmap_destroy(main->id_map); - main->id_map = NULL; - } - - /* (Library.temp_index -> Main), lookup table */ - const uint lib_main_array_len = BLI_listbase_count(&main->libraries); - Main **lib_main_array = MEM_malloc_arrayN(lib_main_array_len, sizeof(*lib_main_array), __func__); - - int i = 0; - for (Library *lib = main->libraries.first; lib; lib = lib->id.next, i++) { - Main *libmain = BKE_main_new(); - libmain->curlib = lib; - libmain->versionfile = lib->versionfile; - libmain->subversionfile = lib->subversionfile; - BLI_addtail(mainlist, libmain); - lib->temp_index = i; - lib_main_array[i] = libmain; - } - - 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. */ - continue; - } - split_libdata(lbarray[i], lib_main_array, lib_main_array_len); - } - - MEM_freeN(lib_main_array); -} - -static void read_file_version(FileData *fd, Main *main) -{ - BHead *bhead; - - for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) { - if (bhead->code == GLOB) { - FileGlobal *fg = read_struct(fd, bhead, "Global"); - if (fg) { - main->subversionfile = fg->subversion; - main->minversionfile = fg->minversion; - main->minsubversionfile = fg->minsubversion; - MEM_freeN(fg); - } - else if (bhead->code == ENDB) { - break; - } - } - } - if (main->curlib) { - main->curlib->versionfile = main->versionfile; - main->curlib->subversionfile = main->subversionfile; - } -} - -static bool blo_bhead_is_id(const BHead *bhead) -{ - /* BHead codes are four bytes (like 'ENDB', 'TEST', etc.), but if the two most-significant bytes - * are zero, the values actually indicate an ID type. */ - return bhead->code <= 0xFFFF; -} - -static bool blo_bhead_is_id_valid_type(const BHead *bhead) -{ - if (!blo_bhead_is_id(bhead)) { - return false; - } - - const short id_type_code = bhead->code & 0xFFFF; - return BKE_idtype_idcode_is_valid(id_type_code); -} - -#ifdef USE_GHASH_BHEAD -static void read_file_bhead_idname_map_create(FileData *fd) -{ - BHead *bhead; - - /* dummy values */ - bool is_link = false; - int code_prev = ENDB; - uint reserve = 0; - - for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) { - if (code_prev != bhead->code) { - code_prev = bhead->code; - is_link = blo_bhead_is_id_valid_type(bhead) ? - BKE_idtype_idcode_is_linkable((short)code_prev) : - false; - } - - if (is_link) { - reserve += 1; - } - } - - BLI_assert(fd->bhead_idname_hash == NULL); - - fd->bhead_idname_hash = BLI_ghash_str_new_ex(__func__, reserve); - - for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) { - if (code_prev != bhead->code) { - code_prev = bhead->code; - is_link = blo_bhead_is_id_valid_type(bhead) ? - BKE_idtype_idcode_is_linkable((short)code_prev) : - false; - } - - if (is_link) { - BLI_ghash_insert(fd->bhead_idname_hash, (void *)blo_bhead_id_name(fd, bhead), bhead); - } - } -} -#endif - -static Main *blo_find_main(FileData *fd, const char *filepath, const char *relabase) -{ - ListBase *mainlist = fd->mainlist; - Main *m; - Library *lib; - char name1[FILE_MAX]; - - BLI_strncpy(name1, filepath, sizeof(name1)); - BLI_path_normalize(relabase, name1); - - // printf("blo_find_main: relabase %s\n", relabase); - // 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) { - const char *libname = (m->curlib) ? m->curlib->filepath_abs : m->filepath; - - if (BLI_path_cmp(name1, libname) == 0) { - if (G.debug & G_DEBUG) { - CLOG_INFO(&LOG, 3, "Found library %s", libname); - } - return m; - } - } - - m = BKE_main_new(); - BLI_addtail(mainlist, m); - - /* 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); - - /* Important, consistency with main ID reading code from read_libblock(). */ - lib->id.us = ID_FAKE_USERS(lib); - - /* Matches direct_link_library(). */ - id_us_ensure_real(&lib->id); - - BLI_strncpy(lib->filepath, filepath, sizeof(lib->filepath)); - BLI_strncpy(lib->filepath_abs, name1, sizeof(lib->filepath_abs)); - - m->curlib = lib; - - read_file_version(fd, m); - - if (G.debug & G_DEBUG) { - CLOG_INFO(&LOG, 3, "Added new lib %s", filepath); - } - return m; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name File Parsing - * \{ */ - -typedef struct BlendDataReader { - FileData *fd; -} BlendDataReader; - -typedef struct BlendLibReader { - FileData *fd; - Main *main; -} BlendLibReader; - -typedef struct BlendExpander { - FileData *fd; - Main *main; -} BlendExpander; - -static void switch_endian_bh4(BHead4 *bhead) -{ - /* the ID_.. codes */ - if ((bhead->code & 0xFFFF) == 0) { - bhead->code >>= 16; - } - - if (bhead->code != ENDB) { - BLI_endian_switch_int32(&bhead->len); - BLI_endian_switch_int32(&bhead->SDNAnr); - BLI_endian_switch_int32(&bhead->nr); - } -} - -static void switch_endian_bh8(BHead8 *bhead) -{ - /* the ID_.. codes */ - if ((bhead->code & 0xFFFF) == 0) { - bhead->code >>= 16; - } - - if (bhead->code != ENDB) { - BLI_endian_switch_int32(&bhead->len); - BLI_endian_switch_int32(&bhead->SDNAnr); - BLI_endian_switch_int32(&bhead->nr); - } -} - -static void bh4_from_bh8(BHead *bhead, BHead8 *bhead8, bool do_endian_swap) -{ - BHead4 *bhead4 = (BHead4 *)bhead; - int64_t old; - - bhead4->code = bhead8->code; - bhead4->len = bhead8->len; - - if (bhead4->code != ENDB) { - /* perform a endian swap on 64bit pointers, otherwise the pointer might map to zero - * 0x0000000000000000000012345678 would become 0x12345678000000000000000000000000 - */ - if (do_endian_swap) { - BLI_endian_switch_uint64(&bhead8->old); - } - - /* this patch is to avoid `intptr_t` being read from not-eight aligned positions - * is necessary on any modern 64bit architecture) */ - memcpy(&old, &bhead8->old, 8); - bhead4->old = (int)(old >> 3); - - bhead4->SDNAnr = bhead8->SDNAnr; - bhead4->nr = bhead8->nr; - } -} - -static void bh8_from_bh4(BHead *bhead, BHead4 *bhead4) -{ - BHead8 *bhead8 = (BHead8 *)bhead; - - bhead8->code = bhead4->code; - bhead8->len = bhead4->len; - - if (bhead8->code != ENDB) { - bhead8->old = bhead4->old; - bhead8->SDNAnr = bhead4->SDNAnr; - bhead8->nr = bhead4->nr; - } -} - -static BHeadN *get_bhead(FileData *fd) -{ - BHeadN *new_bhead = NULL; - ssize_t readsize; - - if (fd) { - if (!fd->is_eof) { - /* initializing to zero isn't strictly needed but shuts valgrind up - * since uninitialized memory gets compared */ - BHead8 bhead8 = {0}; - BHead4 bhead4 = {0}; - BHead bhead = {0}; - - /* First read the bhead structure. - * Depending on the platform the file was written on this can - * be a big or little endian BHead4 or BHead8 structure. - * - * As usual 'ENDB' (the last *partial* bhead of the file) - * needs some special handling. We don't want to EOF just yet. - */ - if (fd->flags & FD_FLAGS_FILE_POINTSIZE_IS_4) { - bhead4.code = DATA; - readsize = fd->file->read(fd->file, &bhead4, sizeof(bhead4)); - - if (readsize == sizeof(bhead4) || bhead4.code == ENDB) { - if (fd->flags & FD_FLAGS_SWITCH_ENDIAN) { - switch_endian_bh4(&bhead4); - } - - if (fd->flags & FD_FLAGS_POINTSIZE_DIFFERS) { - bh8_from_bh4(&bhead, &bhead4); - } - else { - /* MIN2 is only to quiet '-Warray-bounds' compiler warning. */ - BLI_assert(sizeof(bhead) == sizeof(bhead4)); - memcpy(&bhead, &bhead4, MIN2(sizeof(bhead), sizeof(bhead4))); - } - } - else { - fd->is_eof = true; - bhead.len = 0; - } - } - else { - bhead8.code = DATA; - readsize = fd->file->read(fd->file, &bhead8, sizeof(bhead8)); - - if (readsize == sizeof(bhead8) || bhead8.code == ENDB) { - if (fd->flags & FD_FLAGS_SWITCH_ENDIAN) { - switch_endian_bh8(&bhead8); - } - - if (fd->flags & FD_FLAGS_POINTSIZE_DIFFERS) { - bh4_from_bh8(&bhead, &bhead8, (fd->flags & FD_FLAGS_SWITCH_ENDIAN) != 0); - } - else { - /* MIN2 is only to quiet '-Warray-bounds' compiler warning. */ - BLI_assert(sizeof(bhead) == sizeof(bhead8)); - memcpy(&bhead, &bhead8, MIN2(sizeof(bhead), sizeof(bhead8))); - } - } - else { - fd->is_eof = true; - bhead.len = 0; - } - } - - /* make sure people are not trying to pass bad blend files */ - if (bhead.len < 0) { - fd->is_eof = true; - } - - /* bhead now contains the (converted) bhead structure. Now read - * the associated data and put everything in a BHeadN (creative naming !) - */ - if (fd->is_eof) { - /* pass */ - } -#ifdef USE_BHEAD_READ_ON_DEMAND - else if (fd->file->seek != NULL && BHEAD_USE_READ_ON_DEMAND(&bhead)) { - /* Delay reading bhead content. */ - new_bhead = MEM_mallocN(sizeof(BHeadN), "new_bhead"); - if (new_bhead) { - new_bhead->next = new_bhead->prev = NULL; - new_bhead->file_offset = fd->file->offset; - new_bhead->has_data = false; - new_bhead->is_memchunk_identical = false; - new_bhead->bhead = bhead; - off64_t seek_new = fd->file->seek(fd->file, bhead.len, SEEK_CUR); - if (seek_new == -1) { - fd->is_eof = true; - MEM_freeN(new_bhead); - new_bhead = NULL; - } - BLI_assert(fd->file->offset == seek_new); - } - else { - fd->is_eof = true; - } - } -#endif - else { - new_bhead = MEM_mallocN(sizeof(BHeadN) + (size_t)bhead.len, "new_bhead"); - if (new_bhead) { - new_bhead->next = new_bhead->prev = NULL; -#ifdef USE_BHEAD_READ_ON_DEMAND - new_bhead->file_offset = 0; /* don't seek. */ - new_bhead->has_data = true; -#endif - new_bhead->is_memchunk_identical = false; - new_bhead->bhead = bhead; - - readsize = fd->file->read(fd->file, new_bhead + 1, (size_t)bhead.len); - - if (readsize != bhead.len) { - fd->is_eof = true; - MEM_freeN(new_bhead); - new_bhead = NULL; - } - - if (fd->flags & FD_FLAGS_IS_MEMFILE) { - new_bhead->is_memchunk_identical = ((UndoReader *)fd->file)->memchunk_identical; - } - } - else { - fd->is_eof = true; - } - } - } - } - - /* We've read a new block. Now add it to the list - * of blocks. - */ - if (new_bhead) { - BLI_addtail(&fd->bhead_list, new_bhead); - } - - return new_bhead; -} - -BHead *blo_bhead_first(FileData *fd) -{ - BHeadN *new_bhead; - BHead *bhead = NULL; - - /* Rewind the file - * Read in a new block if necessary - */ - new_bhead = fd->bhead_list.first; - if (new_bhead == NULL) { - new_bhead = get_bhead(fd); - } - - if (new_bhead) { - bhead = &new_bhead->bhead; - } - - return bhead; -} - -BHead *blo_bhead_prev(FileData *UNUSED(fd), BHead *thisblock) -{ - BHeadN *bheadn = BHEADN_FROM_BHEAD(thisblock); - BHeadN *prev = bheadn->prev; - - return (prev) ? &prev->bhead : NULL; -} - -BHead *blo_bhead_next(FileData *fd, BHead *thisblock) -{ - BHeadN *new_bhead = NULL; - BHead *bhead = NULL; - - if (thisblock) { - /* bhead is actually a sub part of BHeadN - * We calculate the BHeadN pointer from the BHead pointer below */ - new_bhead = BHEADN_FROM_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) { - new_bhead = get_bhead(fd); - } - } - - if (new_bhead) { - /* here we do the reverse: - * go from the BHeadN pointer to the BHead pointer */ - bhead = &new_bhead->bhead; - } - - return bhead; -} - -#ifdef USE_BHEAD_READ_ON_DEMAND -static bool blo_bhead_read_data(FileData *fd, BHead *thisblock, void *buf) -{ - bool success = true; - BHeadN *new_bhead = BHEADN_FROM_BHEAD(thisblock); - BLI_assert(new_bhead->has_data == false && new_bhead->file_offset != 0); - off64_t offset_backup = fd->file->offset; - if (UNLIKELY(fd->file->seek(fd->file, new_bhead->file_offset, SEEK_SET) == -1)) { - success = false; - } - else { - if (fd->file->read(fd->file, buf, (size_t)new_bhead->bhead.len) != new_bhead->bhead.len) { - success = false; - } - if (fd->flags & FD_FLAGS_IS_MEMFILE) { - new_bhead->is_memchunk_identical = ((UndoReader *)fd->file)->memchunk_identical; - } - } - if (fd->file->seek(fd->file, offset_backup, SEEK_SET) == -1) { - success = false; - } - return success; -} - -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"); - 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 &new_bhead_data->bhead; -} -#endif /* USE_BHEAD_READ_ON_DEMAND */ - -const char *blo_bhead_id_name(const FileData *fd, const BHead *bhead) -{ - return (const char *)POINTER_OFFSET(bhead, sizeof(*bhead) + fd->id_name_offset); -} - -AssetMetaData *blo_bhead_id_asset_data_address(const FileData *fd, const BHead *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; -} - -static void decode_blender_header(FileData *fd) -{ - char header[SIZEOFBLENDERHEADER], num[4]; - ssize_t readsize; - - /* read in the header data */ - readsize = fd->file->read(fd->file, header, sizeof(header)); - - if (readsize == sizeof(header) && STREQLEN(header, "BLENDER", 7) && ELEM(header[7], '_', '-') && - ELEM(header[8], 'v', 'V') && - (isdigit(header[9]) && isdigit(header[10]) && isdigit(header[11]))) { - fd->flags |= FD_FLAGS_FILE_OK; - - /* what size are pointers in the file ? */ - if (header[7] == '_') { - fd->flags |= FD_FLAGS_FILE_POINTSIZE_IS_4; - if (sizeof(void *) != 4) { - fd->flags |= FD_FLAGS_POINTSIZE_DIFFERS; - } - } - else { - if (sizeof(void *) != 8) { - fd->flags |= FD_FLAGS_POINTSIZE_DIFFERS; - } - } - - /* is the file saved in a different endian - * than we need ? - */ - if (((header[8] == 'v') ? L_ENDIAN : B_ENDIAN) != ENDIAN_ORDER) { - fd->flags |= FD_FLAGS_SWITCH_ENDIAN; - } - - /* get the version number */ - memcpy(num, header + 9, 3); - num[3] = 0; - fd->fileversion = atoi(num); - } -} - -/** - * \return Success if the file is read correctly, else set \a r_error_message. - */ -static bool read_file_dna(FileData *fd, const char **r_error_message) -{ - BHead *bhead; - int subversion = 0; - - for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) { - if (bhead->code == GLOB) { - /* Before this, the subversion didn't exist in 'FileGlobal' so the subversion - * value isn't accessible for the purpose of DNA versioning in this case. */ - if (fd->fileversion <= 242) { - continue; - } - /* 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]; - BLI_STATIC_ASSERT(offsetof(FileGlobal, subvstr) == 0, "Must be first: subvstr") - char num[5]; - memcpy(num, fg->subvstr, 4); - num[4] = 0; - subversion = atoi(num); - } - else if (bhead->code == DNA1) { - const bool do_endian_swap = (fd->flags & FD_FLAGS_SWITCH_ENDIAN) != 0; - - fd->filesdna = DNA_sdna_from_data( - &bhead[1], bhead->len, do_endian_swap, true, r_error_message); - if (fd->filesdna) { - blo_do_versions_dna(fd->filesdna, fd->fileversion, subversion); - fd->compflags = DNA_struct_get_compareflags(fd->filesdna, fd->memsdna); - fd->reconstruct_info = DNA_reconstruct_info_create( - fd->filesdna, fd->memsdna, fd->compflags); - /* used to retrieve ID names from (bhead+1) */ - fd->id_name_offset = DNA_elem_offset(fd->filesdna, "ID", "char", "name[]"); - BLI_assert(fd->id_name_offset != -1); - fd->id_asset_data_offset = DNA_elem_offset( - fd->filesdna, "ID", "AssetMetaData", "*asset_data"); - - return true; - } - - return false; - } - else if (bhead->code == ENDB) { - break; - } - } - - *r_error_message = "Missing DNA block"; - return false; -} - -static int *read_file_thumbnail(FileData *fd) -{ - BHead *bhead; - int *blend_thumb = NULL; - - for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) { - if (bhead->code == TEST) { - const bool do_endian_swap = (fd->flags & FD_FLAGS_SWITCH_ENDIAN) != 0; - int *data = (int *)(bhead + 1); - - if (bhead->len < (sizeof(int[2]))) { - break; - } - - if (do_endian_swap) { - BLI_endian_switch_int32(&data[0]); - BLI_endian_switch_int32(&data[1]); - } - - const int width = data[0]; - const int height = data[1]; - if (!BLEN_THUMB_MEMSIZE_IS_VALID(width, height)) { - break; - } - if (bhead->len < BLEN_THUMB_MEMSIZE_FILE(width, height)) { - break; - } - - blend_thumb = data; - break; - } - if (bhead->code != REND) { - /* Thumbnail is stored in TEST immediately after first REND... */ - break; - } - } - - return blend_thumb; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name File Data API - * \{ */ - -static FileData *filedata_new(BlendFileReadReport *reports) -{ - BLI_assert(reports != NULL); - - FileData *fd = MEM_callocN(sizeof(FileData), "FileData"); - - fd->memsdna = DNA_sdna_current_get(); - - fd->datamap = oldnewmap_new(); - fd->globmap = oldnewmap_new(); - fd->libmap = oldnewmap_new(); - - fd->reports = reports; - - return fd; -} - -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; - 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; - } - } - else { - BKE_reportf( - reports, RPT_ERROR, "Failed to read blend file '%s', not a blend file", fd->relabase); - blo_filedata_free(fd); - fd = NULL; - } - - return fd; -} - -static FileData *blo_filedata_from_file_descriptor(const char *filepath, - BlendFileReadReport *reports, - int filedes) -{ - char header[7]; - FileReader *rawfile = BLI_filereader_new_file(filedes); - FileReader *file = NULL; - - 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)) { - BKE_reportf(reports->reports, - RPT_WARNING, - "Unable to read '%s': %s", - filepath, - errno ? strerror(errno) : TIP_("insufficient content")); - if (rawfile) { - rawfile->close(rawfile); - } - else { - close(filedes); - } - return NULL; - } - - /* Rewind the file after reading the header. */ - rawfile->seek(rawfile, 0, SEEK_SET); - - /* Check if we have a regular file. */ - if (memcmp(header, "BLENDER", sizeof(header)) == 0) { - /* Try opening the file with memory-mapped IO. */ - file = BLI_filereader_new_mmap(filedes); - if (file == NULL) { - /* mmap failed, so just keep using rawfile. */ - file = rawfile; - rawfile = NULL; - } - } - 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`. */ - } - } - 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`. */ - } - } - - /* Clean up `rawfile` if it wasn't taken over. */ - if (rawfile != NULL) { - rawfile->close(rawfile); - } - if (file == NULL) { - BKE_reportf(reports->reports, RPT_WARNING, "Unrecognized file format '%s'", filepath); - return NULL; - } - - FileData *fd = filedata_new(reports); - fd->file = file; - - return fd; -} - -static FileData *blo_filedata_from_file_open(const char *filepath, BlendFileReadReport *reports) -{ - errno = 0; - const int file = BLI_open(filepath, O_BINARY | O_RDONLY, 0); - if (file == -1) { - BKE_reportf(reports->reports, - RPT_WARNING, - "Unable to open '%s': %s", - filepath, - errno ? strerror(errno) : TIP_("unknown error reading file")); - return NULL; - } - return blo_filedata_from_file_descriptor(filepath, reports, file); -} - -FileData *blo_filedata_from_file(const char *filepath, BlendFileReadReport *reports) -{ - FileData *fd = blo_filedata_from_file_open(filepath, reports); - if (fd != NULL) { - /* 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; -} - -/** - * Same as blo_filedata_from_file(), but does not reads DNA data, only header. - * Use it for light access (e.g. thumbnail reading). - */ -static FileData *blo_filedata_from_file_minimal(const char *filepath) -{ - FileData *fd = blo_filedata_from_file_open(filepath, &(BlendFileReadReport){.reports = NULL}); - if (fd != NULL) { - decode_blender_header(fd); - if (fd->flags & FD_FLAGS_FILE_OK) { - return fd; - } - blo_filedata_free(fd); - } - return NULL; -} - -FileData *blo_filedata_from_memory(const void *mem, int memsize, BlendFileReadReport *reports) -{ - if (!mem || memsize < SIZEOFBLENDERHEADER) { - BKE_report( - reports->reports, RPT_WARNING, (mem) ? TIP_("Unable to read") : TIP_("Unable to open")); - return NULL; - } - - FileReader *mem_file = BLI_filereader_new_memory(mem, memsize); - FileReader *file = mem_file; - - if (BLI_file_magic_is_gzip(mem)) { - file = BLI_filereader_new_gzip(mem_file); - } - else if (BLI_file_magic_is_zstd(mem)) { - file = BLI_filereader_new_zstd(mem_file); - } - - if (file == NULL) { - /* Compression initialization failed. */ - mem_file->close(mem_file); - return NULL; - } - - FileData *fd = filedata_new(reports); - fd->file = file; - - return blo_decode_and_check(fd, reports->reports); -} - -FileData *blo_filedata_from_memfile(MemFile *memfile, - const struct BlendFileReadParams *params, - BlendFileReadReport *reports) -{ - if (!memfile) { - BKE_report(reports->reports, RPT_WARNING, "Unable to open blend "); - return NULL; - } - - FileData *fd = filedata_new(reports); - fd->file = BLO_memfile_new_filereader(memfile, params->undo_direction); - fd->undo_direction = params->undo_direction; - fd->flags |= FD_FLAGS_IS_MEMFILE; - - return blo_decode_and_check(fd, reports->reports); -} - -void blo_filedata_free(FileData *fd) -{ - if (fd) { - - /* Free all BHeadN data blocks */ -#ifndef NDEBUG - BLI_freelistN(&fd->bhead_list); -#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)) { - BLI_assert(new_bhead->has_data == 0); - } - MEM_freeN(new_bhead); - } -#endif - fd->file->close(fd->file); - - if (fd->filesdna) { - DNA_sdna_free(fd->filesdna); - } - if (fd->compflags) { - MEM_freeN((void *)fd->compflags); - } - if (fd->reconstruct_info) { - DNA_reconstruct_info_free(fd->reconstruct_info); - } - - if (fd->datamap) { - oldnewmap_free(fd->datamap); - } - if (fd->globmap) { - oldnewmap_free(fd->globmap); - } - if (fd->packedmap) { - oldnewmap_free(fd->packedmap); - } - if (fd->libmap && !(fd->flags & FD_FLAGS_NOT_MY_LIBMAP)) { - oldnewmap_free(fd->libmap); - } - if (fd->old_idmap != NULL) { - BKE_main_idmap_destroy(fd->old_idmap); - } - blo_cache_storage_end(fd); - if (fd->bheadmap) { - MEM_freeN(fd->bheadmap); - } - -#ifdef USE_GHASH_BHEAD - if (fd->bhead_idname_hash) { - BLI_ghash_free(fd->bhead_idname_hash, NULL, NULL); - } -#endif - - MEM_freeN(fd); - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Public Utilities - * \{ */ - -bool BLO_has_bfile_extension(const char *str) -{ - const char *ext_test[4] = {".blend", ".ble", ".blend.gz", NULL}; - return BLI_path_extension_check_array(str, ext_test); -} - -bool BLO_library_path_explode(const char *path, char *r_dir, char **r_group, char **r_name) -{ - /* 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'; - - r_dir[0] = '\0'; - if (r_group) { - *r_group = NULL; - } - if (r_name) { - *r_name = NULL; - } - - /* if path leads to an existing directory, we can be sure we're not (in) a library */ - if (BLI_is_dir(path)) { - return false; - } - - strcpy(r_dir, path); - - while ((slash = (char *)BLI_path_slash_rfind(r_dir))) { - char tc = *slash; - *slash = '\0'; - if (BLO_has_bfile_extension(r_dir) && BLI_is_file(r_dir)) { - break; - } - if (STREQ(r_dir, BLO_EMBEDDED_STARTUP_BLEND)) { - break; - } - - if (prev_slash) { - *prev_slash = c; - } - prev_slash = slash; - c = tc; - } - - if (!slash) { - return false; - } - - if (slash[1] != '\0') { - BLI_assert(strlen(slash + 1) < BLO_GROUP_MAX); - if (r_group) { - *r_group = slash + 1; - } - } - - if (prev_slash && (prev_slash[1] != '\0')) { - BLI_assert(strlen(prev_slash + 1) < MAX_ID_NAME - 2); - if (r_name) { - *r_name = prev_slash + 1; - } - } - - return true; -} - -BlendThumbnail *BLO_thumbnail_from_file(const char *filepath) -{ - FileData *fd; - BlendThumbnail *data = NULL; - int *fd_data; - - fd = blo_filedata_from_file_minimal(filepath); - fd_data = fd ? read_file_thumbnail(fd) : NULL; - - 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__); - if (data) { - BLI_assert((data_size - sizeof(*data)) == - (BLEN_THUMB_MEMSIZE_FILE(width, height) - (sizeof(*fd_data) * 2))); - data->width = width; - data->height = height; - memcpy(data->rect, &fd_data[2], data_size - sizeof(*data)); - } - } - } - - blo_filedata_free(fd); - - return data; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Old/New Pointer Map - * \{ */ - -/* Only direct data-blocks. */ -static void *newdataadr(FileData *fd, const void *adr) -{ - return oldnewmap_lookup_and_inc(fd->datamap, adr, true); -} - -/* Only direct data-blocks. */ -static void *newdataadr_no_us(FileData *fd, const void *adr) -{ - return oldnewmap_lookup_and_inc(fd->datamap, adr, false); -} - -void *blo_read_get_new_globaldata_address(FileData *fd, const void *adr) -{ - return oldnewmap_lookup_and_inc(fd->globmap, adr, true); -} - -/* Used to restore packed data after undo. */ -static void *newpackedadr(FileData *fd, const void *adr) -{ - if (fd->packedmap && adr) { - return oldnewmap_lookup_and_inc(fd->packedmap, adr, true); - } - - return oldnewmap_lookup_and_inc(fd->datamap, adr, true); -} - -/* only lib data */ -static void *newlibadr(FileData *fd, const void *lib, const void *adr) -{ - return oldnewmap_liblookup(fd->libmap, adr, lib); -} - -void *blo_do_versions_newlibadr(FileData *fd, const void *lib, const void *adr) -{ - return newlibadr(fd, lib, adr); -} - -/* increases user number */ -static void change_link_placeholder_to_real_ID_pointer_fd(FileData *fd, const void *old, void *new) -{ - 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); - } - } - } -} - -static void change_link_placeholder_to_real_ID_pointer(ListBase *mainlist, - FileData *basefd, - void *old, - void *new) -{ - LISTBASE_FOREACH (Main *, mainptr, mainlist) { - FileData *fd; - - if (mainptr->curlib) { - fd = mainptr->curlib->filedata; - } - else { - fd = basefd; - } - - if (fd) { - change_link_placeholder_to_real_ID_pointer_fd(fd, old, new); - } - } -} - -/* XXX disabled this feature - packed files also belong in temp saves and quit.blend, - * to make restore work. */ - -static void insert_packedmap(FileData *fd, PackedFile *pf) -{ - oldnewmap_insert(fd->packedmap, pf, pf, 0); - oldnewmap_insert(fd->packedmap, pf->data, pf->data, 0); -} - -void blo_make_packed_pointer_map(FileData *fd, Main *oldmain) -{ - fd->packedmap = oldnewmap_new(); - - LISTBASE_FOREACH (Image *, ima, &oldmain->images) { - if (ima->packedfile) { - insert_packedmap(fd, ima->packedfile); - } - - LISTBASE_FOREACH (ImagePackedFile *, imapf, &ima->packedfiles) { - if (imapf->packedfile) { - insert_packedmap(fd, imapf->packedfile); - } - } - } - - LISTBASE_FOREACH (VFont *, vfont, &oldmain->fonts) { - if (vfont->packedfile) { - insert_packedmap(fd, vfont->packedfile); - } - } - - LISTBASE_FOREACH (bSound *, sound, &oldmain->sounds) { - if (sound->packedfile) { - insert_packedmap(fd, sound->packedfile); - } - } - - LISTBASE_FOREACH (Volume *, volume, &oldmain->volumes) { - if (volume->packedfile) { - insert_packedmap(fd, volume->packedfile); - } - } - - LISTBASE_FOREACH (Library *, lib, &oldmain->libraries) { - if (lib->packedfile) { - insert_packedmap(fd, lib->packedfile); - } - } -} - -void blo_end_packed_pointer_map(FileData *fd, Main *oldmain) -{ - OldNew *entry = fd->packedmap->entries; - - /* 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; - } - } - - LISTBASE_FOREACH (Image *, ima, &oldmain->images) { - ima->packedfile = newpackedadr(fd, ima->packedfile); - - LISTBASE_FOREACH (ImagePackedFile *, imapf, &ima->packedfiles) { - imapf->packedfile = newpackedadr(fd, imapf->packedfile); - } - } - - LISTBASE_FOREACH (VFont *, vfont, &oldmain->fonts) { - vfont->packedfile = newpackedadr(fd, vfont->packedfile); - } - - LISTBASE_FOREACH (bSound *, sound, &oldmain->sounds) { - sound->packedfile = newpackedadr(fd, sound->packedfile); - } - - LISTBASE_FOREACH (Library *, lib, &oldmain->libraries) { - lib->packedfile = newpackedadr(fd, lib->packedfile); - } - - LISTBASE_FOREACH (Volume *, volume, &oldmain->volumes) { - volume->packedfile = newpackedadr(fd, volume->packedfile); - } -} - -void blo_add_library_pointer_map(ListBase *old_mainlist, FileData *fd) -{ - ListBase *lbarray[INDEX_ID_MAX]; - - LISTBASE_FOREACH (Main *, ptr, old_mainlist) { - int i = set_listbasepointers(ptr, lbarray); - while (i--) { - LISTBASE_FOREACH (ID *, id, lbarray[i]) { - oldnewmap_insert(fd->libmap, id, id, GS(id->name)); - } - } - } - - fd->old_mainlist = old_mainlist; -} - -void blo_make_old_idmap_from_main(FileData *fd, Main *bmain) -{ - if (fd->old_idmap != NULL) { - BKE_main_idmap_destroy(fd->old_idmap); - } - fd->old_idmap = BKE_main_idmap_create(bmain, false, NULL, MAIN_IDMAP_TYPE_UUID); -} - -typedef struct BLOCacheStorage { - GHash *cache_map; - MemArena *memarena; -} BLOCacheStorage; - -typedef struct BLOCacheStorageValue { - void *cache_v; - uint new_usage_count; -} BLOCacheStorageValue; - -/** Register a cache data entry to be preserved when reading some undo memfile. */ -static void blo_cache_storage_entry_register( - ID *id, const IDCacheKey *key, void **cache_p, uint UNUSED(flags), void *cache_storage_v) -{ - BLI_assert(key->id_session_uuid == id->session_uuid); - UNUSED_VARS_NDEBUG(id); - - BLOCacheStorage *cache_storage = 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)); - *storage_key = *key; - BLOCacheStorageValue *storage_value = 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); -} - -/** Restore a cache data entry from old ID into new one, when reading some undo memfile. */ -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; - - if (cache_storage == NULL) { - /* 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; - } - return; - } - - BLOCacheStorageValue *storage_value = BLI_ghash_lookup(cache_storage->cache_map, key); - if (storage_value == NULL) { - *cache_p = NULL; - return; - } - storage_value->new_usage_count++; - *cache_p = storage_value->cache_v; -} - -/** Clear as needed a cache data entry from old ID, when reading some undo memfile. */ -static void blo_cache_storage_entry_clear_in_old(ID *UNUSED(id), - const IDCacheKey *key, - void **cache_p, - uint UNUSED(flags), - void *cache_storage_v) -{ - BLOCacheStorage *cache_storage = cache_storage_v; - - BLOCacheStorageValue *storage_value = BLI_ghash_lookup(cache_storage->cache_map, key); - if (storage_value == NULL) { - *cache_p = NULL; - 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; - } - else { - BLI_assert(*cache_p == storage_value->cache_v); - } -} - -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__); - 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) { - continue; - } - - const IDTypeInfo *type_info = BKE_idtype_get_info_from_id(id); - if (type_info->foreach_cache == NULL) { - continue; - } - - FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id) { - if (ID_IS_LINKED(id)) { - continue; - } - BKE_idtype_id_foreach_cache(id, blo_cache_storage_entry_register, fd->cache_storage); - } - FOREACH_MAIN_LISTBASE_ID_END; - } - FOREACH_MAIN_LISTBASE_END; - } - else { - fd->cache_storage = NULL; - } -} - -void blo_cache_storage_old_bmain_clear(FileData *fd, Main *bmain_old) -{ - if (fd->cache_storage != NULL) { - ListBase *lb; - FOREACH_MAIN_LISTBASE_BEGIN (bmain_old, lb) { - ID *id = lb->first; - if (id == NULL) { - continue; - } - - const IDTypeInfo *type_info = BKE_idtype_get_info_from_id(id); - if (type_info->foreach_cache == NULL) { - continue; - } - - FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id) { - if (ID_IS_LINKED(id)) { - continue; - } - BKE_idtype_id_foreach_cache(id, blo_cache_storage_entry_clear_in_old, fd->cache_storage); - } - FOREACH_MAIN_LISTBASE_ID_END; - } - FOREACH_MAIN_LISTBASE_END; - } -} - -void blo_cache_storage_end(FileData *fd) -{ - if (fd->cache_storage != NULL) { - BLI_ghash_free(fd->cache_storage->cache_map, NULL, NULL); - BLI_memarena_free(fd->cache_storage->memarena); - MEM_freeN(fd->cache_storage); - fd->cache_storage = NULL; - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name DNA Struct Loading - * \{ */ - -static void switch_endian_structs(const struct SDNA *filesdna, BHead *bhead) -{ - int blocksize, nblocks; - char *data; - - data = (char *)(bhead + 1); - blocksize = filesdna->types_size[filesdna->structs[bhead->SDNAnr]->type]; - - nblocks = bhead->nr; - while (nblocks--) { - DNA_struct_switch_endian(filesdna, bhead->SDNAnr, data); - - data += blocksize; - } -} - -static void *read_struct(FileData *fd, BHead *bh, const char *blockname) -{ - void *temp = NULL; - - if (bh->len) { -#ifdef USE_BHEAD_READ_ON_DEMAND - BHead *bh_orig = bh; -#endif - - /* switch is based on file dna */ - if (bh->SDNAnr && (fd->flags & FD_FLAGS_SWITCH_ENDIAN)) { -#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)) { - fd->flags &= ~FD_FLAGS_FILE_OK; - return NULL; - } - } -#endif - switch_endian_structs(fd->filesdna, bh); - } - - if (fd->compflags[bh->SDNAnr] != SDNA_CMP_REMOVED) { - if (fd->compflags[bh->SDNAnr] == SDNA_CMP_NOT_EQUAL) { -#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)) { - fd->flags &= ~FD_FLAGS_FILE_OK; - return NULL; - } - } -#endif - temp = DNA_struct_reconstruct(fd->reconstruct_info, bh->SDNAnr, bh->nr, (bh + 1)); - } - else { - /* SDNA_CMP_EQUAL */ - temp = MEM_mallocN(bh->len, blockname); -#ifdef USE_BHEAD_READ_ON_DEMAND - if (BHEADN_FROM_BHEAD(bh)->has_data) { - memcpy(temp, (bh + 1), bh->len); - } - else { - /* Instead of allocating the bhead, then copying it, - * read the data from the file directly into the memory. */ - if (UNLIKELY(!blo_bhead_read_data(fd, bh, temp))) { - fd->flags &= ~FD_FLAGS_FILE_OK; - MEM_freeN(temp); - temp = NULL; - } - } -#else - memcpy(temp, (bh + 1), bh->len); -#endif - } - } - -#ifdef USE_BHEAD_READ_ON_DEMAND - if (bh_orig != bh) { - MEM_freeN(BHEADN_FROM_BHEAD(bh)); - } -#endif - } - - return temp; -} - -/* Like read_struct, but gets a pointer without allocating. Only works for - * undo since DNA must match. */ -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; -} - -static void link_glob_list(FileData *fd, ListBase *lb) /* for glob data */ -{ - Link *ln, *prev; - void *poin; - - if (BLI_listbase_is_empty(lb)) { - return; - } - poin = newdataadr(fd, lb->first); - if (lb->first) { - oldnewmap_insert(fd->globmap, lb->first, poin, 0); - } - lb->first = poin; - - ln = lb->first; - prev = NULL; - while (ln) { - poin = newdataadr(fd, ln->next); - if (ln->next) { - oldnewmap_insert(fd->globmap, ln->next, poin, 0); - } - ln->next = poin; - ln->prev = prev; - prev = ln; - ln = ln->next; - } - lb->last = prev; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Read ID - * \{ */ - -static void lib_link_id(BlendLibReader *reader, ID *id); - -static void lib_link_id_embedded_id(BlendLibReader *reader, ID *id) -{ - - /* Handle 'private IDs'. */ - bNodeTree *nodetree = ntreeFromID(id); - if (nodetree != NULL) { - lib_link_id(reader, &nodetree->id); - ntreeBlendReadLib(reader, nodetree); - } - - if (GS(id->name) == ID_SCE) { - Scene *scene = (Scene *)id; - if (scene->master_collection != NULL) { - lib_link_id(reader, &scene->master_collection->id); - BKE_collection_blend_read_lib(reader, scene->master_collection); - } - } -} - -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); - - AnimData *adt = BKE_animdata_from_id(id); - if (adt != NULL) { - BKE_animdata_blend_read_lib(reader, id, adt); - } - - if (id->override_library) { - BLO_read_id_address(reader, id->lib, &id->override_library->reference); - BLO_read_id_address(reader, id->lib, &id->override_library->storage); - BLO_read_id_address(reader, id->lib, &id->override_library->hierarchy_root); - } - - lib_link_id_embedded_id(reader, id); -} - -static void direct_link_id_override_property_operation_cb(BlendDataReader *reader, void *data) -{ - IDOverrideLibraryPropertyOperation *opop = data; - - BLO_read_data_address(reader, &opop->subitem_reference_name); - BLO_read_data_address(reader, &opop->subitem_local_name); - - opop->tag = 0; /* Runtime only. */ -} - -static void direct_link_id_override_property_cb(BlendDataReader *reader, void *data) -{ - IDOverrideLibraryProperty *op = data; - - BLO_read_data_address(reader, &op->rna_path); - - op->tag = 0; /* Runtime only. */ - - BLO_read_list_cb(reader, &op->operations, direct_link_id_override_property_operation_cb); -} - -static void direct_link_id_common( - BlendDataReader *reader, Library *current_library, ID *id, ID *id_old, const int tag); - -static void direct_link_id_embedded_id(BlendDataReader *reader, - Library *current_library, - ID *id, - ID *id_old) -{ - /* Handle 'private IDs'. */ - bNodeTree **nodetree = BKE_ntree_ptr_from_id(id); - if (nodetree != NULL && *nodetree != NULL) { - BLO_read_data_address(reader, nodetree); - direct_link_id_common(reader, - current_library, - (ID *)*nodetree, - id_old != NULL ? (ID *)ntreeFromID(id_old) : NULL, - 0); - ntreeBlendReadData(reader, *nodetree); - } - - if (GS(id->name) == ID_SCE) { - Scene *scene = (Scene *)id; - if (scene->master_collection != NULL) { - 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, - 0); - BKE_collection_blend_read_data(reader, scene->master_collection); - } - } -} - -static int direct_link_id_restore_recalc_exceptions(const ID *id_current) -{ - /* Exception for armature objects, where the pose has direct points to the - * armature data-block. */ - if (GS(id_current->name) == ID_OB && ((Object *)id_current)->pose) { - return ID_RECALC_GEOMETRY; - } - - return 0; -} - -static int direct_link_id_restore_recalc(const FileData *fd, - const ID *id_target, - const ID *id_current, - const bool is_identical) -{ - /* These are the evaluations that had not been performed yet at the time the - * target undo state was written. These need to be done again, since they may - * flush back changes to the original datablock. */ - int recalc = id_target->recalc; - - if (id_current == NULL) { - /* 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. */ - recalc |= ID_RECALC_ALL; - } - else { - /* If the contents datablock changed, the depsgraph needs to copy the - * datablock again to ensure it matches the original datablock. */ - if (!is_identical) { - recalc |= ID_RECALC_COPY_ON_WRITE; - } - - /* Special exceptions. */ - recalc |= direct_link_id_restore_recalc_exceptions(id_current); - - /* Evaluations for the current state that have not been performed yet - * by the time we are performing this undo step. */ - recalc |= id_current->recalc; - - /* Tags that were set between the target state and the current state, - * that we need to perform again. */ - if (fd->undo_direction == STEP_UNDO) { - /* Undo: tags from target to the current state. */ - recalc |= id_current->recalc_up_to_undo_push; - } - else { - BLI_assert(fd->undo_direction == STEP_REDO); - /* Redo: tags from current to the target state. */ - recalc |= id_target->recalc_up_to_undo_push; - } - } - - return recalc; -} - -static void direct_link_id_common( - BlendDataReader *reader, Library *current_library, ID *id, ID *id_old, const int tag) -{ - if (!BLO_read_data_is_undo(reader)) { - /* When actually reading a file, we do want to reset/re-generate session uuids. - * In undo case, we want to re-use existing ones. */ - id->session_uuid = MAIN_ID_SESSION_UUID_UNSET; - } - - if ((tag & LIB_TAG_TEMP_MAIN) == 0) { - BKE_lib_libblock_session_uuid_ensure(id); - } - - 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; - - /* Initialize with provided tag. */ - id->tag = tag; - - if (ID_IS_LINKED(id)) { - id->library_weak_reference = NULL; - } - else { - BLO_read_data_address(reader, &id->library_weak_reference); - } - - if (tag & LIB_TAG_ID_LINK_PLACEHOLDER) { - /* For placeholder we only need to set the tag and properly initialize generic ID fields above, - * no further data to read. */ - return; - } - - if (id->asset_data) { - BLO_read_data_address(reader, &id->asset_data); - BKE_asset_metadata_read(reader, id->asset_data); - /* Restore runtime asset type info. */ - const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id); - id->asset_data->local_type_info = id_type->asset_type_info; - } - - /* Link direct data of ID properties. */ - if (id->properties) { - BLO_read_data_address(reader, &id->properties); - /* this case means the data was written incorrectly, it should not happen */ - IDP_BlendDataRead(reader, &id->properties); - } - - id->flag &= ~LIB_INDIRECT_WEAK_LINK; - - /* NOTE: It is important to not clear the recalc flags for undo/redo. - * Preserving recalc flags on redo/undo is the only way to make dependency graph detect - * that animation is to be evaluated on undo/redo. If this is not enforced by the recalc - * flags dependency graph does not do animation update to avoid loss of unkeyed changes., - * which conflicts with undo/redo of changes to animation data itself. - * - * But for regular file load we clear the flag, since the flags might have been changed since - * the version the file has been saved with. */ - if (!BLO_read_data_is_undo(reader)) { - id->recalc = 0; - id->recalc_after_undo_push = 0; - } - else if ((reader->fd->skip_flags & BLO_READ_SKIP_UNDO_OLD_MAIN) == 0) { - id->recalc = direct_link_id_restore_recalc(reader->fd, id, id_old, false); - id->recalc_after_undo_push = 0; - } - - /* Link direct data of overrides. */ - 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) { - BLO_read_list_cb( - reader, &id->override_library->properties, direct_link_id_override_property_cb); - id->override_library->runtime = NULL; - } - } - - DrawDataList *drawdata = DRW_drawdatalist_from_id(id); - if (drawdata) { - BLI_listbase_clear((ListBase *)drawdata); - } - - /* Handle 'private IDs'. */ - direct_link_id_embedded_id(reader, current_library, id, id_old); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Read Animation (legacy for version patching) - * \{ */ - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Read ID: Shape Keys - * \{ */ - -void blo_do_versions_key_uidgen(Key *key) -{ - key->uidgen = 1; - LISTBASE_FOREACH (KeyBlock *, block, &key->block) { - block->uid = key->uidgen++; - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Read ID: Scene - * \{ */ - -#ifdef USE_SETSCENE_CHECK -/** - * A version of #BKE_scene_validate_setscene with special checks for linked libs. - */ -static bool scene_validate_setscene__liblink(Scene *sce, const int totscene) -{ - Scene *sce_iter; - int a; - - if (sce->set == NULL) { - return true; - } - - for (a = 0, sce_iter = sce; sce_iter->set; sce_iter = sce_iter->set, a++) { - /* This runs per library (before each libraries #Main has been joined), - * so we can't step into other libraries since `totscene` is only for this library. - * - * Also, other libraries may not have been linked yet, - * while we could check #LIB_TAG_NEED_LINK the library pointer check is sufficient. */ - if (sce->id.lib != sce_iter->id.lib) { - return true; - } - if (sce_iter->flag & SCE_READFILE_LIBLINK_NEED_SETSCENE_CHECK) { - return true; - } - - if (a > totscene) { - sce->set = NULL; - return false; - } - } - - return true; -} -#endif - -static void lib_link_scenes_check_set(Main *bmain) -{ -#ifdef USE_SETSCENE_CHECK - const int totscene = BLI_listbase_count(&bmain->scenes); - LISTBASE_FOREACH (Scene *, sce, &bmain->scenes) { - if (sce->flag & SCE_READFILE_LIBLINK_NEED_SETSCENE_CHECK) { - sce->flag &= ~SCE_READFILE_LIBLINK_NEED_SETSCENE_CHECK; - if (!scene_validate_setscene__liblink(sce, totscene)) { - CLOG_WARN(&LOG, "Found cyclic background scene when linking %s", sce->id.name + 2); - } - } - } -#else - UNUSED_VARS(bmain, totscene); -#endif -} - -#undef USE_SETSCENE_CHECK - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Read ID: Screen - * \{ */ - -/* how to handle user count on pointer restore */ -typedef enum ePointerUserMode { - USER_IGNORE = 0, /* ignore user count */ - USER_REAL = 1, /* ensure at least one real user (fake user ignored) */ -} ePointerUserMode; - -static void restore_pointer_user(ID *id, ID *newid, ePointerUserMode user) -{ - BLI_assert(STREQ(newid->name + 2, id->name + 2)); - BLI_assert(newid->lib == id->lib); - UNUSED_VARS_NDEBUG(id); - - if (user == USER_REAL) { - id_us_ensure_real(newid); - } -} - -#ifndef USE_GHASH_RESTORE_POINTER -/** - * A version of #restore_pointer_by_name that performs a full search (slow!). - * Use only for limited lookups, when the overhead of - * creating a #IDNameLib_Map for a single lookup isn't worthwhile. - */ -static void *restore_pointer_by_name_main(Main *mainp, ID *id, ePointerUserMode user) -{ - if (id) { - ListBase *lb = which_libbase(mainp, GS(id->name)); - if (lb) { /* there's still risk of checking corrupt mem (freed Ids in oops) */ - ID *idn = lb->first; - for (; idn; idn = idn->next) { - if (STREQ(idn->name + 2, id->name + 2)) { - if (idn->lib == id->lib) { - restore_pointer_user(id, idn, user); - break; - } - } - } - return idn; - } - } - return NULL; -} -#endif - -/** - * Only for undo files, or to restore a screen after reading without UI... - * - * \param user: - * - USER_IGNORE: no user-count change. - * - USER_REAL: ensure a real user (even if a fake one is set). - * \param id_map: lookup table, use when performing many lookups. - * this could be made an optional argument (falling back to a full lookup), - * however at the moment it's always available. - */ -static void *restore_pointer_by_name(struct IDNameLib_Map *id_map, ID *id, ePointerUserMode user) -{ -#ifdef USE_GHASH_RESTORE_POINTER - if (id) { - /* use fast lookup when available */ - ID *idn = BKE_main_idmap_lookup_id(id_map, id); - if (idn) { - restore_pointer_user(id, idn, user); - } - return idn; - } - return NULL; -#else - Main *mainp = BKE_main_idmap_main_get(id_map); - return restore_pointer_by_name_main(mainp, id, user); -#endif -} - -static void lib_link_seq_clipboard_pt_restore(ID *id, struct IDNameLib_Map *id_map) -{ - if (id) { - /* clipboard must ensure this */ - BLI_assert(id->newid != NULL); - id->newid = 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; - - lib_link_seq_clipboard_pt_restore((ID *)seq->scene, id_map); - lib_link_seq_clipboard_pt_restore((ID *)seq->scene_camera, id_map); - lib_link_seq_clipboard_pt_restore((ID *)seq->clip, id_map); - lib_link_seq_clipboard_pt_restore((ID *)seq->mask, id_map); - lib_link_seq_clipboard_pt_restore((ID *)seq->sound, id_map); - return true; -} - -static void lib_link_clipboard_restore(struct IDNameLib_Map *id_map) -{ - /* update IDs stored in sequencer clipboard */ - SEQ_for_each_callback(&seqbase_clipboard, lib_link_seq_clipboard_cb, id_map); -} - -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) { - return IDWALK_RET_NOP; - } - - /* Special ugly case here, thanks again for those non-IDs IDs... */ - /* We probably need to add more cases here (hint: nodetrees), - * but will wait for changes from D5559 to get in first. */ - if (GS((*id_pointer)->name) == ID_GR) { - Collection *collection = (Collection *)*id_pointer; - if (collection->flag & COLLECTION_IS_MASTER) { - /* We should never reach that point anymore, since master collection private ID should be - * properly tagged with IDWALK_CB_EMBEDDED. */ - BLI_assert_unreachable(); - return IDWALK_RET_NOP; - } - } - - struct IDNameLib_Map *id_map = cb_data->user_data; - - /* NOTE: Handling of usercount 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); - - return IDWALK_RET_NOP; -} - -static void lib_link_main_data_restore(struct IDNameLib_Map *id_map, Main *newmain) -{ - ID *id; - FOREACH_MAIN_ID_BEGIN (newmain, id) { - BKE_library_foreach_ID_link(newmain, id, lib_link_main_data_restore_cb, id_map, IDWALK_NOP); - } - FOREACH_MAIN_ID_END; -} - -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); -} - -static void lib_link_window_scene_data_restore(wmWindow *win, Scene *scene, ViewLayer *view_layer) -{ - bScreen *screen = BKE_workspace_active_screen_get(win->workspace_hook); - - LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { - LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { - if (sl->spacetype == SPACE_VIEW3D) { - View3D *v3d = (View3D *)sl; - - if (v3d->camera == NULL || v3d->scenelock) { - v3d->camera = scene->camera; - } - - if (v3d->localvd) { - Base *base = NULL; - - 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) { - if (base->local_view_bits & v3d->local_view_uuid) { - break; - } - } - if (base == NULL) { - MEM_freeN(v3d->localvd); - v3d->localvd = NULL; - v3d->local_view_uuid = 0; - - /* Region-base storage is different depending if the space is active. */ - ListBase *regionbase = (sl == area->spacedata.first) ? &area->regionbase : - &sl->regionbase; - LISTBASE_FOREACH (ARegion *, region, regionbase) { - if (region->regiontype == RGN_TYPE_WINDOW) { - RegionView3D *rv3d = region->regiondata; - if (rv3d->localvd) { - MEM_freeN(rv3d->localvd); - rv3d->localvd = NULL; - } - } - } - } - } - } - } - } -} - -static void lib_link_workspace_layout_restore(struct IDNameLib_Map *id_map, - Main *newmain, - WorkSpaceLayout *layout) -{ - bScreen *screen = BKE_workspace_layout_screen_get(layout); - - /* avoid conflicts with 2.8x branch */ - { - LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { - LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { - 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); - } - 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); - - if (ads->filter_grp) { - ads->filter_grp = restore_pointer_by_name( - id_map, (ID *)ads->filter_grp, USER_IGNORE); - } - } - - /* force recalc of list of channels (i.e. includes calculating F-Curve colors) - * thus preventing the "black curves" problem post-undo - */ - sipo->runtime.flag |= SIPO_RUNTIME_FLAG_NEED_CHAN_SYNC_COLOR; - } - 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->flag &= ~SB_PIN_CONTEXT; - } - - /* TODO: restore path pointers: T40046 - * (complicated because this contains data pointers too, not just ID). */ - MEM_SAFE_FREE(sbuts->path); - } - else if (sl->spacetype == SPACE_FILE) { - SpaceFile *sfile = (SpaceFile *)sl; - sfile->op = NULL; - 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); - - if (saction->ads.filter_grp) { - saction->ads.filter_grp = restore_pointer_by_name( - id_map, (ID *)saction->ads.filter_grp, USER_IGNORE); - } - - /* force recalc of list of channels, potentially updating the active action - * while we're at it (as it can only be updated that way) T28962. - */ - saction->runtime.flag |= SACTION_RUNTIME_FLAG_NEED_CHAN_SYNC; - } - else if (sl->spacetype == SPACE_IMAGE) { - SpaceImage *sima = (SpaceImage *)sl; - - sima->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; - -#if 0 - /* Those are allocated and freed by space code, no need to handle them here. */ - MEM_SAFE_FREE(sima->scopes.waveform_1); - MEM_SAFE_FREE(sima->scopes.waveform_2); - MEM_SAFE_FREE(sima->scopes.waveform_3); - MEM_SAFE_FREE(sima->scopes.vecscope); -#endif - sima->scopes.ok = 0; - - /* 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); - } - else if (sl->spacetype == SPACE_SEQ) { - SpaceSeq *sseq = (SpaceSeq *)sl; - - /* 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); - } - 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); - - if (ads->filter_grp) { - ads->filter_grp = 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; - } - } - else if (sl->spacetype == SPACE_SCRIPT) { - SpaceScript *scpt = (SpaceScript *)sl; - - scpt->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. */ - if (scpt->script) { - SCRIPT_SET_NULL(scpt->script); - } - } - 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))) { - /* 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); - } - else { - tselem->id = NULL; - } - } - /* rebuild hash table, because it depends on ids too */ - space_outliner->storeflag |= SO_TREESTORE_REBUILD; - } - } - else if (sl->spacetype == SPACE_NODE) { - SpaceNode *snode = (SpaceNode *)sl; - bNodeTreePath *path, *path_next; - 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); - - ntree = snode->id ? ntreeFromID(snode->id) : NULL; - snode->nodetree = ntree ? - ntree : - restore_pointer_by_name(id_map, (ID *)snode->nodetree, USER_REAL); - - for (path = 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); - } - - if (!path->nodetree) { - break; - } - } - - /* remaining path entries are invalid, remove */ - for (; path; path = path_next) { - path_next = path->next; - - BLI_remlink(&snode->treepath, path); - MEM_freeN(path); - } - - /* 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; - snode->edittree = path->nodetree; - } - else { - snode->edittree = NULL; - } - } - 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->scopes.ok = 0; - } - else if (sl->spacetype == SPACE_SPREADSHEET) { - SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl; - - 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); - } - } - } - } - } - } -} - -void blo_lib_link_restore(Main *oldmain, - Main *newmain, - wmWindowManager *curwm, - Scene *curscene, - ViewLayer *cur_view_layer) -{ - struct IDNameLib_Map *id_map = BKE_main_idmap_create( - newmain, true, oldmain, MAIN_IDMAP_TYPE_NAME); - - LISTBASE_FOREACH (WorkSpace *, workspace, &newmain->workspaces) { - 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); - } - - LISTBASE_FOREACH (wmWindow *, win, &curwm->windows) { - WorkSpace *workspace = BKE_workspace_active_get(win->workspace_hook); - ID *workspace_id = (ID *)workspace; - Scene *oldscene = win->scene; - - 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 = 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) { - STRNCPY(win->view_layer_name, cur_view_layer->name); - } - BKE_workspace_active_set(win->workspace_hook, workspace); - - /* keep cursor location through undo */ - memcpy(&win->scene->cursor, &oldscene->cursor, sizeof(win->scene->cursor)); - - /* NOTE: even though that function seems to redo part of what is done by - * `lib_link_workspace_layout_restore()` above, it seems to have a slightly different scope: - * while the former updates the whole UI pointers from Main db (going over all layouts of - * all workspaces), that one only focuses one current active screen, takes care of - * 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); - } - - lib_link_wm_xr_data_restore(id_map, &curwm->xr); - - /* Restore all ID pointers in Main database itself - * (especially IDProperties might point to some word-space of other 'weirdly unchanged' ID - * pointers, see T69146). - * Note that this will re-apply again a few pointers in workspaces or so, - * but since we are remapping final ones already set above, - * that is just some minor harmless double-processing. */ - lib_link_main_data_restore(id_map, newmain); - - /* update IDs stored in all possible clipboards */ - lib_link_clipboard_restore(id_map); - - BKE_main_idmap_destroy(id_map); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Read ID: Library - * \{ */ - -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) { - if (newmain->curlib) { - if (BLI_path_cmp(newmain->curlib->filepath_abs, lib->filepath_abs) == 0) { - BLO_reportf_wrap(fd->reports, - RPT_WARNING, - TIP_("Library '%s', '%s' had multiple instances, save and reload!"), - lib->filepath, - lib->filepath_abs); - - change_link_placeholder_to_real_ID_pointer(fd->mainlist, fd, lib, newmain->curlib); - /* change_link_placeholder_to_real_ID_pointer_fd(fd, lib, newmain->curlib); */ - - BLI_remlink(&main->libraries, lib); - MEM_freeN(lib); - - /* Now, since Blender always expect **latest** Main pointer from fd->mainlist - * to be the active library Main pointer, - * where to add all non-library data-blocks found in file next, we have to switch that - * 'dupli' found Main to latest position in the list! - * Otherwise, you get weird disappearing linked data on a rather inconsistent basis. - * See also T53977 for reproducible case. */ - BLI_remlink(fd->mainlist, newmain); - BLI_addtail(fd->mainlist, newmain); - - return; - } - } - } - - /* Make sure we have full path in lib->filepath_abs */ - BLI_strncpy(lib->filepath_abs, lib->filepath, sizeof(lib->filepath)); - BLI_path_normalize(fd->relabase, lib->filepath_abs); - - // printf("direct_link_library: filepath %s\n", lib->filepath); - // printf("direct_link_library: filepath_abs %s\n", lib->filepath_abs); - - BlendDataReader reader = {fd}; - BKE_packedfile_blend_read(&reader, &lib->packedfile); - - /* new main */ - newmain = BKE_main_new(); - BLI_addtail(fd->mainlist, newmain); - newmain->curlib = lib; - - lib->parent = NULL; - - id_us_ensure_real(&lib->id); -} - -static void lib_link_library(BlendLibReader *UNUSED(reader), Library *UNUSED(lib)) -{ -} - -/* Always call this once you have loaded new library data to set the relative paths correctly - * in relation to the blend file. */ -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') { - 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 - * it absolute. This can happen when appending an object with a relative - * link into an unsaved blend file. See T27405. - * The remap relative option will make it relative again on save - campbell */ - if (BLI_path_is_rel(lib->filepath)) { - BLI_strncpy(lib->filepath, lib->filepath_abs, sizeof(lib->filepath)); - } - } - } - else { - LISTBASE_FOREACH (Library *, lib, &main->libraries) { - /* Libraries store both relative and abs paths, recreate relative paths, - * relative to the blend file since indirectly linked libs will be - * relative to their direct linked library. */ - if (BLI_path_is_rel(lib->filepath)) { /* if this is relative to begin with? */ - BLI_strncpy(lib->filepath, lib->filepath_abs, sizeof(lib->filepath)); - BLI_path_rel(lib->filepath, basepath); - } - } - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Read Library Data Block - * \{ */ - -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); - - *((short *)ph_id->name) = idcode; - BLI_strncpy(ph_id->name + 2, idname, sizeof(ph_id->name) - 2); - BKE_libblock_init_empty(ph_id); - ph_id->lib = mainvar->curlib; - ph_id->tag = tag | LIB_TAG_MISSING; - ph_id->us = ID_FAKE_USERS(ph_id); - ph_id->icon_id = 0; - - BLI_addtail(lb, ph_id); - id_sort_by_name(lb, ph_id, NULL); - - if (mainvar->id_map != NULL) { - BKE_main_idmap_insert_id(mainvar->id_map, ph_id); - } - - if ((tag & LIB_TAG_TEMP_MAIN) == 0) { - BKE_lib_libblock_session_uuid_ensure(ph_id); - } - - return ph_id; -} - -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) { - BKE_object_materials_test(bmain, ob, obdata); - } - } -} - -static const char *dataname(short id_code) -{ - switch ((ID_Type)id_code) { - case ID_OB: - return "Data from OB"; - case ID_ME: - return "Data from ME"; - case ID_IP: - return "Data from IP"; - case ID_SCE: - return "Data from SCE"; - case ID_MA: - return "Data from MA"; - case ID_TE: - return "Data from TE"; - case ID_CU_LEGACY: - return "Data from CU"; - case ID_GR: - return "Data from GR"; - case ID_AR: - return "Data from AR"; - case ID_AC: - return "Data from AC"; - case ID_LI: - return "Data from LI"; - case ID_MB: - return "Data from MB"; - case ID_IM: - return "Data from IM"; - case ID_LT: - return "Data from LT"; - case ID_LA: - return "Data from LA"; - case ID_CA: - return "Data from CA"; - case ID_KE: - return "Data from KE"; - case ID_WO: - return "Data from WO"; - case ID_SCR: - return "Data from SCR"; - case ID_VF: - return "Data from VF"; - case ID_TXT: - return "Data from TXT"; - case ID_SPK: - return "Data from SPK"; - case ID_LP: - return "Data from LP"; - case ID_SO: - return "Data from SO"; - case ID_NT: - return "Data from NT"; - case ID_BR: - return "Data from BR"; - case ID_PA: - return "Data from PA"; - case ID_PAL: - return "Data from PAL"; - case ID_PC: - return "Data from PCRV"; - case ID_GD: - return "Data from GD"; - case ID_WM: - return "Data from WM"; - case ID_MC: - return "Data from MC"; - case ID_MSK: - return "Data from MSK"; - case ID_LS: - return "Data from LS"; - case ID_CF: - return "Data from CF"; - case ID_WS: - return "Data from WS"; - case ID_CV: - return "Data from HA"; - case ID_PT: - return "Data from PT"; - case ID_VO: - return "Data from VO"; - case ID_SIM: - return "Data from SIM"; - } - return "Data from Lib Block"; -} - -static bool direct_link_id(FileData *fd, Main *main, const int tag, ID *id, ID *id_old) -{ - BlendDataReader reader = {fd}; - - /* Read part of datablock that is common between real and embedded datablocks. */ - direct_link_id_common(&reader, main->curlib, id, id_old, tag); - - if (tag & LIB_TAG_ID_LINK_PLACEHOLDER) { - /* For placeholder we only need to set the tag, no further data to read. */ - id->tag = tag; - return true; - } - - const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id); - if (id_type->blend_read_data != NULL) { - id_type->blend_read_data(&reader, id); - } - - /* XXX Very weakly handled currently, see comment in read_libblock() before trying to - * use it for anything new. */ - bool success = true; - - switch (GS(id->name)) { - case ID_SCR: - success = BKE_screen_blend_read_data(&reader, (bScreen *)id); - break; - case ID_LI: - direct_link_library(fd, (Library *)id, main); - break; - default: - /* Do nothing. Handled by IDTypeInfo callback. */ - break; - } - - /* try to restore (when undoing) or clear ID's cache pointers. */ - if (id_type->foreach_cache != NULL) { - BKE_idtype_id_foreach_cache( - id, blo_cache_storage_entry_restore_in_new, reader.fd->cache_storage); - } - - return success; -} - -/* Read all data associated with a datablock into datamap. */ -static BHead *read_data_into_datamap(FileData *fd, BHead *bhead, const char *allocname) -{ - bhead = blo_bhead_next(fd, bhead); - - while (bhead && bhead->code == DATA) { - /* The code below is useful for debugging leaks in data read from the blend file. - * Without this the messages only tell us what ID-type the memory came from, - * eg: `Data from OB len 64`, see #dataname. - * 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]]; - size_t allocname_size = strlen(allocname) + 1; - char *allocname_buf = malloc(allocname_size); - memcpy(allocname_buf, allocname, allocname_size); - allocname = allocname_buf; - } -#endif - - void *data = read_struct(fd, bhead, allocname); - if (data) { - oldnewmap_insert(fd->datamap, bhead->old, data, 0); - } - - bhead = blo_bhead_next(fd, bhead); - } - - return bhead; -} - -/* Verify if the datablock and all associated data is identical. */ -static bool read_libblock_is_identical(FileData *fd, BHead *bhead) -{ - /* Test ID itself. */ - if (bhead->len && !BHEADN_FROM_BHEAD(bhead)->is_memchunk_identical) { - return false; - } - - /* Test any other data that is part of ID (logic must match read_data_into_datamap). */ - bhead = blo_bhead_next(fd, bhead); - - while (bhead && bhead->code == DATA) { - if (bhead->len && !BHEADN_FROM_BHEAD(bhead)->is_memchunk_identical) { - return false; - } - - bhead = blo_bhead_next(fd, bhead); - } - - return true; -} - -/* For undo, restore matching library datablock from the old main. */ -static bool read_libblock_undo_restore_library(FileData *fd, Main *main, const ID *id) -{ - /* In undo case, most libs and linked data should be kept as is from previous state - * (see BLO_read_from_memfile). - * However, some needed by the snapshot being read may have been removed in previous one, - * and would go missing. - * This leads e.g. to disappearing objects in some undo/redo case, see T34446. - * That means we have to carefully check whether current lib or - * libdata already exits in old main, if it does we merely copy it over into new main area, - * 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; - /* 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; - CLOG_INFO(&LOG_UNDO, - 2, - " compare with %s -> match", - libmain->curlib ? libmain->curlib->id.name : ""); - /* 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! - * Order is crucial, we cannot bulk-add it in BLO_read_from_memfile() - * like it used to be. */ - BLI_remlink(fd->old_mainlist, libmain); - BLI_remlink_safe(&oldmain->libraries, libmain->curlib); - BLI_addtail(fd->mainlist, libmain); - BLI_addtail(&main->libraries, libmain->curlib); - return true; - } - CLOG_INFO(&LOG_UNDO, - 2, - " compare with %s -> NO match", - libmain->curlib ? libmain->curlib->id.name : ""); - } - - return false; -} - -/* For undo, restore existing linked datablock from the old main. */ -static bool read_libblock_undo_restore_linked(FileData *fd, Main *main, const ID *id, BHead *bhead) -{ - 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) { - CLOG_INFO(&LOG_UNDO, - 2, - " from %s (%s): found", - main->curlib ? main->curlib->id.name : "", - main->curlib ? main->curlib->filepath : ""); - /* 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)); - } - - /* No need to do anything else for ID_LINK_PLACEHOLDER, it's assumed - * already present in its lib's main. */ - return true; - } - - CLOG_INFO(&LOG_UNDO, - 2, - " from %s (%s): NOT found", - main->curlib ? main->curlib->id.name : "", - main->curlib ? main->curlib->filepath : ""); - return false; -} - -/* For undo, restore unchanged datablock from old main. */ -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); - - /* Some tags need to be preserved here. */ - id_old->tag = tag | (id_old->tag & LIB_TAG_EXTRAUSER); - id_old->lib = main->curlib; - 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; - - const short idcode = GS(id_old->name); - Main *old_bmain = 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); - BLI_addtail(new_lb, id_old); - - /* Recalc flags, mostly these just remain as they are. */ - id_old->recalc |= direct_link_id_restore_recalc_exceptions(id_old); - id_old->recalc_after_undo_push = 0; - - if (GS(id_old->name) == ID_OB) { - Object *ob = (Object *)id_old; - /* For undo we stay in object mode during undo presses, so keep editmode disabled for re-used - * data-blocks too. */ - ob->mode &= ~OB_MODE_EDIT; - } -} - -/* For undo, store changed datablock at old address. */ -static void read_libblock_undo_restore_at_old_address(FileData *fd, Main *main, ID *id, ID *id_old) -{ - /* During memfile undo, if an ID changed and we cannot directly re-use existing one from old - * bmain, we do a full read of the new id from the memfile, and then fully swap its content - * with the old id. This allows us to keep the same pointer even for modified data, which - * 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); - - const short idcode = GS(id->name); - - Main *old_bmain = 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); - BLI_remlink(new_lb, id); - - /* 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); - - /* 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 - * #Scene. */ - id_old->orig_id = id; - - BLI_addtail(new_lb, id_old); - BLI_addtail(old_lb, id); -} - -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 short idcode = GS(id->name); - - if (bhead->code == ID_LI) { - /* Restore library datablock. */ - if (read_libblock_undo_restore_library(fd, main, id)) { - return true; - } - } - else if (bhead->code == ID_LINK_PLACEHOLDER) { - /* Restore linked datablock. */ - if (read_libblock_undo_restore_linked(fd, main, id, bhead)) { - return true; - } - } - else if (ELEM(idcode, ID_WM, ID_SCR, ID_WS)) { - /* Skip reading any UI datablocks, existing ones are kept. We don't - * support pointers from other datablocks to UI datablocks so those - * we also don't put UI datablocks in fd->libmap. */ - return true; - } - - /* Restore local datablocks. */ - ID *id_old = NULL; - 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); - - /* 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); - id_old = BKE_main_idmap_lookup_uuid(fd->old_idmap, id->session_uuid); - } - - if (id_old != NULL && read_libblock_is_identical(fd, bhead)) { - /* Local datablock was unchanged, restore from the old main. */ - CLOG_INFO(&LOG_UNDO, - 2, - "UNDO: read %s (uuid %u) -> keep identical datablock", - id->name, - id->session_uuid); - - /* Do not add LIB_TAG_NEW here, this should not be needed/used in undo case anyway (as - * this is only for do_version-like code), but for sake of consistency, and also because - * it will tell us which ID is re-used from old Main, and which one is actually new. */ - /* Also do not add LIB_TAG_NEED_LINK, those IDs will never be re-liblinked, hence that tag will - * never be cleared, leading to critical issue in link/append code. */ - const int id_tag = tag | LIB_TAG_UNDO_OLD_ID_REUSED; - read_libblock_undo_restore_identical(fd, main, id, id_old, id_tag); - - /* 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); - - *r_id_old = id_old; - return true; - } - if (id_old != NULL) { - /* Local datablock was changed. Restore at the address of the old datablock. */ - CLOG_INFO(&LOG_UNDO, - 2, - "UNDO: read %s (uuid %u) -> read to old existing address", - id->name, - id->session_uuid); - *r_id_old = id_old; - return false; - } - - /* Local datablock does not exist in the undo step, so read from scratch. */ - CLOG_INFO( - &LOG_UNDO, 2, "UNDO: read %s (uuid %u) -> read at new address", id->name, id->session_uuid); - return false; -} - -/* This routine reads a datablock and its direct data, and advances bhead to - * the next datablock. For library linked datablocks, only a placeholder will - * be generated, to be replaced in read_library_linked_ids. - * - * When reading for undo, libraries, linked datablocks and unchanged datablocks - * will be restored from the old database. Only new or changed datablocks will - * actually be read. */ -static BHead *read_libblock(FileData *fd, - Main *main, - BHead *bhead, - const int tag, - const bool placeholder_set_indirect_extern, - ID **r_id) -{ - /* 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; - 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) { - BKE_main_idmap_insert_id(main->id_map, id_old); - } - - return blo_bhead_next(fd, bhead); - } - } - - /* Read libblock struct. */ - ID *id = read_struct(fd, bhead, "lib block"); - if (id == NULL) { - if (r_id) { - *r_id = NULL; - } - return blo_bhead_next(fd, bhead); - } - - /* Determine ID type and add to main database list. */ - const short idcode = GS(id->name); - ListBase *lb = which_libbase(main, idcode); - if (lb == NULL) { - /* Unknown ID type. */ - CLOG_WARN(&LOG, "Unknown id code '%c%c'", (idcode & 0xff), (idcode >> 8)); - MEM_freeN(id); - if (r_id) { - *r_id = NULL; - } - return blo_bhead_next(fd, bhead); - } - - /* NOTE: id must be added to the list before direct_link_id(), since - * direct_link_library() may remove it from there in case of duplicates. */ - BLI_addtail(lb, id); - - /* 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 - * 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); - - if (r_id) { - *r_id = id_target; - } - - /* Set tag for new datablock to indicate lib linking and versioning needs - * to be done still. */ - int id_tag = tag | LIB_TAG_NEED_LINK | LIB_TAG_NEW; - - if (bhead->code == ID_LINK_PLACEHOLDER) { - /* Read placeholder for linked datablock. */ - id_tag |= LIB_TAG_ID_LINK_PLACEHOLDER; - - if (placeholder_set_indirect_extern) { - if (id->flag & LIB_INDIRECT_WEAK_LINK) { - id_tag |= LIB_TAG_INDIRECT; - } - else { - id_tag |= LIB_TAG_EXTERN; - } - } - - direct_link_id(fd, main, id_tag, id, id_old); - - if (main->id_map != NULL) { - BKE_main_idmap_insert_id(main->id_map, id); - } - - return blo_bhead_next(fd, bhead); - } - - /* Read datablock contents. - * Use convenient malloc name for debugging and better memory link prints. */ - const char *allocname = dataname(idcode); - bhead = read_data_into_datamap(fd, bhead, allocname); - const bool success = direct_link_id(fd, main, id_tag, id, id_old); - oldnewmap_clear(fd->datamap); - - if (!success) { - /* XXX This is probably working OK currently given the very limited scope of that flag. - * However, it is absolutely **not** handled correctly: it is freeing an ID pointer that has - * 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; - } - } - else if (id_old) { - /* For undo, store contents read into id at id_old. */ - read_libblock_undo_restore_at_old_address(fd, main, id, id_old); - - if (main->id_map != NULL) { - BKE_main_idmap_insert_id(main->id_map, id_old); - } - } - else if (main->id_map != NULL) { - BKE_main_idmap_insert_id(main->id_map, id); - } - - return bhead; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Read Asset Data - * \{ */ - -BHead *blo_read_asset_data_block(FileData *fd, BHead *bhead, AssetMetaData **r_asset_data) -{ - BLI_assert(blo_bhead_is_id_valid_type(bhead)); - - bhead = read_data_into_datamap(fd, bhead, "asset-data read"); - - BlendDataReader reader = {fd}; - BLO_read_data_address(&reader, r_asset_data); - BKE_asset_metadata_read(&reader, *r_asset_data); - - oldnewmap_clear(fd->datamap); - - return bhead; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Read Global Data - * \{ */ - -/* NOTE: this has to be kept for reading older files... */ -/* also version info is written here */ -static BHead *read_global(BlendFileData *bfd, FileData *fd, BHead *bhead) -{ - FileGlobal *fg = read_struct(fd, bhead, "Global"); - - /* copy to bfd handle */ - bfd->main->subversionfile = fg->subversion; - bfd->main->minversionfile = fg->minversion; - bfd->main->minsubversionfile = fg->minsubversion; - bfd->main->build_commit_timestamp = fg->build_commit_timestamp; - BLI_strncpy(bfd->main->build_hash, fg->build_hash, sizeof(bfd->main->build_hash)); - - bfd->fileflags = fg->fileflags; - bfd->globalf = fg->globalf; - STRNCPY(bfd->filepath, fg->filepath); - - /* Error in 2.65 and older: `main->filepath` was not set if you save from startup - * (not after loading file). */ - if (bfd->filepath[0] == 0) { - if (fd->fileversion < 265 || (fd->fileversion == 265 && fg->subversion < 1)) { - if ((G.fileflags & G_FILE_RECOVER_READ) == 0) { - STRNCPY(bfd->filepath, BKE_main_blendfile_path(bfd->main)); - } - } - - /* early 2.50 version patch - filepath not in FileGlobal struct at all */ - if (fd->fileversion <= 250) { - STRNCPY(bfd->filepath, BKE_main_blendfile_path(bfd->main)); - } - } - - if (G.fileflags & G_FILE_RECOVER_READ) { - BLI_strncpy(fd->relabase, fg->filepath, sizeof(fd->relabase)); - } - - bfd->curscreen = fg->curscreen; - bfd->curscene = fg->curscene; - bfd->cur_view_layer = fg->cur_view_layer; - - MEM_freeN(fg); - - fd->globalf = bfd->globalf; - fd->fileflags = bfd->fileflags; - - return blo_bhead_next(fd, 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); - /* this happens in files older than 2.35 */ - if (bfd->curscene == NULL) { - if (bfd->curscreen) { - bfd->curscene = bfd->curscreen->scene; - } - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Versioning - * \{ */ - -static void do_versions_userdef(FileData *UNUSED(fd), BlendFileData *bfd) -{ - UserDef *user = bfd->user; - - if (user == NULL) { - return; - } - - blo_do_versions_userdef(user); -} - -static void do_versions(FileData *fd, Library *lib, Main *main) -{ - /* WATCH IT!!!: pointers from libdata have not been converted */ - - /* Don't allow versioning to create new data-blocks. */ - main->is_locked_for_linking = true; - - 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; - if (LIKELY(tm)) { - strftime(build_commit_datetime, sizeof(build_commit_datetime), "%Y-%m-%d %H:%M", tm); - } - else { - BLI_strncpy(build_commit_datetime, "unknown", sizeof(build_commit_datetime)); - } - - CLOG_INFO(&LOG, 0, "Read file %s", fd->relabase); - CLOG_INFO(&LOG, - 0, - " Version %d sub %d date %s hash %s", - main->versionfile, - main->subversionfile, - build_commit_datetime, - main->build_hash); - } - - blo_do_versions_pre250(fd, lib, main); - blo_do_versions_250(fd, lib, main); - blo_do_versions_260(fd, lib, main); - blo_do_versions_270(fd, lib, main); - blo_do_versions_280(fd, lib, main); - blo_do_versions_290(fd, lib, main); - blo_do_versions_300(fd, lib, main); - blo_do_versions_cycles(fd, lib, main); - - /* WATCH IT!!!: pointers from libdata have not been converted yet here! */ - /* WATCH IT 2!: Userdef struct init see do_versions_userdef() above! */ - - /* don't forget to set version number in BKE_blender_version.h! */ - - main->is_locked_for_linking = false; -} - -static void do_versions_after_linking(Main *main, ReportList *reports) -{ - CLOG_INFO(&LOG, - 2, - "Processing %s (%s), %d.%d", - main->curlib ? main->curlib->filepath : main->filepath, - main->curlib ? "LIB" : "MAIN", - main->versionfile, - main->subversionfile); - - /* Don't allow versioning to create new data-blocks. */ - main->is_locked_for_linking = true; - - do_versions_after_linking_250(main); - do_versions_after_linking_260(main); - do_versions_after_linking_270(main); - do_versions_after_linking_280(main, reports); - do_versions_after_linking_290(main, reports); - do_versions_after_linking_300(main, reports); - do_versions_after_linking_cycles(main); - - main->is_locked_for_linking = false; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Read Library Data Block (all) - * \{ */ - -static void lib_link_all(FileData *fd, Main *bmain) -{ - const bool do_partial_undo = (fd->skip_flags & BLO_READ_SKIP_UNDO_OLD_MAIN) == 0; - - BlendLibReader reader = {fd, bmain}; - - ID *id; - FOREACH_MAIN_ID_BEGIN (bmain, id) { - if ((id->tag & LIB_TAG_NEED_LINK) == 0) { - /* This ID does not need liblink, just skip to next one. */ - continue; - } - - if ((fd->flags & FD_FLAGS_IS_MEMFILE) && GS(id->name) == ID_WM) { - /* No load UI for undo memfiles. - * Only WM currently, SCR needs it still (see below), and so does WS? */ - continue; - } - - if ((fd->flags & FD_FLAGS_IS_MEMFILE) && do_partial_undo && - (id->tag & LIB_TAG_UNDO_OLD_ID_REUSED) != 0) { - /* This ID has been re-used from 'old' bmain. Since it was therefore unchanged across - * current undo step, and old IDs re-use their old memory address, we do not need to liblink - * it at all. */ - continue; - } - - lib_link_id(&reader, id); - - const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id); - if (id_type->blend_read_lib != NULL) { - id_type->blend_read_lib(&reader, id); - } - - if (GS(id->name) == ID_LI) { - lib_link_library(&reader, (Library *)id); /* Only init users. */ - } - - id->tag &= ~LIB_TAG_NEED_LINK; - - /* 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) { - id_type->blend_read_undo_preserve(&reader, id, id->orig_id); - } - } - FOREACH_MAIN_ID_END; - - /* Cleanup `ID.orig_id`, this is now reserved for depsgraph/COW usage only. */ - FOREACH_MAIN_ID_BEGIN (bmain, id) { - id->orig_id = NULL; - } - FOREACH_MAIN_ID_END; - -#ifndef NDEBUG - /* Double check we do not have any 'need link' tag remaining, this should never be the case once - * this function has run. */ - FOREACH_MAIN_ID_BEGIN (bmain, id) { - BLI_assert((id->tag & LIB_TAG_NEED_LINK) == 0); - } - FOREACH_MAIN_ID_END; -#endif -} - -/** - * Checks to perform after `lib_link_all`. - * Those operations cannot perform properly in a split bmain case, since some data from other - * bmain's (aka libraries) may not have been processed yet. - */ -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)); - - /* 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); - - /* We have to rebuild that runtime information *after* all data-blocks have been properly linked. - */ - BKE_main_collections_parent_relations_rebuild(bmain); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Read User Preferences - * \{ */ - -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->flag &= ~KMI_UPDATE; -} - -static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead) -{ - UserDef *user; - bfd->user = user = read_struct(fd, bhead, "user def"); - - /* User struct has separate do-version handling */ - user->versionfile = bfd->main->versionfile; - user->subversionfile = bfd->main->subversionfile; - - /* read all data into fd->datamap */ - bhead = read_data_into_datamap(fd, bhead, "user def"); - - BlendDataReader reader_ = {fd}; - BlendDataReader *reader = &reader_; - - BLO_read_list(reader, &user->themes); - BLO_read_list(reader, &user->user_keymaps); - BLO_read_list(reader, &user->user_keyconfig_prefs); - BLO_read_list(reader, &user->user_menus); - BLO_read_list(reader, &user->addons); - BLO_read_list(reader, &user->autoexec_paths); - BLO_read_list(reader, &user->asset_libraries); - - LISTBASE_FOREACH (wmKeyMap *, keymap, &user->user_keymaps) { - keymap->modal_items = NULL; - keymap->poll = NULL; - keymap->flag &= ~KEYMAP_UPDATE; - - BLO_read_list(reader, &keymap->diff_items); - BLO_read_list(reader, &keymap->items); - - LISTBASE_FOREACH (wmKeyMapDiffItem *, kmdi, &keymap->diff_items) { - BLO_read_data_address(reader, &kmdi->remove_item); - BLO_read_data_address(reader, &kmdi->add_item); - - if (kmdi->remove_item) { - direct_link_keymapitem(reader, kmdi->remove_item); - } - if (kmdi->add_item) { - direct_link_keymapitem(reader, kmdi->add_item); - } - } - - LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) { - direct_link_keymapitem(reader, kmi); - } - } - - LISTBASE_FOREACH (wmKeyConfigPref *, kpt, &user->user_keyconfig_prefs) { - BLO_read_data_address(reader, &kpt->prop); - IDP_BlendDataRead(reader, &kpt->prop); - } - - LISTBASE_FOREACH (bUserMenu *, um, &user->user_menus) { - BLO_read_list(reader, &um->items); - LISTBASE_FOREACH (bUserMenuItem *, umi, &um->items) { - if (umi->type == USER_MENU_TYPE_OPERATOR) { - bUserMenuItem_Op *umi_op = (bUserMenuItem_Op *)umi; - BLO_read_data_address(reader, &umi_op->prop); - IDP_BlendDataRead(reader, &umi_op->prop); - } - } - } - - LISTBASE_FOREACH (bAddon *, addon, &user->addons) { - BLO_read_data_address(reader, &addon->prop); - IDP_BlendDataRead(reader, &addon->prop); - } - - /* XXX */ - user->uifonts.first = user->uifonts.last = NULL; - - BLO_read_list(reader, &user->uistyles); - - /* Don't read the active app template, use the default one. */ - user->app_template[0] = '\0'; - - /* Clear runtime data. */ - user->runtime.is_dirty = false; - user->edit_studio_light = 0; - - /* free fd->datamap again */ - oldnewmap_clear(fd->datamap); - - return bhead; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Read File (Internal) - * \{ */ - -BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath) -{ - BHead *bhead = blo_bhead_first(fd); - BlendFileData *bfd; - ListBase mainlist = {NULL, NULL}; - - if (fd->flags & FD_FLAGS_IS_MEMFILE) { - CLOG_INFO(&LOG_UNDO, 2, "UNDO: read step"); - } - - bfd = MEM_callocN(sizeof(BlendFileData), "blendfiledata"); - - bfd->main = BKE_main_new(); - bfd->main->versionfile = fd->fileversion; - - bfd->type = BLENFILETYPE_BLEND; - - if ((fd->skip_flags & BLO_READ_SKIP_DATA) == 0) { - BLI_addtail(&mainlist, bfd->main); - fd->mainlist = &mainlist; - STRNCPY(bfd->main->filepath, filepath); - } - - if (G.background) { - /* We only read & store .blend thumbnail in background mode - * (because we cannot re-generate it, no OpenGL available). - */ - const int *data = read_file_thumbnail(fd); - - if (data) { - const int width = data[0]; - 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__); - - BLI_assert((data_size - sizeof(*bfd->main->blen_thumb)) == - (BLEN_THUMB_MEMSIZE_FILE(width, height) - (sizeof(*data) * 2))); - bfd->main->blen_thumb->width = width; - bfd->main->blen_thumb->height = height; - memcpy(bfd->main->blen_thumb->rect, &data[2], data_size - sizeof(*bfd->main->blen_thumb)); - } - } - } - - while (bhead) { - switch (bhead->code) { - case DATA: - case DNA1: - case TEST: /* used as preview since 2.5x */ - case REND: - bhead = blo_bhead_next(fd, bhead); - break; - case GLOB: - bhead = read_global(bfd, fd, bhead); - break; - case USER: - if (fd->skip_flags & BLO_READ_SKIP_USERDEF) { - bhead = blo_bhead_next(fd, bhead); - } - else { - bhead = read_userdef(bfd, fd, bhead); - } - break; - case ENDB: - bhead = NULL; - break; - - case ID_LINK_PLACEHOLDER: - if (fd->skip_flags & BLO_READ_SKIP_DATA) { - bhead = blo_bhead_next(fd, bhead); - } - else { - /* Add link placeholder to the main of the library it belongs to. - * 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); - } - break; - /* in 2.50+ files, the file identifier for screens is patched, forward compatibility */ - case ID_SCRN: - bhead->code = ID_SCR; - /* pass on to default */ - ATTR_FALLTHROUGH; - default: - if (fd->skip_flags & BLO_READ_SKIP_DATA) { - bhead = blo_bhead_next(fd, bhead); - } - else { - bhead = read_libblock(fd, bfd->main, bhead, LIB_TAG_LOCAL, false, NULL); - } - } - } - - /* 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); - } - - if ((fd->skip_flags & BLO_READ_SKIP_USERDEF) == 0) { - do_versions_userdef(fd, bfd); - } - } - - if ((fd->skip_flags & BLO_READ_SKIP_DATA) == 0) { - fd->reports->duration.libraries = PIL_check_seconds_timer(); - read_libraries(fd, &mainlist); - - blo_join_main(&mainlist); - - lib_link_all(fd, bfd->main); - after_liblink_merged_bmain_process(bfd->main); - - fd->reports->duration.libraries = PIL_check_seconds_timer() - fd->reports->duration.libraries; - - /* Skip in undo case. */ - if ((fd->flags & FD_FLAGS_IS_MEMFILE) == 0) { - /* Note that we can't recompute user-counts at this point in undo case, we play too much with - * IDs from different memory realms, and Main database is not in a fully valid state yet. - */ - /* Some versioning code does expect some proper user-reference-counting, e.g. in conversion - * from groups to collections... We could optimize out that first call when we are reading a - * current version file, but again this is really not a bottle neck currently. - * So not worth it. */ - BKE_main_id_refcount_recompute(bfd->main, false); - - /* Yep, second splitting... but this is a very cheap operation, so no big deal. */ - blo_split_main(&mainlist, bfd->main); - LISTBASE_FOREACH (Main *, mainvar, &mainlist) { - BLI_assert(mainvar->versionfile != 0); - do_versions_after_linking(mainvar, fd->reports->reports); - } - blo_join_main(&mainlist); - - /* And we have to compute those user-reference-counts again, as `do_versions_after_linking()` - * does not always properly handle user counts, and/or that function does not take into - * account old, deprecated data. */ - BKE_main_id_refcount_recompute(bfd->main, false); - } - - /* After all data has been read and versioned, uses LIB_TAG_NEW. Theoretically this should - * not be calculated in the undo case, but it is currently needed even on undo to recalculate - * a cache. */ - ntreeUpdateAllNew(bfd->main); - - placeholders_ensure_valid(bfd->main); - - BKE_main_id_tag_all(bfd->main, LIB_TAG_NEW, false); - - /* Now that all our data-blocks are loaded, - * we can re-generate overrides from their references. */ - if ((fd->flags & FD_FLAGS_IS_MEMFILE) == 0) { - /* Do not apply in undo case! */ - fd->reports->duration.lib_overrides = PIL_check_seconds_timer(); - - BKE_lib_override_library_main_validate(bfd->main, fd->reports->reports); - BKE_lib_override_library_main_update(bfd->main); - - fd->reports->duration.lib_overrides = PIL_check_seconds_timer() - - fd->reports->duration.lib_overrides; - } - - BKE_collections_after_lib_link(bfd->main); - - /* Make all relative paths, relative to the open blend file. */ - fix_relpaths_library(fd->relabase, bfd->main); - - link_global(fd, bfd); /* as last */ - } - - fd->mainlist = NULL; /* Safety, this is local variable, shall not be used afterward. */ - - BLI_assert(bfd->main->id_map == NULL); - - return bfd; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Library Linking - * - * Also used for append. - * \{ */ - -struct BHeadSort { - BHead *bhead; - const void *old; -}; - -static int verg_bheadsort(const void *v1, const void *v2) -{ - const struct BHeadSort *x1 = v1, *x2 = v2; - - if (x1->old > x2->old) { - return 1; - } - if (x1->old < x2->old) { - return -1; - } - return 0; -} - -static void sort_bhead_old_map(FileData *fd) -{ - BHead *bhead; - struct BHeadSort *bhs; - int tot = 0; - - for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) { - tot++; - } - - fd->tot_bheadmap = tot; - if (tot == 0) { - return; - } - - bhs = fd->bheadmap = 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; - bhs->old = bhead->old; - } - - qsort(fd->bheadmap, tot, sizeof(struct BHeadSort), verg_bheadsort); -} - -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; - } - - for (; bhead; bhead = blo_bhead_prev(fd, bhead)) { - if (bhead->code == ID_LI) { - break; - } - } - - return bhead; -} - -static BHead *find_bhead(FileData *fd, void *old) -{ -#if 0 - BHead* bhead; -#endif - struct BHeadSort *bhs, bhs_s; - - if (!old) { - return NULL; - } - - if (fd->bheadmap == NULL) { - sort_bhead_old_map(fd); - } - - bhs_s.old = old; - bhs = bsearch(&bhs_s, fd->bheadmap, fd->tot_bheadmap, sizeof(struct BHeadSort), verg_bheadsort); - - if (bhs) { - return bhs->bhead; - } - -#if 0 - for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) { - if (bhead->old == old) { - return bhead; - } - } -#endif - - return NULL; -} - -static BHead *find_bhead_from_code_name(FileData *fd, const short idcode, const char *name) -{ -#ifdef USE_GHASH_BHEAD - - char idname_full[MAX_ID_NAME]; - - *((short *)idname_full) = idcode; - BLI_strncpy(idname_full + 2, name, sizeof(idname_full) - 2); - - return BLI_ghash_lookup(fd->bhead_idname_hash, idname_full); - -#else - BHead *bhead; - - for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) { - if (bhead->code == idcode) { - const char *idname_test = blo_bhead_id_name(fd, bhead); - if (STREQ(idname_test + 2, name)) { - return bhead; - } - } - else if (bhead->code == ENDB) { - break; - } - } - - return NULL; -#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); -#else - return find_bhead_from_code_name(fd, GS(idname), idname + 2); -#endif -} - -static ID *is_yet_read(FileData *fd, Main *mainvar, BHead *bhead) -{ - if (mainvar->id_map == NULL) { - mainvar->id_map = BKE_main_idmap_create(mainvar, false, NULL, MAIN_IDMAP_TYPE_NAME); - } - BLI_assert(BKE_main_idmap_main_get(mainvar->id_map) == mainvar); - - const char *idname = blo_bhead_id_name(fd, bhead); - - ID *id = BKE_main_idmap_lookup_name(mainvar->id_map, GS(idname), idname + 2, mainvar->curlib); - BLI_assert(id == BLI_findstring(which_libbase(mainvar, GS(idname)), idname, offsetof(ID, name))); - return id; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Library Linking (expand pointers) - * \{ */ - -static void expand_doit_library(void *fdhandle, Main *mainvar, void *old) -{ - FileData *fd = fdhandle; - - BHead *bhead = find_bhead(fd, old); - if (bhead == NULL) { - return; - } - - if (bhead->code == ID_LINK_PLACEHOLDER) { - /* Placeholder link to data-lock in another library. */ - BHead *bheadlib = find_previous_lib(fd, bhead); - if (bheadlib == NULL) { - return; - } - - Library *lib = read_struct(fd, bheadlib, "Library"); - Main *libmain = blo_find_main(fd, lib->filepath, fd->relabase); - - if (libmain->curlib == NULL) { - const char *idname = blo_bhead_id_name(fd, bhead); - - BLO_reportf_wrap(fd->reports, - RPT_WARNING, - TIP_("LIB: Data refers to main .blend file: '%s' from %s"), - idname, - mainvar->curlib->filepath_abs); - return; - } - - ID *id = is_yet_read(fd, libmain, bhead); - - if (id == NULL) { - /* ID has not been read yet, add placeholder to the main of the - * library it belongs to, so that it will be read later. */ - read_libblock(fd, libmain, bhead, fd->id_tag_extra | LIB_TAG_INDIRECT, false, &id); - id_sort_by_name(which_libbase(libmain, GS(id->name)), id, id->prev); - - /* commented because this can print way too much */ - // if (G.debug & G_DEBUG) printf("expand_doit: other lib %s\n", lib->filepath); - - /* for outliner dependency only */ - libmain->curlib->parent = mainvar->curlib; - } - else { - /* Convert any previously read weak link to regular link - * to signal that we want to read this data-block. */ - if (id->tag & LIB_TAG_ID_LINK_PLACEHOLDER) { - id->flag &= ~LIB_INDIRECT_WEAK_LINK; - } - - /* "id" is either a placeholder or real ID that is already in the - * main of the library (A) it belongs to. However it might have been - * put there by another library (C) which only updated its own - * fd->libmap. In that case we also need to update the fd->libmap - * of the current library (B) so we can find it for lookups. - * - * An example of such a setup is: - * (A) tree.blend: contains Tree object. - * (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); - - /* If "id" is a real data-lock and not a placeholder, we need to - * update fd->libmap to replace ID_LINK_PLACEHOLDER with the real - * ID_* code. - * - * When the real ID is read this replacement happens for all - * libraries read so far, but not for libraries that have not been - * read yet at that point. */ - change_link_placeholder_to_real_ID_pointer_fd(fd, bhead->old, id); - - /* Commented because this can print way too much. */ -#if 0 - if (G.debug & G_DEBUG) { - printf("expand_doit: already linked: %s lib: %s\n", id->name, lib->filepath); - } -#endif - } - - MEM_freeN(lib); - } - else { - /* Data-block in same library. */ - /* In 2.50+ file identifier for screens is patched, forward compatibility. */ - if (bhead->code == ID_SCRN) { - bhead->code = ID_SCR; - } - - ID *id = is_yet_read(fd, mainvar, bhead); - if (id == NULL) { - 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); - } - else { - /* Convert any previously read weak link to regular link - * to signal that we want to read this data-block. */ - if (id->tag & LIB_TAG_ID_LINK_PLACEHOLDER) { - id->flag &= ~LIB_INDIRECT_WEAK_LINK; - } - - /* 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); - /* commented because this can print way too much */ - // if (G.debug & G_DEBUG) printf("expand: already read %s\n", id->name); - } - } -} - -static BLOExpandDoitCallback expand_doit; - -static void expand_id(BlendExpander *expander, ID *id); - -static void expand_id_embedded_id(BlendExpander *expander, ID *id) -{ - /* Handle 'private IDs'. */ - bNodeTree *nodetree = ntreeFromID(id); - if (nodetree != NULL) { - expand_id(expander, &nodetree->id); - ntreeBlendReadExpand(expander, nodetree); - } - - if (GS(id->name) == ID_SCE) { - Scene *scene = (Scene *)id; - if (scene->master_collection != NULL) { - expand_id(expander, &scene->master_collection->id); - BKE_collection_blend_read_expand(expander, scene->master_collection); - } - } -} - -static void expand_id(BlendExpander *expander, ID *id) -{ - IDP_BlendReadExpand(expander, id->properties); - - if (id->override_library) { - BLO_expand(expander, id->override_library->reference); - BLO_expand(expander, id->override_library->storage); - } - - AnimData *adt = BKE_animdata_from_id(id); - if (adt != NULL) { - BKE_animdata_blend_read_expand(expander, adt); - } - - expand_id_embedded_id(expander, id); -} - -void BLO_main_expander(BLOExpandDoitCallback expand_doit_func) -{ - expand_doit = expand_doit_func; -} - -void BLO_expand_main(void *fdhandle, Main *mainvar) -{ - ListBase *lbarray[INDEX_ID_MAX]; - FileData *fd = fdhandle; - ID *id; - int a; - bool do_it = true; - - BlendExpander expander = {fd, mainvar}; - - while (do_it) { - do_it = false; - - a = set_listbasepointers(mainvar, lbarray); - while (a--) { - 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) { - id_type->blend_read_expand(&expander, id); - } - - do_it = true; - id->tag &= ~LIB_TAG_NEED_EXPAND; - } - id = id->next; - } - } - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Library Linking (helper functions) - * \{ */ - -/* returns true if the item was found - * but it may already have already been appended/linked */ -static ID *link_named_part( - Main *mainl, FileData *fd, const short idcode, const char *name, const int flag) -{ - BHead *bhead = find_bhead_from_code_name(fd, idcode, name); - ID *id; - - const bool use_placeholders = (flag & BLO_LIBLINK_USE_PLACEHOLDERS) != 0; - const bool force_indirect = (flag & BLO_LIBLINK_FORCE_INDIRECT) != 0; - - BLI_assert(BKE_idtype_idcode_is_linkable(idcode) && BKE_idtype_idcode_is_valid(idcode)); - - if (bhead) { - id = is_yet_read(fd, mainl, bhead); - if (id == NULL) { - /* 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); - - if (id) { - /* sort by name in list */ - ListBase *lb = which_libbase(mainl, idcode); - id_sort_by_name(lb, id, NULL); - } - } - else { - /* already linked */ - CLOG_WARN(&LOG, "Append: ID '%s' is already linked", id->name); - oldnewmap_insert(fd->libmap, bhead->old, id, bhead->code); - if (!force_indirect && (id->tag & LIB_TAG_INDIRECT)) { - id->tag &= ~LIB_TAG_INDIRECT; - id->flag &= ~LIB_INDIRECT_WEAK_LINK; - id->tag |= LIB_TAG_EXTERN; - } - } - } - else if (use_placeholders) { - /* XXX flag part is weak! */ - id = create_placeholder( - mainl, idcode, name, force_indirect ? LIB_TAG_INDIRECT : LIB_TAG_EXTERN); - } - else { - id = NULL; - } - - /* if we found the id but the id is NULL, this is really bad */ - BLI_assert(!((bhead != NULL) && (id == NULL))); - - return id; -} - -ID *BLO_library_link_named_part(Main *mainl, - BlendHandle **bh, - const short idcode, - const char *name, - const struct LibraryLink_Params *params) -{ - FileData *fd = (FileData *)(*bh); - return link_named_part(mainl, fd, idcode, name, params->flag); -} - -/* common routine to append/link something from a library */ - -static Main *library_link_begin(Main *mainvar, - FileData **fd, - const char *filepath, - const int id_tag_extra) -{ - Main *mainl; - - /* Only allow specific tags to be set as extra, - * otherwise this could conflict with library loading logic. - * Other flags can be added here, as long as they are safe. */ - BLI_assert((id_tag_extra & ~LIB_TAG_TEMP_MAIN) == 0); - - (*fd)->id_tag_extra = id_tag_extra; - - (*fd)->mainlist = MEM_callocN(sizeof(ListBase), "FileData.mainlist"); - - /* make mains */ - blo_split_main((*fd)->mainlist, mainvar); - - /* which one do we need? */ - mainl = blo_find_main(*fd, filepath, BKE_main_blendfile_path(mainvar)); - - /* needed for do_version */ - mainl->versionfile = (*fd)->fileversion; - read_file_version(*fd, mainl); -#ifdef USE_GHASH_BHEAD - read_file_bhead_idname_map_create(*fd); -#endif - - return mainl; -} - -void BLO_library_link_params_init(struct LibraryLink_Params *params, - struct Main *bmain, - const int flag, - const int id_tag_extra) -{ - memset(params, 0, sizeof(*params)); - params->bmain = bmain; - params->flag = flag; - params->id_tag_extra = id_tag_extra; -} - -void BLO_library_link_params_init_with_context(struct LibraryLink_Params *params, - struct Main *bmain, - const int flag, - const int id_tag_extra, - /* Context arguments. */ - struct Scene *scene, - struct ViewLayer *view_layer, - const struct View3D *v3d) -{ - BLO_library_link_params_init(params, bmain, flag, id_tag_extra); - if (scene != NULL) { - params->context.scene = scene; - params->context.view_layer = view_layer; - params->context.v3d = v3d; - } -} - -Main *BLO_library_link_begin(BlendHandle **bh, - const char *filepath, - const struct LibraryLink_Params *params) -{ - FileData *fd = (FileData *)(*bh); - return library_link_begin(params->bmain, &fd, filepath, params->id_tag_extra); -} - -static void split_main_newid(Main *mainptr, Main *main_newid) -{ - /* We only copy the necessary subset of data in this temp main. */ - main_newid->versionfile = mainptr->versionfile; - main_newid->subversionfile = mainptr->subversionfile; - STRNCPY(main_newid->filepath, mainptr->filepath); - main_newid->curlib = mainptr->curlib; - - ListBase *lbarray[INDEX_ID_MAX]; - ListBase *lbarray_newid[INDEX_ID_MAX]; - int i = set_listbasepointers(mainptr, lbarray); - set_listbasepointers(main_newid, lbarray_newid); - while (i--) { - BLI_listbase_clear(lbarray_newid[i]); - - LISTBASE_FOREACH_MUTABLE (ID *, id, lbarray[i]) { - if (id->tag & LIB_TAG_NEW) { - BLI_remlink(lbarray[i], id); - BLI_addtail(lbarray_newid[i], id); - } - } - } -} - -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); - } - - /* expander now is callback function */ - BLO_main_expander(expand_doit_library); - - /* make main consistent */ - BLO_expand_main(*fd, mainl); - - /* do this when expand found other libs */ - read_libraries(*fd, (*fd)->mainlist); - - curlib = mainl->curlib; - - /* make the lib path relative if required */ - if (flag & FILE_RELPATH) { - /* use the full path, this could have been read by other library even */ - BLI_strncpy(curlib->filepath, curlib->filepath_abs, sizeof(curlib->filepath)); - - /* uses current .blend file as reference */ - BLI_path_rel(curlib->filepath, BKE_main_blendfile_path_from_global()); - } - - blo_join_main((*fd)->mainlist); - mainvar = (*fd)->mainlist->first; - mainl = NULL; /* blo_join_main free's mainl, can't use anymore */ - - lib_link_all(*fd, mainvar); - after_liblink_merged_bmain_process(mainvar); - - /* Some versioning code does expect some proper userrefcounting, e.g. in conversion from - * groups to collections... We could optimize out that first call when we are reading a - * current version file, but again this is really not a bottle neck currently. so not worth - * it. */ - BKE_main_id_refcount_recompute(mainvar, false); - - BKE_collections_after_lib_link(mainvar); - - /* Yep, second splitting... but this is a very cheap operation, so no big deal. */ - blo_split_main((*fd)->mainlist, mainvar); - Main *main_newid = BKE_main_new(); - for (mainvar = ((Main *)(*fd)->mainlist->first)->next; mainvar; mainvar = mainvar->next) { - BLI_assert(mainvar->versionfile != 0); - /* We need to split out IDs already existing, - * or they will go again through do_versions - bad, very bad! */ - split_main_newid(mainvar, main_newid); - - do_versions_after_linking(main_newid, (*fd)->reports->reports); - - add_main_to_main(mainvar, main_newid); - } - - blo_join_main((*fd)->mainlist); - mainvar = (*fd)->mainlist->first; - MEM_freeN((*fd)->mainlist); - - /* This does not take into account old, deprecated data, so we also have to do it after - * `do_versions_after_linking()`. */ - BKE_main_id_refcount_recompute(mainvar, false); - - /* After all data has been read and versioned, uses LIB_TAG_NEW. */ - ntreeUpdateAllNew(mainvar); - - placeholders_ensure_valid(mainvar); - - /* Apply overrides of newly linked data if needed. Already existing IDs need to split out, to - * avoid re-applying their own overrides. */ - BLI_assert(BKE_main_is_empty(main_newid)); - split_main_newid(mainvar, main_newid); - BKE_lib_override_library_main_validate(main_newid, (*fd)->reports->reports); - BKE_lib_override_library_main_update(main_newid); - add_main_to_main(mainvar, main_newid); - BKE_main_free(main_newid); - - BKE_main_id_tag_all(mainvar, LIB_TAG_NEW, false); - - /* Make all relative paths, relative to the open blend file. */ - fix_relpaths_library(BKE_main_blendfile_path(mainvar), mainvar); - - /* patch to prevent switch_endian happens twice */ - if ((*fd)->flags & FD_FLAGS_SWITCH_ENDIAN) { - blo_filedata_free(*fd); - *fd = NULL; - } -} - -void BLO_library_link_end(Main *mainl, BlendHandle **bh, const struct LibraryLink_Params *params) -{ - FileData *fd = (FileData *)(*bh); - library_link_end(mainl, &fd, params->flag); - *bh = (BlendHandle *)fd; -} - -void *BLO_library_read_struct(FileData *fd, BHead *bh, const char *blockname) -{ - return read_struct(fd, bh, blockname); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Library Reading - * \{ */ - -static int has_linked_ids_to_read(Main *mainvar) -{ - ListBase *lbarray[INDEX_ID_MAX]; - int a = set_listbasepointers(mainvar, lbarray); - - while (a--) { - LISTBASE_FOREACH (ID *, id, lbarray[a]) { - if ((id->tag & LIB_TAG_ID_LINK_PLACEHOLDER) && !(id->flag & LIB_INDIRECT_WEAK_LINK)) { - return true; - } - } - } - - return false; -} - -static void read_library_linked_id( - FileData *basefd, FileData *fd, Main *mainvar, ID *id, ID **r_id) -{ - BHead *bhead = NULL; - const bool is_valid = BKE_idtype_idcode_is_linkable(GS(id->name)) || - ((id->tag & LIB_TAG_EXTERN) == 0); - - if (fd) { - bhead = find_bhead_from_idname(fd, id->name); - } - - if (!is_valid) { - BLO_reportf_wrap(basefd->reports, - RPT_ERROR, - TIP_("LIB: %s: '%s' is directly linked from '%s' (parent '%s'), but is a " - "non-linkable data type"), - BKE_idtype_idcode_to_name(GS(id->name)), - id->name + 2, - mainvar->curlib->filepath_abs, - library_parent_filepath(mainvar->curlib)); - } - - id->tag &= ~LIB_TAG_ID_LINK_PLACEHOLDER; - id->flag &= ~LIB_INDIRECT_WEAK_LINK; - - if (bhead) { - id->tag |= LIB_TAG_NEED_EXPAND; - // printf("read lib block %s\n", id->name); - read_libblock(fd, mainvar, bhead, id->tag, false, r_id); - } - else { - BLO_reportf_wrap(basefd->reports, - RPT_INFO, - TIP_("LIB: %s: '%s' missing from '%s', parent '%s'"), - BKE_idtype_idcode_to_name(GS(id->name)), - id->name + 2, - mainvar->curlib->filepath_abs, - library_parent_filepath(mainvar->curlib)); - basefd->reports->count.missing_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; - } - } -} - -static void read_library_linked_ids(FileData *basefd, - FileData *fd, - ListBase *mainlist, - Main *mainvar) -{ - GHash *loaded_ids = BLI_ghash_str_new(__func__); - - ListBase *lbarray[INDEX_ID_MAX]; - int a = set_listbasepointers(mainvar, lbarray); - - while (a--) { - ID *id = lbarray[a]->first; - ListBase pending_free_ids = {NULL}; - - while (id) { - ID *id_next = id->next; - if ((id->tag & LIB_TAG_ID_LINK_PLACEHOLDER) && !(id->flag & LIB_INDIRECT_WEAK_LINK)) { - BLI_remlink(lbarray[a], id); - if (mainvar->id_map != NULL) { - BKE_main_idmap_remove_id(mainvar->id_map, id); - } - - /* When playing with lib renaming and such, you may end with cases where - * you have more than one linked ID of the same data-block from same - * 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; - 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); - - /* 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 - * libraries since multiple might be referencing this ID. */ - change_link_placeholder_to_real_ID_pointer(mainlist, basefd, id, *realid); - - /* We cannot free old lib-ref placeholder ID here anymore, since we use - * its name as key in loaded_ids hash. */ - BLI_addtail(&pending_free_ids, id); - } - id = id_next; - } - - /* Clear GHash and free link placeholder IDs of the current type. */ - BLI_ghash_clear(loaded_ids, NULL, NULL); - BLI_freelistN(&pending_free_ids); - } - - BLI_ghash_free(loaded_ids, NULL, NULL); -} - -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. */ - ListBase *lbarray[INDEX_ID_MAX]; - int a = set_listbasepointers(mainvar, lbarray); - - while (a--) { - ID *id = lbarray[a]->first; - - while (id) { - ID *id_next = 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); - BLI_freelinkN(lbarray[a], id); - } - id = id_next; - } - } -} - -static FileData *read_library_file_data(FileData *basefd, - ListBase *mainlist, - Main *mainl, - Main *mainptr) -{ - FileData *fd = mainptr->curlib->filedata; - - if (fd != NULL) { - /* File already open. */ - return fd; - } - - if (mainptr->curlib->packedfile) { - /* Read packed file. */ - PackedFile *pf = mainptr->curlib->packedfile; - - BLO_reportf_wrap(basefd->reports, - RPT_INFO, - TIP_("Read packed library: '%s', parent '%s'"), - mainptr->curlib->filepath, - library_parent_filepath(mainptr->curlib)); - fd = blo_filedata_from_memory(pf->data, pf->size, basefd->reports); - - /* Needed for library_append and read_libraries. */ - BLI_strncpy(fd->relabase, mainptr->curlib->filepath_abs, sizeof(fd->relabase)); - } - else { - /* Read file on disk. */ - BLO_reportf_wrap(basefd->reports, - RPT_INFO, - TIP_("Read library: '%s', '%s', parent '%s'"), - mainptr->curlib->filepath_abs, - mainptr->curlib->filepath, - library_parent_filepath(mainptr->curlib)); - fd = blo_filedata_from_file(mainptr->curlib->filepath_abs, basefd->reports); - } - - if (fd) { - /* Share the mainlist, so all libraries are added immediately in a - * single list. It used to be that all FileData's had their own list, - * but with indirectly linking this meant we didn't catch duplicate - * libraries properly. */ - fd->mainlist = mainlist; - - fd->reports = basefd->reports; - - if (fd->libmap) { - oldnewmap_free(fd->libmap); - } - - fd->libmap = oldnewmap_new(); - - mainptr->curlib->filedata = fd; - mainptr->versionfile = fd->fileversion; - - /* subversion */ - read_file_version(fd, mainptr); -#ifdef USE_GHASH_BHEAD - read_file_bhead_idname_map_create(fd); -#endif - } - else { - mainptr->curlib->filedata = NULL; - 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) { - BLO_reportf_wrap( - basefd->reports, RPT_INFO, TIP_("Cannot find lib '%s'"), mainptr->curlib->filepath_abs); - basefd->reports->count.missing_libraries++; - } - - return fd; -} - -static void read_libraries(FileData *basefd, ListBase *mainlist) -{ - Main *mainl = mainlist->first; - bool do_it = true; - - /* Expander is now callback function. */ - BLO_main_expander(expand_doit_library); - - /* At this point the base blend file has been read, and each library blend - * encountered so far has a main with placeholders for linked data-blocks. - * - * Now we will read the library blend files and replace the placeholders - * with actual data-blocks. We loop over library mains multiple times in - * case a library needs to link additional data-blocks from another library - * that had been read previously. */ - while (do_it) { - do_it = false; - - /* Loop over mains of all library blend files encountered so far. Note - * this list gets longer as more indirectly library blends are found. */ - for (Main *mainptr = mainl->next; mainptr; mainptr = mainptr->next) { - /* Does this library have any more linked data-blocks we need to read? */ - if (has_linked_ids_to_read(mainptr)) { - CLOG_INFO(&LOG, - 3, - "Reading linked data-blocks from %s (%s)", - mainptr->curlib->id.name, - mainptr->curlib->filepath); - - /* Open file if it has not been done yet. */ - FileData *fd = read_library_file_data(basefd, mainlist, mainl, mainptr); - - if (fd) { - do_it = true; - - if (mainptr->id_map == NULL) { - mainptr->id_map = BKE_main_idmap_create(mainptr, false, NULL, MAIN_IDMAP_TYPE_NAME); - } - } - - /* Read linked data-locks for each link placeholder, and replace - * the placeholder with the real data-lock. */ - read_library_linked_ids(basefd, fd, mainlist, mainptr); - - /* Test if linked data-locks need to read further linked data-locks - * 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. */ - read_library_clear_weak_links(basefd, mainlist, mainptr); - - /* Do versioning for newly added linked data-locks. If no data-locks - * were read from a library versionfile will still be zero and we can - * skip it. */ - if (mainptr->versionfile) { - /* Split out already existing IDs to avoid them going through - * do_versions multiple times, which would have bad consequences. */ - split_main_newid(mainptr, main_newid); - - /* File data can be zero with link/append. */ - if (mainptr->curlib->filedata) { - do_versions(mainptr->curlib->filedata, mainptr->curlib, main_newid); - } - else { - do_versions(basefd, NULL, main_newid); - } - - add_main_to_main(mainptr, main_newid); - } - - /* Lib linking. */ - if (mainptr->curlib->filedata) { - lib_link_all(mainptr->curlib->filedata, mainptr); - } - - /* NOTE: No need to call #do_versions_after_linking() or #BKE_main_id_refcount_recompute() - * here, as this function is only called for library 'subset' data handling, as part of - * either full blendfile reading (#blo_read_file_internal()), or library-data linking - * (#library_link_end()). */ - - /* Free file data we no longer need. */ - if (mainptr->curlib->filedata) { - blo_filedata_free(mainptr->curlib->filedata); - } - mainptr->curlib->filedata = NULL; - } - BKE_main_free(main_newid); -} - -void *BLO_read_get_new_data_address(BlendDataReader *reader, const void *old_address) -{ - return newdataadr(reader->fd, old_address); -} - -void *BLO_read_get_new_data_address_no_us(BlendDataReader *reader, const void *old_address) -{ - return newdataadr_no_us(reader->fd, old_address); -} - -void *BLO_read_get_new_packed_address(BlendDataReader *reader, const void *old_address) -{ - return newpackedadr(reader->fd, old_address); -} - -ID *BLO_read_get_new_id_address(BlendLibReader *reader, Library *lib, ID *id) -{ - return newlibadr(reader->fd, lib, id); -} - -bool BLO_read_requires_endian_switch(BlendDataReader *reader) -{ - return (reader->fd->flags & FD_FLAGS_SWITCH_ENDIAN) != 0; -} - -void BLO_read_list_cb(BlendDataReader *reader, ListBase *list, BlendReadListFn callback) -{ - if (BLI_listbase_is_empty(list)) { - return; - } - - BLO_read_data_address(reader, &list->first); - if (callback != NULL) { - callback(reader, list->first); - } - Link *ln = list->first; - Link *prev = NULL; - while (ln) { - BLO_read_data_address(reader, &ln->next); - if (ln->next != NULL && callback != NULL) { - callback(reader, ln->next); - } - ln->prev = prev; - prev = ln; - ln = ln->next; - } - list->last = prev; -} - -void BLO_read_list(BlendDataReader *reader, struct ListBase *list) -{ - BLO_read_list_cb(reader, list, NULL); -} - -void BLO_read_int32_array(BlendDataReader *reader, int array_size, int32_t **ptr_p) -{ - BLO_read_data_address(reader, ptr_p); - if (BLO_read_requires_endian_switch(reader)) { - BLI_endian_switch_int32_array(*ptr_p, array_size); - } -} - -void BLO_read_uint32_array(BlendDataReader *reader, int array_size, uint32_t **ptr_p) -{ - BLO_read_data_address(reader, ptr_p); - if (BLO_read_requires_endian_switch(reader)) { - BLI_endian_switch_uint32_array(*ptr_p, array_size); - } -} - -void BLO_read_float_array(BlendDataReader *reader, int array_size, float **ptr_p) -{ - BLO_read_data_address(reader, ptr_p); - if (BLO_read_requires_endian_switch(reader)) { - BLI_endian_switch_float_array(*ptr_p, array_size); - } -} - -void BLO_read_float3_array(BlendDataReader *reader, int array_size, float **ptr_p) -{ - BLO_read_float_array(reader, array_size * 3, ptr_p); -} - -void BLO_read_double_array(BlendDataReader *reader, int array_size, double **ptr_p) -{ - BLO_read_data_address(reader, ptr_p); - if (BLO_read_requires_endian_switch(reader)) { - BLI_endian_switch_double_array(*ptr_p, array_size); - } -} - -static void convert_pointer_array_64_to_32(BlendDataReader *reader, - uint array_size, - const uint64_t *src, - uint32_t *dst) -{ - /* Match pointer conversion rules from bh4_from_bh8 and cast_pointer. */ - if (BLO_read_requires_endian_switch(reader)) { - for (int i = 0; i < array_size; i++) { - uint64_t ptr = src[i]; - BLI_endian_switch_uint64(&ptr); - dst[i] = (uint32_t)(ptr >> 3); - } - } - else { - for (int i = 0; i < array_size; i++) { - dst[i] = (uint32_t)(src[i] >> 3); - } - } -} - -static void convert_pointer_array_32_to_64(BlendDataReader *UNUSED(reader), - uint array_size, - const uint32_t *src, - uint64_t *dst) -{ - /* Match pointer conversion rules from bh8_from_bh4 and cast_pointer_32_to_64. */ - for (int i = 0; i < array_size; i++) { - dst[i] = src[i]; - } -} - -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; - return; - } - - int file_pointer_size = fd->filesdna->pointer_size; - int current_pointer_size = fd->memsdna->pointer_size; - - /* 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; - - if (file_pointer_size == current_pointer_size) { - /* No pointer conversion necessary. */ - final_array = orig_array; - } - else if (file_pointer_size == 8 && current_pointer_size == 4) { - /* Convert pointers from 64 to 32 bit. */ - final_array = MEM_malloc_arrayN(array_size, 4, "new pointer array"); - convert_pointer_array_64_to_32( - reader, array_size, (uint64_t *)orig_array, (uint32_t *)final_array); - MEM_freeN(orig_array); - } - else if (file_pointer_size == 4 && current_pointer_size == 8) { - /* Convert pointers from 32 to 64 bit. */ - final_array = MEM_malloc_arrayN(array_size, 8, "new pointer array"); - convert_pointer_array_32_to_64( - reader, array_size, (uint32_t *)orig_array, (uint64_t *)final_array); - MEM_freeN(orig_array); - } - else { - BLI_assert(false); - } - - *ptr_p = final_array; -} - -bool BLO_read_data_is_undo(BlendDataReader *reader) -{ - return (reader->fd->flags & FD_FLAGS_IS_MEMFILE); -} - -void BLO_read_data_globmap_add(BlendDataReader *reader, void *oldaddr, void *newaddr) -{ - oldnewmap_insert(reader->fd->globmap, oldaddr, newaddr, 0); -} - -void BLO_read_glob_list(BlendDataReader *reader, ListBase *list) -{ - link_glob_list(reader->fd, list); -} - -BlendFileReadReport *BLO_read_data_reports(BlendDataReader *reader) -{ - return reader->fd->reports; -} - -bool BLO_read_lib_is_undo(BlendLibReader *reader) -{ - return (reader->fd->flags & FD_FLAGS_IS_MEMFILE); -} - -Main *BLO_read_lib_get_main(BlendLibReader *reader) -{ - return reader->main; -} - -BlendFileReadReport *BLO_read_lib_reports(BlendLibReader *reader) -{ - return reader->fd->reports; -} - -void BLO_expand_id(BlendExpander *expander, ID *id) -{ - expand_doit(expander->fd, expander->main, id); -} - -/** \} */ diff --git a/source/blender/blenloader/intern/readfile.cc b/source/blender/blenloader/intern/readfile.cc new file mode 100644 index 00000000000..71708c09c01 --- /dev/null +++ b/source/blender/blenloader/intern/readfile.cc @@ -0,0 +1,5211 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2001-2002 NaN Holding BV. All rights reserved. */ + +/** \file + * \ingroup blenloader + */ + +#include /* for isdigit. */ +#include /* for open flags (O_BINARY, O_RDONLY). */ +#include +#include /* for va_start/end. */ +#include /* for offsetof. */ +#include /* for atoi. */ +#include /* for gmtime. */ + +#include "BLI_utildefines.h" +#ifndef WIN32 +# include /* for read close */ +#else +# include "BLI_winstuff.h" +# include "winsock2.h" +# include /* for open close read */ +#endif + +#include "CLG_log.h" + +/* allow readfile to use deprecated functionality */ +#define DNA_DEPRECATED_ALLOW + +#include "DNA_anim_types.h" +#include "DNA_asset_types.h" +#include "DNA_cachefile_types.h" +#include "DNA_collection_types.h" +#include "DNA_fileglobal_types.h" +#include "DNA_genfile.h" +#include "DNA_key_types.h" +#include "DNA_layer_types.h" +#include "DNA_node_types.h" +#include "DNA_packedFile_types.h" +#include "DNA_sdna_types.h" +#include "DNA_sound_types.h" +#include "DNA_vfont_types.h" +#include "DNA_volume_types.h" +#include "DNA_workspace_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_endian_defines.h" +#include "BLI_endian_switch.h" +#include "BLI_ghash.h" +#include "BLI_linklist.h" +#include "BLI_math.h" +#include "BLI_memarena.h" +#include "BLI_mempool.h" +#include "BLI_threads.h" + +#include "PIL_time.h" + +#include "BLT_translation.h" + +#include "BKE_anim_data.h" +#include "BKE_animsys.h" +#include "BKE_asset.h" +#include "BKE_collection.h" +#include "BKE_global.h" /* for G */ +#include "BKE_idprop.h" +#include "BKE_idtype.h" +#include "BKE_layer.h" +#include "BKE_lib_id.h" +#include "BKE_lib_override.h" +#include "BKE_lib_query.h" +#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" +#include "BKE_packedFile.h" +#include "BKE_report.h" +#include "BKE_scene.h" +#include "BKE_screen.h" +#include "BKE_undo_system.h" +#include "BKE_workspace.h" + +#include "DRW_engine.h" + +#include "DEG_depsgraph.h" + +#include "BLO_blend_defs.h" +#include "BLO_blend_validate.h" +#include "BLO_read_write.h" +#include "BLO_readfile.h" +#include "BLO_undofile.h" + +#include "SEQ_clipboard.h" +#include "SEQ_iterator.h" +#include "SEQ_modifier.h" +#include "SEQ_sequencer.h" +#include "SEQ_utils.h" + +#include "readfile.h" + +#include + +/* Make preferences read-only. */ +#define U (*((const UserDef *)&U)) + +/** + * READ + * ==== + * + * - Existing Library (#Main) push or free + * - allocate new #Main + * - load file + * - read #SDNA + * - for each LibBlock + * - read LibBlock + * - if a Library + * - make a new #Main + * - attach ID's to it + * - else + * - read associated 'direct data' + * - link direct data (internal and to LibBlock) + * - read #FileGlobal + * - read #USER data, only when indicated (file is `~/.config/blender/X.XX/config/userpref.blend`) + * - free file + * - per Library (per #Main) + * - read file + * - read #SDNA + * - find LibBlocks and attach #ID's to #Main + * - if external LibBlock + * - search all #Main's + * - or it's already read, + * - or not read yet + * - or make new #Main + * - per LibBlock + * - read recursive + * - read associated direct data + * - link direct data (internal and to LibBlock) + * - free file + * - per Library with unread LibBlocks + * - read file + * - read #SDNA + * - per LibBlock + * - read recursive + * - read associated direct data + * - link direct data (internal and to LibBlock) + * - free file + * - join all #Main's + * - link all LibBlocks and indirect pointers to libblocks + * - initialize #FileGlobal and copy pointers to #Global + * + * \note Still a weak point is the new-address function, that doesn't solve reading from + * multiple files at the same time. + * (added remark: oh, i thought that was solved? will look at that... (ton). + */ + +/** + * Delay reading blocks we might not use (especially applies to library linking). + * which keeps large arrays in memory from data-blocks we may not even use. + * + * \note This is disabled when using compression, + * while ZLIB supports seek it's unusably slow, see: T61880. + */ +#define USE_BHEAD_READ_ON_DEMAND + +/** Use #GHash for #BHead name-based lookups (speeds up linking). */ +#define USE_GHASH_BHEAD + +/** Use #GHash for restoring pointers by name. */ +#define USE_GHASH_RESTORE_POINTER + +static CLG_LogRef LOG = {"blo.readfile"}; +static CLG_LogRef LOG_UNDO = {"blo.readfile.undo"}; + +/* local prototypes */ +static void read_libraries(FileData *basefd, ListBase *mainlist); +static void *read_struct(FileData *fd, BHead *bh, const char *blockname); +static BHead *find_bhead_from_code_name(FileData *fd, const short idcode, const char *name); +static BHead *find_bhead_from_idname(FileData *fd, const char *idname); + +typedef struct BHeadN { + struct BHeadN *next, *prev; +#ifdef USE_BHEAD_READ_ON_DEMAND + /** Use to read the data from the file directly into memory as needed. */ + off64_t file_offset; + /** When set, the remainder of this allocation is the data, otherwise it needs to be read. */ + bool has_data; +#endif + bool is_memchunk_identical; + struct BHead bhead; +} BHeadN; + +#define BHEADN_FROM_BHEAD(bh) ((BHeadN *)POINTER_OFFSET(bh, -(int)offsetof(BHeadN, bhead))) + +/** + * We could change this in the future, for now it's simplest if only data is delayed + * because ID names are used in lookup tables. + */ +#define BHEAD_USE_READ_ON_DEMAND(bhead) ((bhead)->code == DATA) + +void BLO_reportf_wrap(BlendFileReadReport *reports, eReportType type, const char *format, ...) +{ + char fixed_buf[1024]; /* should be long enough */ + + va_list args; + + va_start(args, format); + vsnprintf(fixed_buf, sizeof(fixed_buf), format, args); + va_end(args); + + fixed_buf[sizeof(fixed_buf) - 1] = '\0'; + + BKE_report(reports->reports, type, fixed_buf); + + if (G.background == 0) { + printf("%s: %s\n", BKE_report_type_str(type), fixed_buf); + } +} + +/* for reporting linking messages */ +static const char *library_parent_filepath(Library *lib) +{ + return lib->parent ? lib->parent->filepath_abs : ""; +} + +/* -------------------------------------------------------------------- */ +/** \name OldNewMap API + * \{ */ + +typedef struct OldNew { + const void *oldp; + void *newp; + /* `nr` is "user count" for data, and ID code for libdata. */ + int nr; +} OldNew; + +typedef struct OldNewMap { + /* Array that stores the actual entries. */ + OldNew *entries; + int nentries; + /* Hash-map that stores indices into the `entries` array. */ + int32_t *map; + + int capacity_exp; +} OldNewMap; + +#define ENTRIES_CAPACITY(onm) (1ll << (onm)->capacity_exp) +#define MAP_CAPACITY(onm) (1ll << ((onm)->capacity_exp + 1)) +#define SLOT_MASK(onm) (MAP_CAPACITY(onm) - 1) +#define DEFAULT_SIZE_EXP 6 +#define PERTURB_SHIFT 5 + +/* based on the probing algorithm used in Python dicts. */ +#define ITER_SLOTS(onm, KEY, SLOT_NAME, INDEX_NAME) \ + uint32_t hash = BLI_ghashutil_ptrhash(KEY); \ + uint32_t mask = SLOT_MASK(onm); \ + uint perturb = hash; \ + int SLOT_NAME = mask & hash; \ + int INDEX_NAME = onm->map[SLOT_NAME]; \ + for (;; SLOT_NAME = mask & ((5 * SLOT_NAME) + 1 + perturb), \ + perturb >>= PERTURB_SHIFT, \ + INDEX_NAME = onm->map[SLOT_NAME]) + +static void oldnewmap_insert_index_in_map(OldNewMap *onm, const void *ptr, int index) +{ + ITER_SLOTS (onm, ptr, slot, stored_index) { + if (stored_index == -1) { + onm->map[slot] = index; + break; + } + } +} + +static void oldnewmap_insert_or_replace(OldNewMap *onm, OldNew entry) +{ + ITER_SLOTS (onm, entry.oldp, slot, index) { + if (index == -1) { + onm->entries[onm->nentries] = entry; + onm->map[slot] = onm->nentries; + onm->nentries++; + break; + } + if (onm->entries[index].oldp == entry.oldp) { + onm->entries[index] = entry; + break; + } + } +} + +static OldNew *oldnewmap_lookup_entry(const OldNewMap *onm, const void *addr) +{ + ITER_SLOTS (onm, addr, slot, index) { + if (index >= 0) { + OldNew *entry = &onm->entries[index]; + if (entry->oldp == addr) { + return entry; + } + } + else { + return nullptr; + } + } +} + +static void oldnewmap_clear_map(OldNewMap *onm) +{ + memset(onm->map, 0xFF, MAP_CAPACITY(onm) * sizeof(*onm->map)); +} + +static void oldnewmap_increase_size(OldNewMap *onm) +{ + onm->capacity_exp++; + onm->entries = static_cast( + MEM_reallocN(onm->entries, sizeof(*onm->entries) * ENTRIES_CAPACITY(onm))); + onm->map = static_cast(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); + } +} + +/* Public OldNewMap API */ + +static void oldnewmap_init_data(OldNewMap *onm, const int capacity_exp) +{ + memset(onm, 0x0, sizeof(*onm)); + + onm->capacity_exp = capacity_exp; + onm->entries = static_cast( + MEM_malloc_arrayN(ENTRIES_CAPACITY(onm), sizeof(*onm->entries), "OldNewMap.entries")); + onm->map = static_cast( + MEM_malloc_arrayN(MAP_CAPACITY(onm), sizeof(*onm->map), "OldNewMap.map")); + oldnewmap_clear_map(onm); +} + +static OldNewMap *oldnewmap_new(void) +{ + OldNewMap *onm = static_cast(MEM_mallocN(sizeof(*onm), "OldNewMap")); + + oldnewmap_init_data(onm, DEFAULT_SIZE_EXP); + + return onm; +} + +static void oldnewmap_insert(OldNewMap *onm, const void *oldaddr, void *newaddr, int nr) +{ + if (oldaddr == nullptr || newaddr == nullptr) { + return; + } + + if (UNLIKELY(onm->nentries == ENTRIES_CAPACITY(onm))) { + oldnewmap_increase_size(onm); + } + + OldNew entry; + entry.oldp = oldaddr; + entry.newp = newaddr; + entry.nr = nr; + 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); +} + +static void *oldnewmap_lookup_and_inc(OldNewMap *onm, const void *addr, bool increase_users) +{ + OldNew *entry = oldnewmap_lookup_entry(onm, addr); + if (entry == nullptr) { + return nullptr; + } + if (increase_users) { + entry->nr++; + } + return entry->newp; +} + +/* for libdata, OldNew.nr has ID code, no increment */ +static void *oldnewmap_liblookup(OldNewMap *onm, const void *addr, const void *lib) +{ + if (addr == nullptr) { + return nullptr; + } + + ID *id = static_cast(oldnewmap_lookup_and_inc(onm, addr, false)); + if (id == nullptr) { + return nullptr; + } + if (!lib || id->lib) { + return id; + } + return nullptr; +} + +static void oldnewmap_clear(OldNewMap *onm) +{ + /* Free unused data. */ + for (int i = 0; i < onm->nentries; i++) { + OldNew *entry = &onm->entries[i]; + if (entry->nr == 0) { + MEM_freeN(entry->newp); + entry->newp = nullptr; + } + } + + MEM_freeN(onm->entries); + MEM_freeN(onm->map); + + oldnewmap_init_data(onm, DEFAULT_SIZE_EXP); +} + +static void oldnewmap_free(OldNewMap *onm) +{ + MEM_freeN(onm->entries); + MEM_freeN(onm->map); + MEM_freeN(onm); +} + +#undef ENTRIES_CAPACITY +#undef MAP_CAPACITY +#undef SLOT_MASK +#undef DEFAULT_SIZE_EXP +#undef PERTURB_SHIFT +#undef ITER_SLOTS + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Helper Functions + * \{ */ + +static void add_main_to_main(Main *mainvar, Main *from) +{ + ListBase *lbarray[INDEX_ID_MAX], *fromarray[INDEX_ID_MAX]; + int a; + + set_listbasepointers(mainvar, lbarray); + a = set_listbasepointers(from, fromarray); + while (a--) { + BLI_movelisttolist(lbarray[a], fromarray[a]); + } +} + +void blo_join_main(ListBase *mainlist) +{ + Main *tojoin, *mainl; + + mainl = static_cast
(mainlist->first); + + 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 = nullptr; + } + + while ((tojoin = mainl->next)) { + add_main_to_main(mainl, tojoin); + BLI_remlink(mainlist, tojoin); + BKE_main_free(tojoin); + } +} + +static void split_libdata(ListBase *lb_src, Main **lib_main_array, const uint lib_main_array_len) +{ + for (ID *id = static_cast(lb_src->first), *idnext; id; id = idnext) { + idnext = static_cast(id->next); + + if (id->lib) { + if (((uint)id->lib->temp_index < lib_main_array_len) && + /* this check should never fail, just in case 'id->lib' is a dangling pointer. */ + (lib_main_array[id->lib->temp_index]->curlib == id->lib)) { + Main *mainvar = lib_main_array[id->lib->temp_index]; + ListBase *lb_dst = which_libbase(mainvar, GS(id->name)); + BLI_remlink(lb_src, id); + BLI_addtail(lb_dst, id); + } + else { + CLOG_ERROR(&LOG, "Invalid library for '%s'", id->name); + } + } + } +} + +void blo_split_main(ListBase *mainlist, Main *main) +{ + mainlist->first = mainlist->last = main; + main->next = nullptr; + + if (BLI_listbase_is_empty(&main->libraries)) { + return; + } + + 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 = nullptr; + } + + /* (Library.temp_index -> Main), lookup table */ + const uint lib_main_array_len = BLI_listbase_count(&main->libraries); + Main **lib_main_array = static_cast
( + MEM_malloc_arrayN(lib_main_array_len, sizeof(*lib_main_array), __func__)); + + int i = 0; + for (Library *lib = static_cast(main->libraries.first); lib; + lib = static_cast(lib->id.next), i++) { + Main *libmain = BKE_main_new(); + libmain->curlib = lib; + libmain->versionfile = lib->versionfile; + libmain->subversionfile = lib->subversionfile; + BLI_addtail(mainlist, libmain); + lib->temp_index = i; + lib_main_array[i] = libmain; + } + + ListBase *lbarray[INDEX_ID_MAX]; + i = set_listbasepointers(main, lbarray); + while (i--) { + ID *id = static_cast(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); + } + + MEM_freeN(lib_main_array); +} + +static void read_file_version(FileData *fd, Main *main) +{ + BHead *bhead; + + for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) { + if (bhead->code == GLOB) { + FileGlobal *fg = static_cast(read_struct(fd, bhead, "Global")); + if (fg) { + main->subversionfile = fg->subversion; + main->minversionfile = fg->minversion; + main->minsubversionfile = fg->minsubversion; + MEM_freeN(fg); + } + else if (bhead->code == ENDB) { + break; + } + } + } + if (main->curlib) { + main->curlib->versionfile = main->versionfile; + main->curlib->subversionfile = main->subversionfile; + } +} + +static bool blo_bhead_is_id(const BHead *bhead) +{ + /* BHead codes are four bytes (like 'ENDB', 'TEST', etc.), but if the two most-significant bytes + * are zero, the values actually indicate an ID type. */ + return bhead->code <= 0xFFFF; +} + +static bool blo_bhead_is_id_valid_type(const BHead *bhead) +{ + if (!blo_bhead_is_id(bhead)) { + return false; + } + + const short id_type_code = bhead->code & 0xFFFF; + return BKE_idtype_idcode_is_valid(id_type_code); +} + +#ifdef USE_GHASH_BHEAD +static void read_file_bhead_idname_map_create(FileData *fd) +{ + BHead *bhead; + + /* dummy values */ + bool is_link = false; + int code_prev = ENDB; + uint reserve = 0; + + for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) { + if (code_prev != bhead->code) { + code_prev = bhead->code; + is_link = blo_bhead_is_id_valid_type(bhead) ? + BKE_idtype_idcode_is_linkable((short)code_prev) : + false; + } + + if (is_link) { + reserve += 1; + } + } + + BLI_assert(fd->bhead_idname_hash == nullptr); + + fd->bhead_idname_hash = BLI_ghash_str_new_ex(__func__, reserve); + + for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) { + if (code_prev != bhead->code) { + code_prev = bhead->code; + is_link = blo_bhead_is_id_valid_type(bhead) ? + BKE_idtype_idcode_is_linkable((short)code_prev) : + false; + } + + if (is_link) { + BLI_ghash_insert(fd->bhead_idname_hash, (void *)blo_bhead_id_name(fd, bhead), bhead); + } + } +} +#endif + +static Main *blo_find_main(FileData *fd, const char *filepath, const char *relabase) +{ + ListBase *mainlist = fd->mainlist; + Main *m; + Library *lib; + char name1[FILE_MAX]; + + BLI_strncpy(name1, filepath, sizeof(name1)); + BLI_path_normalize(relabase, name1); + + // printf("blo_find_main: relabase %s\n", relabase); + // printf("blo_find_main: original in %s\n", filepath); + // printf("blo_find_main: converted to %s\n", name1); + + for (m = static_cast
(mainlist->first); m; m = m->next) { + const char *libname = (m->curlib) ? m->curlib->filepath_abs : m->filepath; + + if (BLI_path_cmp(name1, libname) == 0) { + if (G.debug & G_DEBUG) { + CLOG_INFO(&LOG, 3, "Found library %s", libname); + } + return m; + } + } + + m = BKE_main_new(); + BLI_addtail(mainlist, m); + + /* 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 = static_cast(BKE_libblock_alloc( + static_cast
(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); + + /* Matches direct_link_library(). */ + id_us_ensure_real(&lib->id); + + BLI_strncpy(lib->filepath, filepath, sizeof(lib->filepath)); + BLI_strncpy(lib->filepath_abs, name1, sizeof(lib->filepath_abs)); + + m->curlib = lib; + + read_file_version(fd, m); + + if (G.debug & G_DEBUG) { + CLOG_INFO(&LOG, 3, "Added new lib %s", filepath); + } + return m; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name File Parsing + * \{ */ + +typedef struct BlendDataReader { + FileData *fd; +} BlendDataReader; + +typedef struct BlendLibReader { + FileData *fd; + Main *main; +} BlendLibReader; + +typedef struct BlendExpander { + FileData *fd; + Main *main; +} BlendExpander; + +static void switch_endian_bh4(BHead4 *bhead) +{ + /* the ID_.. codes */ + if ((bhead->code & 0xFFFF) == 0) { + bhead->code >>= 16; + } + + if (bhead->code != ENDB) { + BLI_endian_switch_int32(&bhead->len); + BLI_endian_switch_int32(&bhead->SDNAnr); + BLI_endian_switch_int32(&bhead->nr); + } +} + +static void switch_endian_bh8(BHead8 *bhead) +{ + /* the ID_.. codes */ + if ((bhead->code & 0xFFFF) == 0) { + bhead->code >>= 16; + } + + if (bhead->code != ENDB) { + BLI_endian_switch_int32(&bhead->len); + BLI_endian_switch_int32(&bhead->SDNAnr); + BLI_endian_switch_int32(&bhead->nr); + } +} + +static void bh4_from_bh8(BHead *bhead, BHead8 *bhead8, bool do_endian_swap) +{ + BHead4 *bhead4 = (BHead4 *)bhead; + int64_t old; + + bhead4->code = bhead8->code; + bhead4->len = bhead8->len; + + if (bhead4->code != ENDB) { + /* perform a endian swap on 64bit pointers, otherwise the pointer might map to zero + * 0x0000000000000000000012345678 would become 0x12345678000000000000000000000000 + */ + if (do_endian_swap) { + BLI_endian_switch_uint64(&bhead8->old); + } + + /* this patch is to avoid `intptr_t` being read from not-eight aligned positions + * is necessary on any modern 64bit architecture) */ + memcpy(&old, &bhead8->old, 8); + bhead4->old = (int)(old >> 3); + + bhead4->SDNAnr = bhead8->SDNAnr; + bhead4->nr = bhead8->nr; + } +} + +static void bh8_from_bh4(BHead *bhead, BHead4 *bhead4) +{ + BHead8 *bhead8 = (BHead8 *)bhead; + + bhead8->code = bhead4->code; + bhead8->len = bhead4->len; + + if (bhead8->code != ENDB) { + bhead8->old = bhead4->old; + bhead8->SDNAnr = bhead4->SDNAnr; + bhead8->nr = bhead4->nr; + } +} + +static BHeadN *get_bhead(FileData *fd) +{ + BHeadN *new_bhead = nullptr; + ssize_t readsize; + + if (fd) { + if (!fd->is_eof) { + /* initializing to zero isn't strictly needed but shuts valgrind up + * since uninitialized memory gets compared */ + BHead8 bhead8 = {0}; + BHead4 bhead4 = {0}; + BHead bhead = {0}; + + /* First read the bhead structure. + * Depending on the platform the file was written on this can + * be a big or little endian BHead4 or BHead8 structure. + * + * As usual 'ENDB' (the last *partial* bhead of the file) + * needs some special handling. We don't want to EOF just yet. + */ + if (fd->flags & FD_FLAGS_FILE_POINTSIZE_IS_4) { + bhead4.code = DATA; + readsize = fd->file->read(fd->file, &bhead4, sizeof(bhead4)); + + if (readsize == sizeof(bhead4) || bhead4.code == ENDB) { + if (fd->flags & FD_FLAGS_SWITCH_ENDIAN) { + switch_endian_bh4(&bhead4); + } + + if (fd->flags & FD_FLAGS_POINTSIZE_DIFFERS) { + bh8_from_bh4(&bhead, &bhead4); + } + else { + /* MIN2 is only to quiet '-Warray-bounds' compiler warning. */ + BLI_assert(sizeof(bhead) == sizeof(bhead4)); + memcpy(&bhead, &bhead4, MIN2(sizeof(bhead), sizeof(bhead4))); + } + } + else { + fd->is_eof = true; + bhead.len = 0; + } + } + else { + bhead8.code = DATA; + readsize = fd->file->read(fd->file, &bhead8, sizeof(bhead8)); + + if (readsize == sizeof(bhead8) || bhead8.code == ENDB) { + if (fd->flags & FD_FLAGS_SWITCH_ENDIAN) { + switch_endian_bh8(&bhead8); + } + + if (fd->flags & FD_FLAGS_POINTSIZE_DIFFERS) { + bh4_from_bh8(&bhead, &bhead8, (fd->flags & FD_FLAGS_SWITCH_ENDIAN) != 0); + } + else { + /* MIN2 is only to quiet '-Warray-bounds' compiler warning. */ + BLI_assert(sizeof(bhead) == sizeof(bhead8)); + memcpy(&bhead, &bhead8, MIN2(sizeof(bhead), sizeof(bhead8))); + } + } + else { + fd->is_eof = true; + bhead.len = 0; + } + } + + /* make sure people are not trying to pass bad blend files */ + if (bhead.len < 0) { + fd->is_eof = true; + } + + /* bhead now contains the (converted) bhead structure. Now read + * the associated data and put everything in a BHeadN (creative naming !) + */ + if (fd->is_eof) { + /* pass */ + } +#ifdef USE_BHEAD_READ_ON_DEMAND + else if (fd->file->seek != nullptr && BHEAD_USE_READ_ON_DEMAND(&bhead)) { + /* Delay reading bhead content. */ + new_bhead = static_cast(MEM_mallocN(sizeof(BHeadN), "new_bhead")); + if (new_bhead) { + 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; + new_bhead->bhead = bhead; + off64_t seek_new = fd->file->seek(fd->file, bhead.len, SEEK_CUR); + if (seek_new == -1) { + fd->is_eof = true; + MEM_freeN(new_bhead); + new_bhead = nullptr; + } + BLI_assert(fd->file->offset == seek_new); + } + else { + fd->is_eof = true; + } + } +#endif + else { + new_bhead = static_cast( + MEM_mallocN(sizeof(BHeadN) + (size_t)bhead.len, "new_bhead")); + if (new_bhead) { + 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; +#endif + new_bhead->is_memchunk_identical = false; + new_bhead->bhead = bhead; + + readsize = fd->file->read(fd->file, new_bhead + 1, (size_t)bhead.len); + + if (readsize != bhead.len) { + fd->is_eof = true; + MEM_freeN(new_bhead); + new_bhead = nullptr; + } + + if (fd->flags & FD_FLAGS_IS_MEMFILE) { + new_bhead->is_memchunk_identical = ((UndoReader *)fd->file)->memchunk_identical; + } + } + else { + fd->is_eof = true; + } + } + } + } + + /* We've read a new block. Now add it to the list + * of blocks. + */ + if (new_bhead) { + BLI_addtail(&fd->bhead_list, new_bhead); + } + + return new_bhead; +} + +BHead *blo_bhead_first(FileData *fd) +{ + BHeadN *new_bhead; + BHead *bhead = nullptr; + + /* Rewind the file + * Read in a new block if necessary + */ + new_bhead = static_cast(fd->bhead_list.first); + if (new_bhead == nullptr) { + new_bhead = get_bhead(fd); + } + + if (new_bhead) { + bhead = &new_bhead->bhead; + } + + return bhead; +} + +BHead *blo_bhead_prev(FileData *UNUSED(fd), BHead *thisblock) +{ + BHeadN *bheadn = BHEADN_FROM_BHEAD(thisblock); + BHeadN *prev = bheadn->prev; + + return (prev) ? &prev->bhead : nullptr; +} + +BHead *blo_bhead_next(FileData *fd, BHead *thisblock) +{ + BHeadN *new_bhead = nullptr; + BHead *bhead = nullptr; + + if (thisblock) { + /* bhead is actually a sub part of BHeadN + * We calculate the BHeadN pointer from the BHead pointer below */ + new_bhead = BHEADN_FROM_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 == nullptr) { + new_bhead = get_bhead(fd); + } + } + + if (new_bhead) { + /* here we do the reverse: + * go from the BHeadN pointer to the BHead pointer */ + bhead = &new_bhead->bhead; + } + + return bhead; +} + +#ifdef USE_BHEAD_READ_ON_DEMAND +static bool blo_bhead_read_data(FileData *fd, BHead *thisblock, void *buf) +{ + bool success = true; + BHeadN *new_bhead = BHEADN_FROM_BHEAD(thisblock); + BLI_assert(new_bhead->has_data == false && new_bhead->file_offset != 0); + off64_t offset_backup = fd->file->offset; + if (UNLIKELY(fd->file->seek(fd->file, new_bhead->file_offset, SEEK_SET) == -1)) { + success = false; + } + else { + if (fd->file->read(fd->file, buf, (size_t)new_bhead->bhead.len) != new_bhead->bhead.len) { + success = false; + } + if (fd->flags & FD_FLAGS_IS_MEMFILE) { + new_bhead->is_memchunk_identical = ((UndoReader *)fd->file)->memchunk_identical; + } + } + if (fd->file->seek(fd->file, offset_backup, SEEK_SET) == -1) { + success = false; + } + return success; +} + +static BHead *blo_bhead_read_full(FileData *fd, BHead *thisblock) +{ + BHeadN *new_bhead = BHEADN_FROM_BHEAD(thisblock); + BHeadN *new_bhead_data = static_cast( + 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 nullptr; + } + return &new_bhead_data->bhead; +} +#endif /* USE_BHEAD_READ_ON_DEMAND */ + +const char *blo_bhead_id_name(const FileData *fd, const BHead *bhead) +{ + return (const char *)POINTER_OFFSET(bhead, sizeof(*bhead) + fd->id_name_offset); +} + +AssetMetaData *blo_bhead_id_asset_data_address(const FileData *fd, const BHead *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) : + nullptr; +} + +static void decode_blender_header(FileData *fd) +{ + char header[SIZEOFBLENDERHEADER], num[4]; + ssize_t readsize; + + /* read in the header data */ + readsize = fd->file->read(fd->file, header, sizeof(header)); + + if (readsize == sizeof(header) && STREQLEN(header, "BLENDER", 7) && ELEM(header[7], '_', '-') && + ELEM(header[8], 'v', 'V') && + (isdigit(header[9]) && isdigit(header[10]) && isdigit(header[11]))) { + fd->flags |= FD_FLAGS_FILE_OK; + + /* what size are pointers in the file ? */ + if (header[7] == '_') { + fd->flags |= FD_FLAGS_FILE_POINTSIZE_IS_4; + if (sizeof(void *) != 4) { + fd->flags |= FD_FLAGS_POINTSIZE_DIFFERS; + } + } + else { + if (sizeof(void *) != 8) { + fd->flags |= FD_FLAGS_POINTSIZE_DIFFERS; + } + } + + /* is the file saved in a different endian + * than we need ? + */ + if (((header[8] == 'v') ? L_ENDIAN : B_ENDIAN) != ENDIAN_ORDER) { + fd->flags |= FD_FLAGS_SWITCH_ENDIAN; + } + + /* get the version number */ + memcpy(num, header + 9, 3); + num[3] = 0; + fd->fileversion = atoi(num); + } +} + +/** + * \return Success if the file is read correctly, else set \a r_error_message. + */ +static bool read_file_dna(FileData *fd, const char **r_error_message) +{ + BHead *bhead; + int subversion = 0; + + for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) { + if (bhead->code == GLOB) { + /* Before this, the subversion didn't exist in 'FileGlobal' so the subversion + * value isn't accessible for the purpose of DNA versioning in this case. */ + if (fd->fileversion <= 242) { + continue; + } + /* We can't use read_global because this needs 'DNA1' to be decoded, + * however the first 4 chars are _always_ the subversion. */ + FileGlobal *fg = reinterpret_cast(&bhead[1]); + BLI_STATIC_ASSERT(offsetof(FileGlobal, subvstr) == 0, "Must be first: subvstr") + char num[5]; + memcpy(num, fg->subvstr, 4); + num[4] = 0; + subversion = atoi(num); + } + else if (bhead->code == DNA1) { + const bool do_endian_swap = (fd->flags & FD_FLAGS_SWITCH_ENDIAN) != 0; + + fd->filesdna = DNA_sdna_from_data( + &bhead[1], bhead->len, do_endian_swap, true, r_error_message); + if (fd->filesdna) { + blo_do_versions_dna(fd->filesdna, fd->fileversion, subversion); + fd->compflags = DNA_struct_get_compareflags(fd->filesdna, fd->memsdna); + fd->reconstruct_info = DNA_reconstruct_info_create( + fd->filesdna, fd->memsdna, fd->compflags); + /* used to retrieve ID names from (bhead+1) */ + fd->id_name_offset = DNA_elem_offset(fd->filesdna, "ID", "char", "name[]"); + BLI_assert(fd->id_name_offset != -1); + fd->id_asset_data_offset = DNA_elem_offset( + fd->filesdna, "ID", "AssetMetaData", "*asset_data"); + + return true; + } + + return false; + } + else if (bhead->code == ENDB) { + break; + } + } + + *r_error_message = "Missing DNA block"; + return false; +} + +static int *read_file_thumbnail(FileData *fd) +{ + BHead *bhead; + int *blend_thumb = nullptr; + + for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) { + if (bhead->code == TEST) { + const bool do_endian_swap = (fd->flags & FD_FLAGS_SWITCH_ENDIAN) != 0; + int *data = (int *)(bhead + 1); + + if (bhead->len < (sizeof(int[2]))) { + break; + } + + if (do_endian_swap) { + BLI_endian_switch_int32(&data[0]); + BLI_endian_switch_int32(&data[1]); + } + + const int width = data[0]; + const int height = data[1]; + if (!BLEN_THUMB_MEMSIZE_IS_VALID(width, height)) { + break; + } + if (bhead->len < BLEN_THUMB_MEMSIZE_FILE(width, height)) { + break; + } + + blend_thumb = data; + break; + } + if (bhead->code != REND) { + /* Thumbnail is stored in TEST immediately after first REND... */ + break; + } + } + + return blend_thumb; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name File Data API + * \{ */ + +static FileData *filedata_new(BlendFileReadReport *reports) +{ + BLI_assert(reports != nullptr); + + FileData *fd = static_cast(MEM_callocN(sizeof(FileData), "FileData")); + + fd->memsdna = DNA_sdna_current_get(); + + fd->datamap = oldnewmap_new(); + fd->globmap = oldnewmap_new(); + fd->libmap = oldnewmap_new(); + + fd->reports = reports; + + return fd; +} + +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 = 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 = nullptr; + } + } + else { + BKE_reportf( + reports, RPT_ERROR, "Failed to read blend file '%s', not a blend file", fd->relabase); + blo_filedata_free(fd); + fd = nullptr; + } + + return fd; +} + +static FileData *blo_filedata_from_file_descriptor(const char *filepath, + BlendFileReadReport *reports, + int filedes) +{ + char header[7]; + FileReader *rawfile = BLI_filereader_new_file(filedes); + FileReader *file = nullptr; + + errno = 0; + /* If opening the file failed or we can't read the header, give up. */ + if (rawfile == nullptr || rawfile->read(rawfile, header, sizeof(header)) != sizeof(header)) { + BKE_reportf(reports->reports, + RPT_WARNING, + "Unable to read '%s': %s", + filepath, + errno ? strerror(errno) : TIP_("insufficient content")); + if (rawfile) { + rawfile->close(rawfile); + } + else { + close(filedes); + } + return nullptr; + } + + /* Rewind the file after reading the header. */ + rawfile->seek(rawfile, 0, SEEK_SET); + + /* Check if we have a regular file. */ + if (memcmp(header, "BLENDER", sizeof(header)) == 0) { + /* Try opening the file with memory-mapped IO. */ + file = BLI_filereader_new_mmap(filedes); + if (file == nullptr) { + /* mmap failed, so just keep using rawfile. */ + file = rawfile; + rawfile = nullptr; + } + } + else if (BLI_file_magic_is_gzip(header)) { + file = BLI_filereader_new_gzip(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 != nullptr) { + rawfile = nullptr; /* The `Zstd` #FileReader takes ownership of `rawfile`. */ + } + } + + /* Clean up `rawfile` if it wasn't taken over. */ + if (rawfile != nullptr) { + rawfile->close(rawfile); + } + if (file == nullptr) { + BKE_reportf(reports->reports, RPT_WARNING, "Unrecognized file format '%s'", filepath); + return nullptr; + } + + FileData *fd = filedata_new(reports); + fd->file = file; + + return fd; +} + +static FileData *blo_filedata_from_file_open(const char *filepath, BlendFileReadReport *reports) +{ + errno = 0; + const int file = BLI_open(filepath, O_BINARY | O_RDONLY, 0); + if (file == -1) { + BKE_reportf(reports->reports, + RPT_WARNING, + "Unable to open '%s': %s", + filepath, + errno ? strerror(errno) : TIP_("unknown error reading file")); + return nullptr; + } + return blo_filedata_from_file_descriptor(filepath, reports, file); +} + +FileData *blo_filedata_from_file(const char *filepath, BlendFileReadReport *reports) +{ + FileData *fd = blo_filedata_from_file_open(filepath, reports); + 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 nullptr; +} + +/** + * Same as blo_filedata_from_file(), but does not reads DNA data, only header. + * Use it for light access (e.g. thumbnail reading). + */ +static FileData *blo_filedata_from_file_minimal(const char *filepath) +{ + 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 nullptr; +} + +FileData *blo_filedata_from_memory(const void *mem, int memsize, BlendFileReadReport *reports) +{ + if (!mem || memsize < SIZEOFBLENDERHEADER) { + BKE_report( + reports->reports, RPT_WARNING, (mem) ? TIP_("Unable to read") : TIP_("Unable to open")); + return nullptr; + } + + FileReader *mem_file = BLI_filereader_new_memory(mem, memsize); + FileReader *file = mem_file; + + if (BLI_file_magic_is_gzip(static_cast(mem))) { + file = BLI_filereader_new_gzip(mem_file); + } + else if (BLI_file_magic_is_zstd(static_cast(mem))) { + file = BLI_filereader_new_zstd(mem_file); + } + + if (file == nullptr) { + /* Compression initialization failed. */ + mem_file->close(mem_file); + return nullptr; + } + + FileData *fd = filedata_new(reports); + fd->file = file; + + return blo_decode_and_check(fd, reports->reports); +} + +FileData *blo_filedata_from_memfile(MemFile *memfile, + const struct BlendFileReadParams *params, + BlendFileReadReport *reports) +{ + if (!memfile) { + BKE_report(reports->reports, RPT_WARNING, "Unable to open blend "); + return nullptr; + } + + FileData *fd = filedata_new(reports); + fd->file = BLO_memfile_new_filereader(memfile, params->undo_direction); + fd->undo_direction = params->undo_direction; + fd->flags |= FD_FLAGS_IS_MEMFILE; + + return blo_decode_and_check(fd, reports->reports); +} + +void blo_filedata_free(FileData *fd) +{ + if (fd) { + + /* Free all BHeadN data blocks */ +#ifndef NDEBUG + BLI_freelistN(&fd->bhead_list); +#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 != nullptr && BHEAD_USE_READ_ON_DEMAND(&new_bhead->bhead)) { + BLI_assert(new_bhead->has_data == 0); + } + MEM_freeN(new_bhead); + } +#endif + fd->file->close(fd->file); + + if (fd->filesdna) { + DNA_sdna_free(fd->filesdna); + } + if (fd->compflags) { + MEM_freeN((void *)fd->compflags); + } + if (fd->reconstruct_info) { + DNA_reconstruct_info_free(fd->reconstruct_info); + } + + if (fd->datamap) { + oldnewmap_free(fd->datamap); + } + if (fd->globmap) { + oldnewmap_free(fd->globmap); + } + if (fd->packedmap) { + oldnewmap_free(fd->packedmap); + } + if (fd->libmap && !(fd->flags & FD_FLAGS_NOT_MY_LIBMAP)) { + oldnewmap_free(fd->libmap); + } + if (fd->old_idmap != nullptr) { + BKE_main_idmap_destroy(fd->old_idmap); + } + blo_cache_storage_end(fd); + if (fd->bheadmap) { + MEM_freeN(fd->bheadmap); + } + +#ifdef USE_GHASH_BHEAD + if (fd->bhead_idname_hash) { + BLI_ghash_free(fd->bhead_idname_hash, nullptr, nullptr); + } +#endif + + MEM_freeN(fd); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Public Utilities + * \{ */ + +bool BLO_has_bfile_extension(const char *str) +{ + const char *ext_test[4] = {".blend", ".ble", ".blend.gz", nullptr}; + return BLI_path_extension_check_array(str, ext_test); +} + +bool BLO_library_path_explode(const char *path, char *r_dir, char **r_group, char **r_name) +{ + /* 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 = nullptr, *prev_slash = nullptr, c = '\0'; + + r_dir[0] = '\0'; + if (r_group) { + *r_group = nullptr; + } + if (r_name) { + *r_name = nullptr; + } + + /* if path leads to an existing directory, we can be sure we're not (in) a library */ + if (BLI_is_dir(path)) { + return false; + } + + strcpy(r_dir, path); + + while ((slash = (char *)BLI_path_slash_rfind(r_dir))) { + char tc = *slash; + *slash = '\0'; + if (BLO_has_bfile_extension(r_dir) && BLI_is_file(r_dir)) { + break; + } + if (STREQ(r_dir, BLO_EMBEDDED_STARTUP_BLEND)) { + break; + } + + if (prev_slash) { + *prev_slash = c; + } + prev_slash = slash; + c = tc; + } + + if (!slash) { + return false; + } + + if (slash[1] != '\0') { + BLI_assert(strlen(slash + 1) < BLO_GROUP_MAX); + if (r_group) { + *r_group = slash + 1; + } + } + + if (prev_slash && (prev_slash[1] != '\0')) { + BLI_assert(strlen(prev_slash + 1) < MAX_ID_NAME - 2); + if (r_name) { + *r_name = prev_slash + 1; + } + } + + return true; +} + +BlendThumbnail *BLO_thumbnail_from_file(const char *filepath) +{ + FileData *fd; + BlendThumbnail *data = nullptr; + int *fd_data; + + fd = blo_filedata_from_file_minimal(filepath); + 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 = static_cast(MEM_mallocN(data_size, __func__)); + if (data) { + BLI_assert((data_size - sizeof(*data)) == + (BLEN_THUMB_MEMSIZE_FILE(width, height) - (sizeof(*fd_data) * 2))); + data->width = width; + data->height = height; + memcpy(data->rect, &fd_data[2], data_size - sizeof(*data)); + } + } + } + + blo_filedata_free(fd); + + return data; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Old/New Pointer Map + * \{ */ + +/* Only direct data-blocks. */ +static void *newdataadr(FileData *fd, const void *adr) +{ + return oldnewmap_lookup_and_inc(fd->datamap, adr, true); +} + +/* Only direct data-blocks. */ +static void *newdataadr_no_us(FileData *fd, const void *adr) +{ + return oldnewmap_lookup_and_inc(fd->datamap, adr, false); +} + +void *blo_read_get_new_globaldata_address(FileData *fd, const void *adr) +{ + return oldnewmap_lookup_and_inc(fd->globmap, adr, true); +} + +/* Used to restore packed data after undo. */ +static void *newpackedadr(FileData *fd, const void *adr) +{ + if (fd->packedmap && adr) { + return oldnewmap_lookup_and_inc(fd->packedmap, adr, true); + } + + return oldnewmap_lookup_and_inc(fd->datamap, adr, true); +} + +/* only lib data */ +static void *newlibadr(FileData *fd, const void *lib, const void *adr) +{ + return oldnewmap_liblookup(fd->libmap, adr, lib); +} + +void *blo_do_versions_newlibadr(FileData *fd, const void *lib, const void *adr) +{ + return newlibadr(fd, lib, adr); +} + +/* increases user number */ +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 = newp; + if (newp) { + entry->nr = GS(((ID *)newp)->name); + } + } + } +} + +static void change_link_placeholder_to_real_ID_pointer(ListBase *mainlist, + FileData *basefd, + void *old, + void *newp) +{ + LISTBASE_FOREACH (Main *, mainptr, mainlist) { + FileData *fd; + + if (mainptr->curlib) { + fd = mainptr->curlib->filedata; + } + else { + fd = basefd; + } + + if (fd) { + change_link_placeholder_to_real_ID_pointer_fd(fd, old, newp); + } + } +} + +/* XXX disabled this feature - packed files also belong in temp saves and quit.blend, + * to make restore work. */ + +static void insert_packedmap(FileData *fd, PackedFile *pf) +{ + oldnewmap_insert(fd->packedmap, pf, pf, 0); + oldnewmap_insert(fd->packedmap, pf->data, pf->data, 0); +} + +void blo_make_packed_pointer_map(FileData *fd, Main *oldmain) +{ + fd->packedmap = oldnewmap_new(); + + LISTBASE_FOREACH (Image *, ima, &oldmain->images) { + if (ima->packedfile) { + insert_packedmap(fd, ima->packedfile); + } + + LISTBASE_FOREACH (ImagePackedFile *, imapf, &ima->packedfiles) { + if (imapf->packedfile) { + insert_packedmap(fd, imapf->packedfile); + } + } + } + + LISTBASE_FOREACH (VFont *, vfont, &oldmain->fonts) { + if (vfont->packedfile) { + insert_packedmap(fd, vfont->packedfile); + } + } + + LISTBASE_FOREACH (bSound *, sound, &oldmain->sounds) { + if (sound->packedfile) { + insert_packedmap(fd, sound->packedfile); + } + } + + LISTBASE_FOREACH (Volume *, volume, &oldmain->volumes) { + if (volume->packedfile) { + insert_packedmap(fd, volume->packedfile); + } + } + + LISTBASE_FOREACH (Library *, lib, &oldmain->libraries) { + if (lib->packedfile) { + insert_packedmap(fd, lib->packedfile); + } + } +} + +void blo_end_packed_pointer_map(FileData *fd, Main *oldmain) +{ + OldNew *entry = fd->packedmap->entries; + + /* 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 = nullptr; + } + } + + LISTBASE_FOREACH (Image *, ima, &oldmain->images) { + ima->packedfile = static_cast(newpackedadr(fd, ima->packedfile)); + + LISTBASE_FOREACH (ImagePackedFile *, imapf, &ima->packedfiles) { + imapf->packedfile = static_cast(newpackedadr(fd, imapf->packedfile)); + } + } + + LISTBASE_FOREACH (VFont *, vfont, &oldmain->fonts) { + vfont->packedfile = static_cast(newpackedadr(fd, vfont->packedfile)); + } + + LISTBASE_FOREACH (bSound *, sound, &oldmain->sounds) { + sound->packedfile = static_cast(newpackedadr(fd, sound->packedfile)); + } + + LISTBASE_FOREACH (Library *, lib, &oldmain->libraries) { + lib->packedfile = static_cast(newpackedadr(fd, lib->packedfile)); + } + + LISTBASE_FOREACH (Volume *, volume, &oldmain->volumes) { + volume->packedfile = static_cast(newpackedadr(fd, volume->packedfile)); + } +} + +void blo_add_library_pointer_map(ListBase *old_mainlist, FileData *fd) +{ + ListBase *lbarray[INDEX_ID_MAX]; + + LISTBASE_FOREACH (Main *, ptr, old_mainlist) { + int i = set_listbasepointers(ptr, lbarray); + while (i--) { + LISTBASE_FOREACH (ID *, id, lbarray[i]) { + oldnewmap_lib_insert(fd, id, id, GS(id->name)); + } + } + } + + fd->old_mainlist = old_mainlist; +} + +void blo_make_old_idmap_from_main(FileData *fd, Main *bmain) +{ + if (fd->old_idmap != nullptr) { + BKE_main_idmap_destroy(fd->old_idmap); + } + fd->old_idmap = BKE_main_idmap_create(bmain, false, nullptr, MAIN_IDMAP_TYPE_UUID); +} + +typedef struct BLOCacheStorage { + GHash *cache_map; + MemArena *memarena; +} BLOCacheStorage; + +typedef struct BLOCacheStorageValue { + void *cache_v; + uint new_usage_count; +} BLOCacheStorageValue; + +/** Register a cache data entry to be preserved when reading some undo memfile. */ +static void blo_cache_storage_entry_register( + ID *id, const IDCacheKey *key, void **cache_p, uint UNUSED(flags), void *cache_storage_v) +{ + BLI_assert(key->id_session_uuid == id->session_uuid); + UNUSED_VARS_NDEBUG(id); + + BLOCacheStorage *cache_storage = static_cast(cache_storage_v); + BLI_assert(!BLI_ghash_haskey(cache_storage->cache_map, key)); + + IDCacheKey *storage_key = static_cast( + BLI_memarena_alloc(cache_storage->memarena, sizeof(*storage_key))); + *storage_key = *key; + BLOCacheStorageValue *storage_value = static_cast( + 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); +} + +/** Restore a cache data entry from old ID into new one, when reading some undo memfile. */ +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 = static_cast(cache_storage_v); + + 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 = nullptr; + } + return; + } + + BLOCacheStorageValue *storage_value = static_cast( + BLI_ghash_lookup(cache_storage->cache_map, key)); + if (storage_value == nullptr) { + *cache_p = nullptr; + return; + } + storage_value->new_usage_count++; + *cache_p = storage_value->cache_v; +} + +/** Clear as needed a cache data entry from old ID, when reading some undo memfile. */ +static void blo_cache_storage_entry_clear_in_old(ID *UNUSED(id), + const IDCacheKey *key, + void **cache_p, + uint UNUSED(flags), + void *cache_storage_v) +{ + BLOCacheStorage *cache_storage = static_cast(cache_storage_v); + + BLOCacheStorageValue *storage_value = static_cast( + 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 = nullptr; + } + else { + BLI_assert(*cache_p == storage_value->cache_v); + } +} + +void blo_cache_storage_init(FileData *fd, Main *bmain) +{ + if (fd->flags & FD_FLAGS_IS_MEMFILE) { + BLI_assert(fd->cache_storage == nullptr); + fd->cache_storage = static_cast( + 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 = static_cast(lb->first); + if (id == nullptr) { + continue; + } + + const IDTypeInfo *type_info = BKE_idtype_get_info_from_id(id); + if (type_info->foreach_cache == nullptr) { + continue; + } + + FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id) { + if (ID_IS_LINKED(id)) { + continue; + } + BKE_idtype_id_foreach_cache(id, blo_cache_storage_entry_register, fd->cache_storage); + } + FOREACH_MAIN_LISTBASE_ID_END; + } + FOREACH_MAIN_LISTBASE_END; + } + else { + fd->cache_storage = nullptr; + } +} + +void blo_cache_storage_old_bmain_clear(FileData *fd, Main *bmain_old) +{ + if (fd->cache_storage != nullptr) { + ListBase *lb; + FOREACH_MAIN_LISTBASE_BEGIN (bmain_old, lb) { + ID *id = static_cast(lb->first); + if (id == nullptr) { + continue; + } + + const IDTypeInfo *type_info = BKE_idtype_get_info_from_id(id); + if (type_info->foreach_cache == nullptr) { + continue; + } + + FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id) { + if (ID_IS_LINKED(id)) { + continue; + } + BKE_idtype_id_foreach_cache(id, blo_cache_storage_entry_clear_in_old, fd->cache_storage); + } + FOREACH_MAIN_LISTBASE_ID_END; + } + FOREACH_MAIN_LISTBASE_END; + } +} + +void blo_cache_storage_end(FileData *fd) +{ + 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 = nullptr; + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name DNA Struct Loading + * \{ */ + +static void switch_endian_structs(const struct SDNA *filesdna, BHead *bhead) +{ + int blocksize, nblocks; + char *data; + + data = (char *)(bhead + 1); + blocksize = filesdna->types_size[filesdna->structs[bhead->SDNAnr]->type]; + + nblocks = bhead->nr; + while (nblocks--) { + DNA_struct_switch_endian(filesdna, bhead->SDNAnr, data); + + data += blocksize; + } +} + +static void *read_struct(FileData *fd, BHead *bh, const char *blockname) +{ + void *temp = nullptr; + + if (bh->len) { +#ifdef USE_BHEAD_READ_ON_DEMAND + BHead *bh_orig = bh; +#endif + + /* switch is based on file dna */ + if (bh->SDNAnr && (fd->flags & FD_FLAGS_SWITCH_ENDIAN)) { +#ifdef USE_BHEAD_READ_ON_DEMAND + if (BHEADN_FROM_BHEAD(bh)->has_data == false) { + bh = blo_bhead_read_full(fd, bh); + if (UNLIKELY(bh == nullptr)) { + fd->flags &= ~FD_FLAGS_FILE_OK; + return nullptr; + } + } +#endif + switch_endian_structs(fd->filesdna, bh); + } + + if (fd->compflags[bh->SDNAnr] != SDNA_CMP_REMOVED) { + if (fd->compflags[bh->SDNAnr] == SDNA_CMP_NOT_EQUAL) { +#ifdef USE_BHEAD_READ_ON_DEMAND + if (BHEADN_FROM_BHEAD(bh)->has_data == false) { + bh = blo_bhead_read_full(fd, bh); + if (UNLIKELY(bh == nullptr)) { + fd->flags &= ~FD_FLAGS_FILE_OK; + return nullptr; + } + } +#endif + temp = DNA_struct_reconstruct(fd->reconstruct_info, bh->SDNAnr, bh->nr, (bh + 1)); + } + else { + /* SDNA_CMP_EQUAL */ + temp = MEM_mallocN(bh->len, blockname); +#ifdef USE_BHEAD_READ_ON_DEMAND + if (BHEADN_FROM_BHEAD(bh)->has_data) { + memcpy(temp, (bh + 1), bh->len); + } + else { + /* Instead of allocating the bhead, then copying it, + * read the data from the file directly into the memory. */ + if (UNLIKELY(!blo_bhead_read_data(fd, bh, temp))) { + fd->flags &= ~FD_FLAGS_FILE_OK; + MEM_freeN(temp); + temp = nullptr; + } + } +#else + memcpy(temp, (bh + 1), bh->len); +#endif + } + } + +#ifdef USE_BHEAD_READ_ON_DEMAND + if (bh_orig != bh) { + MEM_freeN(BHEADN_FROM_BHEAD(bh)); + } +#endif + } + + return temp; +} + +/* Like read_struct, but gets a pointer without allocating. Only works for + * undo since DNA must match. */ +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) : nullptr; +} + +static void link_glob_list(FileData *fd, ListBase *lb) /* for glob data */ +{ + Link *ln, *prev; + void *poin; + + if (BLI_listbase_is_empty(lb)) { + return; + } + poin = newdataadr(fd, lb->first); + if (lb->first) { + oldnewmap_insert(fd->globmap, lb->first, poin, 0); + } + lb->first = poin; + + ln = static_cast(lb->first); + prev = nullptr; + while (ln) { + poin = newdataadr(fd, ln->next); + if (ln->next) { + oldnewmap_insert(fd->globmap, ln->next, poin, 0); + } + ln->next = static_cast(poin); + ln->prev = prev; + prev = ln; + ln = ln->next; + } + lb->last = prev; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Read ID + * \{ */ + +static void lib_link_id(BlendLibReader *reader, ID *id); + +static void lib_link_id_embedded_id(BlendLibReader *reader, ID *id) +{ + + /* Handle 'private IDs'. */ + bNodeTree *nodetree = ntreeFromID(id); + 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 != nullptr) { + lib_link_id(reader, &scene->master_collection->id); + BKE_collection_blend_read_lib(reader, scene->master_collection); + } + } +} + +static void lib_link_id(BlendLibReader *reader, ID *id) +{ + /* 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 != nullptr) { + BKE_animdata_blend_read_lib(reader, id, adt); + } + + if (id->override_library) { + BLO_read_id_address(reader, id->lib, &id->override_library->reference); + BLO_read_id_address(reader, id->lib, &id->override_library->storage); + BLO_read_id_address(reader, id->lib, &id->override_library->hierarchy_root); + } + + lib_link_id_embedded_id(reader, id); +} + +static void direct_link_id_override_property_operation_cb(BlendDataReader *reader, void *data) +{ + IDOverrideLibraryPropertyOperation *opop = static_cast( + data); + + BLO_read_data_address(reader, &opop->subitem_reference_name); + BLO_read_data_address(reader, &opop->subitem_local_name); + + opop->tag = 0; /* Runtime only. */ +} + +static void direct_link_id_override_property_cb(BlendDataReader *reader, void *data) +{ + IDOverrideLibraryProperty *op = static_cast(data); + + BLO_read_data_address(reader, &op->rna_path); + + op->tag = 0; /* Runtime only. */ + + BLO_read_list_cb(reader, &op->operations, direct_link_id_override_property_operation_cb); +} + +static void direct_link_id_common( + BlendDataReader *reader, Library *current_library, ID *id, ID *id_old, const int tag); + +static void direct_link_id_embedded_id(BlendDataReader *reader, + Library *current_library, + ID *id, + ID *id_old) +{ + /* Handle 'private IDs'. */ + bNodeTree **nodetree = BKE_ntree_ptr_from_id(id); + if (nodetree != nullptr && *nodetree != nullptr) { + BLO_read_data_address(reader, nodetree); + direct_link_id_common(reader, + current_library, + (ID *)*nodetree, + id_old != nullptr ? (ID *)ntreeFromID(id_old) : nullptr, + 0); + ntreeBlendReadData(reader, id, *nodetree); + } + + if (GS(id->name) == ID_SCE) { + Scene *scene = (Scene *)id; + 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 != nullptr ? &((Scene *)id_old)->master_collection->id : + nullptr, + 0); + BKE_collection_blend_read_data(reader, scene->master_collection, &scene->id); + } + } +} + +static int direct_link_id_restore_recalc_exceptions(const ID *id_current) +{ + /* Exception for armature objects, where the pose has direct points to the + * armature data-block. */ + if (GS(id_current->name) == ID_OB && ((Object *)id_current)->pose) { + return ID_RECALC_GEOMETRY; + } + + return 0; +} + +static int direct_link_id_restore_recalc(const FileData *fd, + const ID *id_target, + const ID *id_current, + const bool is_identical) +{ + /* These are the evaluations that had not been performed yet at the time the + * target undo state was written. These need to be done again, since they may + * flush back changes to the original datablock. */ + int recalc = id_target->recalc; + + 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. */ + recalc |= ID_RECALC_ALL; + } + else { + /* If the contents datablock changed, the depsgraph needs to copy the + * datablock again to ensure it matches the original datablock. */ + if (!is_identical) { + recalc |= ID_RECALC_COPY_ON_WRITE; + } + + /* Special exceptions. */ + recalc |= direct_link_id_restore_recalc_exceptions(id_current); + + /* Evaluations for the current state that have not been performed yet + * by the time we are performing this undo step. */ + recalc |= id_current->recalc; + + /* Tags that were set between the target state and the current state, + * that we need to perform again. */ + if (fd->undo_direction == STEP_UNDO) { + /* Undo: tags from target to the current state. */ + recalc |= id_current->recalc_up_to_undo_push; + } + else { + BLI_assert(fd->undo_direction == STEP_REDO); + /* Redo: tags from current to the target state. */ + recalc |= id_target->recalc_up_to_undo_push; + } + } + + return recalc; +} + +static void direct_link_id_common( + BlendDataReader *reader, Library *current_library, ID *id, ID *id_old, const int tag) +{ + if (!BLO_read_data_is_undo(reader)) { + /* When actually reading a file, we do want to reset/re-generate session uuids. + * In undo case, we want to re-use existing ones. */ + id->session_uuid = MAIN_ID_SESSION_UUID_UNSET; + } + + if ((tag & LIB_TAG_TEMP_MAIN) == 0) { + BKE_lib_libblock_session_uuid_ensure(id); + } + + id->lib = current_library; + id->us = ID_FAKE_USERS(id); + id->icon_id = 0; + 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 = nullptr; + } + else { + BLO_read_data_address(reader, &id->library_weak_reference); + } + + if (tag & LIB_TAG_ID_LINK_PLACEHOLDER) { + /* For placeholder we only need to set the tag and properly initialize generic ID fields above, + * no further data to read. */ + return; + } + + if (id->asset_data) { + BLO_read_data_address(reader, &id->asset_data); + BKE_asset_metadata_read(reader, id->asset_data); + /* Restore runtime asset type info. */ + const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id); + id->asset_data->local_type_info = id_type->asset_type_info; + } + + /* Link direct data of ID properties. */ + if (id->properties) { + BLO_read_data_address(reader, &id->properties); + /* this case means the data was written incorrectly, it should not happen */ + IDP_BlendDataRead(reader, &id->properties); + } + + id->flag &= ~LIB_INDIRECT_WEAK_LINK; + + /* NOTE: It is important to not clear the recalc flags for undo/redo. + * Preserving recalc flags on redo/undo is the only way to make dependency graph detect + * that animation is to be evaluated on undo/redo. If this is not enforced by the recalc + * flags dependency graph does not do animation update to avoid loss of unkeyed changes., + * which conflicts with undo/redo of changes to animation data itself. + * + * But for regular file load we clear the flag, since the flags might have been changed since + * the version the file has been saved with. */ + if (!BLO_read_data_is_undo(reader)) { + id->recalc = 0; + id->recalc_after_undo_push = 0; + } + else if ((reader->fd->skip_flags & BLO_READ_SKIP_UNDO_OLD_MAIN) == 0) { + id->recalc = direct_link_id_restore_recalc(reader->fd, id, id_old, false); + id->recalc_after_undo_push = 0; + } + + /* Link direct data of overrides. */ + if (id->override_library) { + BLO_read_data_address(reader, &id->override_library); + /* Work around file corruption on writing, see T86853. */ + if (id->override_library != nullptr) { + BLO_read_list_cb( + reader, &id->override_library->properties, direct_link_id_override_property_cb); + id->override_library->runtime = nullptr; + } + } + + DrawDataList *drawdata = DRW_drawdatalist_from_id(id); + if (drawdata) { + BLI_listbase_clear((ListBase *)drawdata); + } + + /* Handle 'private IDs'. */ + direct_link_id_embedded_id(reader, current_library, id, id_old); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Read Animation (legacy for version patching) + * \{ */ + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Read ID: Shape Keys + * \{ */ + +void blo_do_versions_key_uidgen(Key *key) +{ + key->uidgen = 1; + LISTBASE_FOREACH (KeyBlock *, block, &key->block) { + block->uid = key->uidgen++; + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Read ID: Scene + * \{ */ + +#ifdef USE_SETSCENE_CHECK +/** + * A version of #BKE_scene_validate_setscene with special checks for linked libs. + */ +static bool scene_validate_setscene__liblink(Scene *sce, const int totscene) +{ + Scene *sce_iter; + int a; + + if (sce->set == nullptr) { + return true; + } + + for (a = 0, sce_iter = sce; sce_iter->set; sce_iter = sce_iter->set, a++) { + /* This runs per library (before each libraries #Main has been joined), + * so we can't step into other libraries since `totscene` is only for this library. + * + * Also, other libraries may not have been linked yet, + * while we could check #LIB_TAG_NEED_LINK the library pointer check is sufficient. */ + if (sce->id.lib != sce_iter->id.lib) { + return true; + } + if (sce_iter->flag & SCE_READFILE_LIBLINK_NEED_SETSCENE_CHECK) { + return true; + } + + if (a > totscene) { + sce->set = nullptr; + return false; + } + } + + return true; +} +#endif + +static void lib_link_scenes_check_set(Main *bmain) +{ +#ifdef USE_SETSCENE_CHECK + const int totscene = BLI_listbase_count(&bmain->scenes); + LISTBASE_FOREACH (Scene *, sce, &bmain->scenes) { + if (sce->flag & SCE_READFILE_LIBLINK_NEED_SETSCENE_CHECK) { + sce->flag &= ~SCE_READFILE_LIBLINK_NEED_SETSCENE_CHECK; + if (!scene_validate_setscene__liblink(sce, totscene)) { + CLOG_WARN(&LOG, "Found cyclic background scene when linking %s", sce->id.name + 2); + } + } + } +#else + UNUSED_VARS(bmain, totscene); +#endif +} + +#undef USE_SETSCENE_CHECK + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Read ID: Screen + * \{ */ + +/* how to handle user count on pointer restore */ +typedef enum ePointerUserMode { + USER_IGNORE = 0, /* ignore user count */ + USER_REAL = 1, /* ensure at least one real user (fake user ignored) */ +} ePointerUserMode; + +static void restore_pointer_user(ID *id, ID *newid, ePointerUserMode user) +{ + BLI_assert(STREQ(newid->name + 2, id->name + 2)); + BLI_assert(newid->lib == id->lib); + UNUSED_VARS_NDEBUG(id); + + if (user == USER_REAL) { + id_us_ensure_real(newid); + } +} + +#ifndef USE_GHASH_RESTORE_POINTER +/** + * A version of #restore_pointer_by_name that performs a full search (slow!). + * Use only for limited lookups, when the overhead of + * creating a #IDNameLib_Map for a single lookup isn't worthwhile. + */ +static void *restore_pointer_by_name_main(Main *mainp, ID *id, ePointerUserMode user) +{ + if (id) { + ListBase *lb = which_libbase(mainp, GS(id->name)); + if (lb) { /* there's still risk of checking corrupt mem (freed Ids in oops) */ + ID *idn = lb->first; + for (; idn; idn = idn->next) { + if (STREQ(idn->name + 2, id->name + 2)) { + if (idn->lib == id->lib) { + restore_pointer_user(id, idn, user); + break; + } + } + } + return idn; + } + } + return nullptr; +} +#endif + +/** + * Only for undo files, or to restore a screen after reading without UI... + * + * \param user: + * - USER_IGNORE: no user-count change. + * - USER_REAL: ensure a real user (even if a fake one is set). + * \param id_map: lookup table, use when performing many lookups. + * this could be made an optional argument (falling back to a full lookup), + * however at the moment it's always available. + */ +static void *restore_pointer_by_name(struct IDNameLib_Map *id_map, ID *id, ePointerUserMode user) +{ +#ifdef USE_GHASH_RESTORE_POINTER + if (id) { + /* use fast lookup when available */ + ID *idn = BKE_main_idmap_lookup_id(id_map, id); + if (idn) { + restore_pointer_user(id, idn, user); + } + return idn; + } + return nullptr; +#else + Main *mainp = BKE_main_idmap_main_get(id_map); + return restore_pointer_by_name_main(mainp, id, user); +#endif +} + +static void lib_link_seq_clipboard_pt_restore(ID *id, struct IDNameLib_Map *id_map) +{ + if (id) { + /* clipboard must ensure this */ + BLI_assert(id->newid != nullptr); + id->newid = static_cast(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 = static_cast(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); + lib_link_seq_clipboard_pt_restore((ID *)seq->clip, id_map); + lib_link_seq_clipboard_pt_restore((ID *)seq->mask, id_map); + lib_link_seq_clipboard_pt_restore((ID *)seq->sound, id_map); + return true; +} + +static void lib_link_clipboard_restore(struct IDNameLib_Map *id_map) +{ + /* update IDs stored in sequencer clipboard */ + SEQ_for_each_callback(&seqbase_clipboard, lib_link_seq_clipboard_cb, id_map); +} + +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 == nullptr) { + return IDWALK_RET_NOP; + } + + /* Special ugly case here, thanks again for those non-IDs IDs... */ + /* We probably need to add more cases here (hint: nodetrees), + * but will wait for changes from D5559 to get in first. */ + if (GS((*id_pointer)->name) == ID_GR) { + Collection *collection = (Collection *)*id_pointer; + if (collection->flag & COLLECTION_IS_MASTER) { + /* We should never reach that point anymore, since master collection private ID should be + * properly tagged with IDWALK_CB_EMBEDDED. */ + BLI_assert_unreachable(); + return IDWALK_RET_NOP; + } + } + + struct IDNameLib_Map *id_map = static_cast(cb_data->user_data); + + /* 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 = static_cast(restore_pointer_by_name( + id_map, *id_pointer, (cb_flag & IDWALK_CB_USER_ONE) ? USER_REAL : USER_IGNORE)); + + return IDWALK_RET_NOP; +} + +static void lib_link_main_data_restore(struct IDNameLib_Map *id_map, Main *newmain) +{ + ID *id; + FOREACH_MAIN_ID_BEGIN (newmain, id) { + BKE_library_foreach_ID_link(newmain, id, lib_link_main_data_restore_cb, id_map, IDWALK_NOP); + } + FOREACH_MAIN_ID_END; +} + +static void lib_link_wm_xr_data_restore(struct IDNameLib_Map *id_map, wmXrData *xr_data) +{ + xr_data->session_settings.base_pose_object = static_cast(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) +{ + bScreen *screen = BKE_workspace_active_screen_get(win->workspace_hook); + + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype == SPACE_VIEW3D) { + View3D *v3d = (View3D *)sl; + + if (v3d->camera == nullptr || v3d->scenelock) { + v3d->camera = scene->camera; + } + + if (v3d->localvd) { + 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 = static_cast(view_layer->object_bases.first); base; + base = base->next) { + if (base->local_view_bits & v3d->local_view_uuid) { + break; + } + } + if (base == nullptr) { + MEM_freeN(v3d->localvd); + v3d->localvd = nullptr; + v3d->local_view_uuid = 0; + + /* Region-base storage is different depending if the space is active. */ + ListBase *regionbase = (sl == area->spacedata.first) ? &area->regionbase : + &sl->regionbase; + LISTBASE_FOREACH (ARegion *, region, regionbase) { + if (region->regiontype == RGN_TYPE_WINDOW) { + RegionView3D *rv3d = static_cast(region->regiondata); + if (rv3d->localvd) { + MEM_freeN(rv3d->localvd); + rv3d->localvd = nullptr; + } + } + } + } + } + } + } + } +} + +static void lib_link_workspace_layout_restore(struct IDNameLib_Map *id_map, + Main *newmain, + WorkSpaceLayout *layout) +{ + bScreen *screen = BKE_workspace_layout_screen_get(layout); + + /* avoid conflicts with 2.8x branch */ + { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype == SPACE_VIEW3D) { + View3D *v3d = (View3D *)sl; + + v3d->camera = static_cast( + restore_pointer_by_name(id_map, (ID *)v3d->camera, USER_REAL)); + v3d->ob_center = static_cast( + 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 = static_cast( + restore_pointer_by_name(id_map, (ID *)ads->source, USER_REAL)); + + if (ads->filter_grp) { + ads->filter_grp = static_cast( + restore_pointer_by_name(id_map, (ID *)ads->filter_grp, USER_IGNORE)); + } + } + + /* force recalc of list of channels (i.e. includes calculating F-Curve colors) + * thus preventing the "black curves" problem post-undo + */ + sipo->runtime.flag |= SIPO_RUNTIME_FLAG_NEED_CHAN_SYNC_COLOR; + } + else if (sl->spacetype == SPACE_PROPERTIES) { + SpaceProperties *sbuts = (SpaceProperties *)sl; + sbuts->pinid = static_cast( + restore_pointer_by_name(id_map, sbuts->pinid, USER_IGNORE)); + if (sbuts->pinid == nullptr) { + sbuts->flag &= ~SB_PIN_CONTEXT; + } + + /* TODO: restore path pointers: T40046 + * (complicated because this contains data pointers too, not just ID). */ + MEM_SAFE_FREE(sbuts->path); + } + else if (sl->spacetype == SPACE_FILE) { + SpaceFile *sfile = (SpaceFile *)sl; + sfile->op = nullptr; + sfile->tags = FILE_TAG_REBUILD_MAIN_FILES; + } + else if (sl->spacetype == SPACE_ACTION) { + SpaceAction *saction = (SpaceAction *)sl; + + saction->action = static_cast( + restore_pointer_by_name(id_map, (ID *)saction->action, USER_REAL)); + saction->ads.source = static_cast( + restore_pointer_by_name(id_map, (ID *)saction->ads.source, USER_REAL)); + + if (saction->ads.filter_grp) { + saction->ads.filter_grp = static_cast( + restore_pointer_by_name(id_map, (ID *)saction->ads.filter_grp, USER_IGNORE)); + } + + /* force recalc of list of channels, potentially updating the active action + * while we're at it (as it can only be updated that way) T28962. + */ + saction->runtime.flag |= SACTION_RUNTIME_FLAG_NEED_CHAN_SYNC; + } + else if (sl->spacetype == SPACE_IMAGE) { + SpaceImage *sima = (SpaceImage *)sl; + + sima->image = static_cast( + 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 = nullptr; + +#if 0 + /* Those are allocated and freed by space code, no need to handle them here. */ + MEM_SAFE_FREE(sima->scopes.waveform_1); + MEM_SAFE_FREE(sima->scopes.waveform_2); + MEM_SAFE_FREE(sima->scopes.waveform_3); + MEM_SAFE_FREE(sima->scopes.vecscope); +#endif + sima->scopes.ok = 0; + + /* 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 = static_cast( + restore_pointer_by_name(id_map, (ID *)sima->gpd, USER_REAL)); + sima->mask_info.mask = static_cast( + restore_pointer_by_name(id_map, (ID *)sima->mask_info.mask, USER_REAL)); + } + else if (sl->spacetype == SPACE_SEQ) { + SpaceSeq *sseq = (SpaceSeq *)sl; + + /* 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 = static_cast( + 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 = static_cast( + restore_pointer_by_name(id_map, (ID *)ads->source, USER_REAL)); + + if (ads->filter_grp) { + ads->filter_grp = static_cast( + restore_pointer_by_name(id_map, (ID *)ads->filter_grp, USER_IGNORE)); + } + } + } + else if (sl->spacetype == SPACE_TEXT) { + SpaceText *st = (SpaceText *)sl; + + st->text = static_cast( + restore_pointer_by_name(id_map, (ID *)st->text, USER_IGNORE)); + if (st->text == nullptr) { + st->text = static_cast(newmain->texts.first); + } + } + else if (sl->spacetype == SPACE_SCRIPT) { + SpaceScript *scpt = (SpaceScript *)sl; + + scpt->script = static_cast