Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormattoverby <mattoverby@gmail.com>2020-08-28 23:00:01 +0300
committermattoverby <mattoverby@gmail.com>2020-08-28 23:00:01 +0300
commit44819c69a4e702b224c4ba166def9e212bee6d92 (patch)
tree45f0349bd9ad29892d14b0f63d496b309cd32ed8
parent6c32148cd2ea90cde4d4e46beb5ad4969b044c49 (diff)
parent019cd2e56b377a35b1fa2c85aebe60fb8c495335 (diff)
fixed submodulessoc-2020-soft-body
-rw-r--r--.clang-format4
-rw-r--r--CMakeLists.txt13
-rw-r--r--build_files/build_environment/CMakeLists.txt1
-rw-r--r--build_files/build_environment/cmake/versions.cmake1
-rwxr-xr-xbuild_files/build_environment/install_deps.sh10
-rw-r--r--build_files/cmake/Modules/FindEmbree.cmake8
-rw-r--r--build_files/cmake/Modules/GTestTesting.cmake3
-rw-r--r--build_files/cmake/config/blender_full.cmake3
-rw-r--r--build_files/cmake/config/blender_lite.cmake1
-rw-r--r--build_files/cmake/config/blender_release.cmake3
-rw-r--r--build_files/cmake/macros.cmake4
-rw-r--r--build_files/cmake/platform/platform_apple.cmake9
-rw-r--r--build_files/cmake/platform/platform_unix.cmake17
-rw-r--r--build_files/cmake/platform/platform_win32.cmake7
-rw-r--r--doc/python_api/rst/change_log.rst3210
-rw-r--r--extern/bullet2/CMakeLists.txt3
-rwxr-xr-xextern/discregrid/discregrid/include/Discregrid/cubic_lagrange_discrete_grid.hpp2
-rw-r--r--extern/mantaflow/CMakeLists.txt3
-rw-r--r--extern/quadriflow/CMakeLists.txt3
-rw-r--r--extern/quadriflow/patches/blender.patch12
-rw-r--r--extern/quadriflow/src/loader.cpp2
-rw-r--r--extern/softbody/src/admmpd_bvh.cpp5
-rw-r--r--extern/softbody/src/admmpd_bvh.h6
-rw-r--r--extern/softbody/src/admmpd_collision.cpp282
-rw-r--r--extern/softbody/src/admmpd_collision.h27
-rw-r--r--extern/softbody/src/admmpd_linsolve.cpp7
-rw-r--r--extern/softbody/src/admmpd_log.cpp1
-rw-r--r--extern/softbody/src/admmpd_mesh.cpp78
-rw-r--r--extern/softbody/src/admmpd_mesh.h30
-rw-r--r--extern/softbody/src/admmpd_types.h21
-rw-r--r--intern/clog/CLG_log.h3
-rw-r--r--intern/clog/clog.c23
-rw-r--r--intern/cycles/blender/blender_mesh.cpp46
-rw-r--r--intern/cycles/blender/blender_session.cpp3
-rw-r--r--intern/cycles/device/CMakeLists.txt1
-rw-r--r--intern/cycles/device/opencl/device_opencl_impl.cpp8
-rw-r--r--intern/cycles/kernel/kernel_film.h2
-rw-r--r--intern/cycles/kernel/kernels/opencl/kernel_bake.cl14
-rw-r--r--intern/cycles/kernel/osl/osl_globals.h1
-rw-r--r--intern/cycles/kernel/osl/osl_services.cpp8
-rw-r--r--intern/cycles/render/buffers.cpp15
-rw-r--r--intern/cycles/render/buffers.h2
-rw-r--r--intern/cycles/render/film.cpp2
-rw-r--r--intern/ghost/intern/GHOST_WindowCocoa.mm2
-rw-r--r--intern/guardedalloc/MEM_guardedalloc.h5
-rw-r--r--intern/guardedalloc/intern/leak_detector.cc19
-rw-r--r--intern/mantaflow/intern/manta_fluid_API.cpp4
-rw-r--r--intern/numaapi/source/build_config.h2
-rw-r--r--intern/softbody/admmpd_api.cpp289
-rw-r--r--intern/softbody/admmpd_api.h75
m---------release/datafiles/locale0
-rw-r--r--release/datafiles/userdef/userdef_default_theme.c10
m---------release/scripts/addons0
-rw-r--r--release/scripts/modules/rna_manual_reference.py48
-rw-r--r--release/scripts/presets/interface_theme/blender_light.xml6
-rw-r--r--release/scripts/startup/bl_operators/sequencer.py8
-rw-r--r--release/scripts/startup/bl_operators/wm.py18
-rw-r--r--release/scripts/startup/bl_ui/properties_data_camera.py31
-rw-r--r--release/scripts/startup/bl_ui/properties_data_mesh.py6
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_softbody.py3
-rw-r--r--release/scripts/startup/bl_ui/space_toolsystem_toolbar.py1
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py12
-rw-r--r--release/scripts/startup/nodeitems_builtins.py2
-rw-r--r--source/blender/blenkernel/BKE_anim_data.h11
-rw-r--r--source/blender/blenkernel/BKE_blender_version.h2
-rw-r--r--source/blender/blenkernel/BKE_customdata.h10
-rw-r--r--source/blender/blenkernel/BKE_deform.h7
-rw-r--r--source/blender/blenkernel/BKE_fcurve.h22
-rw-r--r--source/blender/blenkernel/BKE_global.h1
-rw-r--r--source/blender/blenkernel/BKE_idprop.h12
-rw-r--r--source/blender/blenkernel/BKE_idtype.h31
-rw-r--r--source/blender/blenkernel/BKE_lib_id.h4
-rw-r--r--source/blender/blenkernel/BKE_lib_override.h9
-rw-r--r--source/blender/blenkernel/BKE_mesh_mapping.h3
-rw-r--r--source/blender/blenkernel/BKE_mesh_remesh_voxel.h5
-rw-r--r--source/blender/blenkernel/BKE_mesh_runtime.h6
-rw-r--r--source/blender/blenkernel/BKE_nla.h13
-rw-r--r--source/blender/blenkernel/BKE_paint.h13
-rw-r--r--source/blender/blenkernel/BKE_rigidbody.h1
-rw-r--r--source/blender/blenkernel/BKE_scene.h13
-rw-r--r--source/blender/blenkernel/BKE_sequencer.h24
-rw-r--r--source/blender/blenkernel/BKE_softbody.h7
-rw-r--r--source/blender/blenkernel/intern/DerivedMesh.c6
-rw-r--r--source/blender/blenkernel/intern/action.c6
-rw-r--r--source/blender/blenkernel/intern/anim_data.c84
-rw-r--r--source/blender/blenkernel/intern/anim_sys.c18
-rw-r--r--source/blender/blenkernel/intern/armature.c6
-rw-r--r--source/blender/blenkernel/intern/bpath.c4
-rw-r--r--source/blender/blenkernel/intern/brush.c7
-rw-r--r--source/blender/blenkernel/intern/cachefile.c6
-rw-r--r--source/blender/blenkernel/intern/camera.c6
-rw-r--r--source/blender/blenkernel/intern/collection.c28
-rw-r--r--source/blender/blenkernel/intern/collision.c26
-rw-r--r--source/blender/blenkernel/intern/context.c5
-rw-r--r--source/blender/blenkernel/intern/curve.c6
-rw-r--r--source/blender/blenkernel/intern/customdata.c184
-rw-r--r--source/blender/blenkernel/intern/deform.c48
-rw-r--r--source/blender/blenkernel/intern/fcurve.c328
-rw-r--r--source/blender/blenkernel/intern/font.c6
-rw-r--r--source/blender/blenkernel/intern/gpencil.c6
-rw-r--r--source/blender/blenkernel/intern/gpencil_geom.c4
-rw-r--r--source/blender/blenkernel/intern/hair.c6
-rw-r--r--source/blender/blenkernel/intern/idprop.c270
-rw-r--r--source/blender/blenkernel/intern/image.c5
-rw-r--r--source/blender/blenkernel/intern/ipo.c10
-rw-r--r--source/blender/blenkernel/intern/key.c6
-rw-r--r--source/blender/blenkernel/intern/lattice.c68
-rw-r--r--source/blender/blenkernel/intern/lib_id.c38
-rw-r--r--source/blender/blenkernel/intern/lib_override.c441
-rw-r--r--source/blender/blenkernel/intern/lib_remap.c12
-rw-r--r--source/blender/blenkernel/intern/library.c6
-rw-r--r--source/blender/blenkernel/intern/light.c6
-rw-r--r--source/blender/blenkernel/intern/lightprobe.c6
-rw-r--r--source/blender/blenkernel/intern/linestyle.c6
-rw-r--r--source/blender/blenkernel/intern/mask.c6
-rw-r--r--source/blender/blenkernel/intern/material.c6
-rw-r--r--source/blender/blenkernel/intern/mball.c6
-rw-r--r--source/blender/blenkernel/intern/mesh.c165
-rw-r--r--source/blender/blenkernel/intern/mesh_remesh_voxel.c135
-rw-r--r--source/blender/blenkernel/intern/movieclip.c5
-rw-r--r--source/blender/blenkernel/intern/multires_reshape.c6
-rw-r--r--source/blender/blenkernel/intern/nla.c133
-rw-r--r--source/blender/blenkernel/intern/node.c5
-rw-r--r--source/blender/blenkernel/intern/object.c27
-rw-r--r--source/blender/blenkernel/intern/paint.c12
-rw-r--r--source/blender/blenkernel/intern/particle.c6
-rw-r--r--source/blender/blenkernel/intern/pointcloud.c6
-rw-r--r--source/blender/blenkernel/intern/rigidbody.c32
-rw-r--r--source/blender/blenkernel/intern/scene.c122
-rw-r--r--source/blender/blenkernel/intern/screen.c6
-rw-r--r--source/blender/blenkernel/intern/seqeffects.c20
-rw-r--r--source/blender/blenkernel/intern/sequencer.c25
-rw-r--r--source/blender/blenkernel/intern/simulation.cc6
-rw-r--r--source/blender/blenkernel/intern/softbody.c233
-rw-r--r--source/blender/blenkernel/intern/sound.c5
-rw-r--r--source/blender/blenkernel/intern/speaker.c6
-rw-r--r--source/blender/blenkernel/intern/text.c6
-rw-r--r--source/blender/blenkernel/intern/texture.c6
-rw-r--r--source/blender/blenkernel/intern/volume.cc5
-rw-r--r--source/blender/blenkernel/intern/workspace.c6
-rw-r--r--source/blender/blenkernel/intern/world.c6
-rw-r--r--source/blender/blenlib/BLI_array.h2
-rw-r--r--source/blender/blenlib/BLI_array.hh124
-rw-r--r--source/blender/blenlib/BLI_delaunay_2d.h85
-rw-r--r--source/blender/blenlib/BLI_double2.hh143
-rw-r--r--source/blender/blenlib/BLI_double3.hh245
-rw-r--r--source/blender/blenlib/BLI_float2.hh46
-rw-r--r--source/blender/blenlib/BLI_float3.hh5
-rw-r--r--source/blender/blenlib/BLI_map.hh277
-rw-r--r--source/blender/blenlib/BLI_map_slots.hh95
-rw-r--r--source/blender/blenlib/BLI_math_base.h8
-rw-r--r--source/blender/blenlib/BLI_math_boolean.hh62
-rw-r--r--source/blender/blenlib/BLI_math_matrix.h3
-rw-r--r--source/blender/blenlib/BLI_math_mpq.hh36
-rw-r--r--source/blender/blenlib/BLI_math_vector.h11
-rw-r--r--source/blender/blenlib/BLI_memory_utils.hh56
-rw-r--r--source/blender/blenlib/BLI_mesh_boolean.hh79
-rw-r--r--source/blender/blenlib/BLI_mesh_intersect.hh359
-rw-r--r--source/blender/blenlib/BLI_mpq2.hh184
-rw-r--r--source/blender/blenlib/BLI_mpq3.hh281
-rw-r--r--source/blender/blenlib/BLI_set.hh114
-rw-r--r--source/blender/blenlib/BLI_set_slots.hh47
-rw-r--r--source/blender/blenlib/BLI_stack.hh95
-rw-r--r--source/blender/blenlib/BLI_vector.hh126
-rw-r--r--source/blender/blenlib/BLI_winstuff.h12
-rw-r--r--source/blender/blenlib/CMakeLists.txt31
-rw-r--r--source/blender/blenlib/intern/delaunay_2d.c5170
-rw-r--r--source/blender/blenlib/intern/delaunay_2d.cc2500
-rw-r--r--source/blender/blenlib/intern/math_boolean.cc2533
-rw-r--r--source/blender/blenlib/intern/math_matrix.c109
-rw-r--r--source/blender/blenlib/intern/math_vec.cc195
-rw-r--r--source/blender/blenlib/intern/math_vector.c20
-rw-r--r--source/blender/blenlib/intern/math_vector_inline.c45
-rw-r--r--source/blender/blenlib/intern/mesh_boolean.cc3382
-rw-r--r--source/blender/blenlib/intern/mesh_intersect.cc3304
-rw-r--r--source/blender/blenlib/intern/path_util.c6
-rw-r--r--source/blender/blenlib/intern/winstuff.c52
-rw-r--r--source/blender/blenlib/tests/BLI_array_test.cc62
-rw-r--r--source/blender/blenlib/tests/BLI_delaunay_2d_test.cc2378
-rw-r--r--source/blender/blenlib/tests/BLI_exception_safety_test_utils.hh102
-rw-r--r--source/blender/blenlib/tests/BLI_map_test.cc67
-rw-r--r--source/blender/blenlib/tests/BLI_memory_utils_test.cc2
-rw-r--r--source/blender/blenlib/tests/BLI_mesh_boolean_test.cc910
-rw-r--r--source/blender/blenlib/tests/BLI_mesh_intersect_test.cc1074
-rw-r--r--source/blender/blenlib/tests/BLI_set_test.cc50
-rw-r--r--source/blender/blenlib/tests/BLI_stack_cxx_test.cc56
-rw-r--r--source/blender/blenlib/tests/BLI_vector_test.cc116
-rw-r--r--source/blender/blenloader/BLO_read_write.h7
-rw-r--r--source/blender/blenloader/intern/readfile.c1632
-rw-r--r--source/blender/blenloader/intern/versioning_250.c16
-rw-r--r--source/blender/blenloader/intern/versioning_260.c12
-rw-r--r--source/blender/blenloader/intern/versioning_270.c19
-rw-r--r--source/blender/blenloader/intern/versioning_280.c4
-rw-r--r--source/blender/blenloader/intern/versioning_290.c92
-rw-r--r--source/blender/blenloader/intern/versioning_legacy.c12
-rw-r--r--source/blender/blenloader/intern/versioning_userdef.c15
-rw-r--r--source/blender/blenloader/intern/writefile.c801
-rw-r--r--source/blender/bmesh/CMakeLists.txt14
-rw-r--r--source/blender/bmesh/bmesh_tools.h1
-rw-r--r--source/blender/bmesh/tools/bmesh_boolean.cc479
-rw-r--r--source/blender/bmesh/tools/bmesh_boolean.h45
-rw-r--r--source/blender/bmesh/tools/bmesh_decimate_collapse.c2
-rw-r--r--source/blender/bmesh/tools/bmesh_edgesplit.h8
-rw-r--r--source/blender/depsgraph/DEG_depsgraph.h8
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes.cc8
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.cc65
-rw-r--r--source/blender/depsgraph/intern/depsgraph.cc8
-rw-r--r--source/blender/depsgraph/intern/depsgraph.h5
-rw-r--r--source/blender/depsgraph/intern/depsgraph_eval.cc37
-rw-r--r--source/blender/depsgraph/intern/depsgraph_tag.cc29
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_flush.cc11
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.cc8
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_time.cc10
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_time.h4
-rw-r--r--source/blender/draw/CMakeLists.txt22
-rw-r--r--source/blender/draw/engines/eevee/eevee_data.c2
-rw-r--r--source/blender/draw/engines/eevee/eevee_engine.c2
-rw-r--r--source/blender/draw/engines/eevee/eevee_lightcache.c12
-rw-r--r--source/blender/draw/engines/eevee/eevee_lightprobes.c16
-rw-r--r--source/blender/draw/engines/eevee/eevee_lights.c2
-rw-r--r--source/blender/draw/engines/eevee/eevee_lookdev.c2
-rw-r--r--source/blender/draw/engines/eevee/eevee_materials.c57
-rw-r--r--source/blender/draw/engines/eevee/eevee_private.h38
-rw-r--r--source/blender/draw/engines/eevee/eevee_render.c2
-rw-r--r--source/blender/draw/engines/eevee/eevee_shadows.c10
-rw-r--r--source/blender/draw/engines/eevee/eevee_subsurface.c2
-rw-r--r--source/blender/draw/engines/eevee/eevee_temporal_sampling.c7
-rw-r--r--source/blender/draw/engines/eevee/eevee_volumes.c3
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_draw_data.c8
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_engine.c15
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_engine.h16
-rw-r--r--source/blender/draw/engines/overlay/overlay_private.h10
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl1
-rw-r--r--source/blender/draw/engines/workbench/workbench_data.c18
-rw-r--r--source/blender/draw/engines/workbench/workbench_effect_cavity.c4
-rw-r--r--source/blender/draw/engines/workbench/workbench_effect_dof.c6
-rw-r--r--source/blender/draw/engines/workbench/workbench_engine.c7
-rw-r--r--source/blender/draw/engines/workbench/workbench_materials.c2
-rw-r--r--source/blender/draw/engines/workbench/workbench_private.h19
-rw-r--r--source/blender/draw/intern/DRW_render.h13
-rw-r--r--source/blender/draw/intern/draw_cache_extract.h1
-rw-r--r--source/blender/draw/intern/draw_cache_extract_mesh.c300
-rw-r--r--source/blender/draw/intern/draw_cache_impl_curve.c14
-rw-r--r--source/blender/draw/intern/draw_cache_impl_mesh.c16
-rw-r--r--source/blender/draw/intern/draw_common.c5
-rw-r--r--source/blender/draw/intern/draw_common.h4
-rw-r--r--source/blender/draw/intern/draw_manager.c47
-rw-r--r--source/blender/draw/intern/draw_manager.h6
-rw-r--r--source/blender/draw/intern/draw_manager_data.c48
-rw-r--r--source/blender/draw/intern/draw_manager_exec.c58
-rw-r--r--source/blender/draw/intern/draw_manager_testing.h39
-rw-r--r--source/blender/draw/intern/draw_view.c2
-rw-r--r--source/blender/draw/tests/shaders_test.cc270
-rw-r--r--source/blender/editors/animation/anim_filter.c2
-rw-r--r--source/blender/editors/armature/armature_relations.c6
-rw-r--r--source/blender/editors/armature/meshlaplacian.c4
-rw-r--r--source/blender/editors/curve/editcurve.c111
-rw-r--r--source/blender/editors/curve/editcurve_paint.c4
-rw-r--r--source/blender/editors/gizmo_library/gizmo_draw_utils.c4
-rw-r--r--source/blender/editors/gpencil/annotate_draw.c4
-rw-r--r--source/blender/editors/gpencil/drawgpencil.c4
-rw-r--r--source/blender/editors/gpencil/gpencil_data.c8
-rw-r--r--source/blender/editors/gpencil/gpencil_fill.c5
-rw-r--r--source/blender/editors/include/ED_screen.h8
-rw-r--r--source/blender/editors/include/UI_interface.h13
-rw-r--r--source/blender/editors/include/UI_resources.h6
-rw-r--r--source/blender/editors/interface/interface.c75
-rw-r--r--source/blender/editors/interface/interface_anim.c4
-rw-r--r--source/blender/editors/interface/interface_context_menu.c12
-rw-r--r--source/blender/editors/interface/interface_draw.c167
-rw-r--r--source/blender/editors/interface/interface_eyedropper_colorband.c6
-rw-r--r--source/blender/editors/interface/interface_eyedropper_datablock.c2
-rw-r--r--source/blender/editors/interface/interface_eyedropper_driver.c6
-rw-r--r--source/blender/editors/interface/interface_handlers.c694
-rw-r--r--source/blender/editors/interface/interface_icons.c60
-rw-r--r--source/blender/editors/interface/interface_icons_event.c8
-rw-r--r--source/blender/editors/interface/interface_intern.h4
-rw-r--r--source/blender/editors/interface/interface_layout.c32
-rw-r--r--source/blender/editors/interface/interface_ops.c26
-rw-r--r--source/blender/editors/interface/interface_panel.c1838
-rw-r--r--source/blender/editors/interface/interface_query.c4
-rw-r--r--source/blender/editors/interface/interface_region_color_picker.c2
-rw-r--r--source/blender/editors/interface/interface_region_hud.c9
-rw-r--r--source/blender/editors/interface/interface_region_menu_pie.c2
-rw-r--r--source/blender/editors/interface/interface_region_popover.c4
-rw-r--r--source/blender/editors/interface/interface_region_popup.c8
-rw-r--r--source/blender/editors/interface/interface_region_search.c8
-rw-r--r--source/blender/editors/interface/interface_region_tooltip.c26
-rw-r--r--source/blender/editors/interface/interface_style.c5
-rw-r--r--source/blender/editors/interface/interface_template_search_menu.c6
-rw-r--r--source/blender/editors/interface/interface_template_search_operator.c2
-rw-r--r--source/blender/editors/interface/interface_templates.c89
-rw-r--r--source/blender/editors/interface/interface_utils.c4
-rw-r--r--source/blender/editors/interface/interface_widgets.c153
-rw-r--r--source/blender/editors/interface/resources.c10
-rw-r--r--source/blender/editors/interface/view2d.c32
-rw-r--r--source/blender/editors/interface/view2d_draw.c54
-rw-r--r--source/blender/editors/interface/view2d_ops.c48
-rw-r--r--source/blender/editors/mask/mask_add.c6
-rw-r--r--source/blender/editors/mask/mask_draw.c2
-rw-r--r--source/blender/editors/mesh/CMakeLists.txt4
-rw-r--r--source/blender/editors/mesh/editmesh_intersect.c182
-rw-r--r--source/blender/editors/mesh/editmesh_knife.c4
-rw-r--r--source/blender/editors/mesh/editmesh_preselect_edgering.c4
-rw-r--r--source/blender/editors/mesh/editmesh_preselect_elem.c4
-rw-r--r--source/blender/editors/mesh/editmesh_select.c34
-rw-r--r--source/blender/editors/mesh/editmesh_utils.c2
-rw-r--r--source/blender/editors/mesh/mesh_data.c4
-rw-r--r--source/blender/editors/mesh/meshtools.c6
-rw-r--r--source/blender/editors/object/object_add.c55
-rw-r--r--source/blender/editors/object/object_intern.h1
-rw-r--r--source/blender/editors/object/object_ops.c1
-rw-r--r--source/blender/editors/object/object_relations.c131
-rw-r--r--source/blender/editors/object/object_remesh.c53
-rw-r--r--source/blender/editors/render/render_opengl.c2
-rw-r--r--source/blender/editors/render/render_shading.c241
-rw-r--r--source/blender/editors/render/render_update.c2
-rw-r--r--source/blender/editors/scene/scene_edit.c2
-rw-r--r--source/blender/editors/screen/area.c133
-rw-r--r--source/blender/editors/screen/screen_context.c38
-rw-r--r--source/blender/editors/screen/screen_draw.c10
-rw-r--r--source/blender/editors/screen/screen_edit.c71
-rw-r--r--source/blender/editors/screen/screen_geometry.c9
-rw-r--r--source/blender/editors/screen/screen_ops.c36
-rw-r--r--source/blender/editors/screen/workspace_edit.c6
-rw-r--r--source/blender/editors/screen/workspace_layout_edit.c3
-rw-r--r--source/blender/editors/sculpt_paint/paint_cursor.c12
-rw-r--r--source/blender/editors/sculpt_paint/paint_mask.c604
-rw-r--r--source/blender/editors/sculpt_paint/paint_stroke.c10
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c28
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_cloth.c91
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_mesh.c170
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h5
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_smooth.c37
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_transform.c6
-rw-r--r--source/blender/editors/sound/sound_ops.c4
-rw-r--r--source/blender/editors/space_action/space_action.c2
-rw-r--r--source/blender/editors/space_buttons/buttons_context.c4
-rw-r--r--source/blender/editors/space_buttons/space_buttons.c4
-rw-r--r--source/blender/editors/space_clip/space_clip.c4
-rw-r--r--source/blender/editors/space_console/space_console.c1
-rw-r--r--source/blender/editors/space_file/file_draw.c4
-rw-r--r--source/blender/editors/space_file/file_ops.c8
-rw-r--r--source/blender/editors/space_file/filelist.c2
-rw-r--r--source/blender/editors/space_file/space_file.c5
-rw-r--r--source/blender/editors/space_graph/space_graph.c10
-rw-r--r--source/blender/editors/space_image/image_draw.c2
-rw-r--r--source/blender/editors/space_image/space_image.c8
-rw-r--r--source/blender/editors/space_info/info_draw.c1
-rw-r--r--source/blender/editors/space_info/info_stats.c2
-rw-r--r--source/blender/editors/space_info/space_info.c1
-rw-r--r--source/blender/editors/space_nla/nla_edit.c6
-rw-r--r--source/blender/editors/space_nla/space_nla.c2
-rw-r--r--source/blender/editors/space_node/node_draw.c6
-rw-r--r--source/blender/editors/space_node/node_templates.c4
-rw-r--r--source/blender/editors/space_outliner/outliner_draw.c102
-rw-r--r--source/blender/editors/space_outliner/outliner_edit.c37
-rw-r--r--source/blender/editors/space_outliner/outliner_intern.h9
-rw-r--r--source/blender/editors/space_outliner/outliner_select.c58
-rw-r--r--source/blender/editors/space_outliner/outliner_tools.c114
-rw-r--r--source/blender/editors/space_outliner/outliner_tree.c76
-rw-r--r--source/blender/editors/space_outliner/outliner_utils.c18
-rw-r--r--source/blender/editors/space_outliner/space_outliner.c19
-rw-r--r--source/blender/editors/space_script/space_script.c1
-rw-r--r--source/blender/editors/space_sequencer/sequencer_draw.c20
-rw-r--r--source/blender/editors/space_sequencer/sequencer_edit.c76
-rw-r--r--source/blender/editors/space_sequencer/sequencer_modifier.c4
-rw-r--r--source/blender/editors/space_sequencer/sequencer_select.c56
-rw-r--r--source/blender/editors/space_text/space_text.c1
-rw-r--r--source/blender/editors/space_text/text_draw.c17
-rw-r--r--source/blender/editors/space_userpref/space_userpref.c3
-rw-r--r--source/blender/editors/space_view3d/space_view3d.c6
-rw-r--r--source/blender/editors/space_view3d/view3d_draw.c8
-rw-r--r--source/blender/editors/space_view3d/view3d_placement.c8
-rw-r--r--source/blender/editors/space_view3d/view3d_select.c9
-rw-r--r--source/blender/editors/space_view3d/view3d_view.c4
-rw-r--r--source/blender/editors/transform/transform_constraints.c14
-rw-r--r--source/blender/editors/transform/transform_convert_object.c4
-rw-r--r--source/blender/editors/transform/transform_gizmo_3d.c4
-rw-r--r--source/blender/editors/transform/transform_mode_edge_slide.c4
-rw-r--r--source/blender/editors/transform/transform_mode_vert_slide.c4
-rw-r--r--source/blender/editors/transform/transform_snap.c4
-rw-r--r--source/blender/editors/uvedit/uvedit_draw.c19
-rw-r--r--source/blender/editors/uvedit/uvedit_intern.h3
-rw-r--r--source/blender/editors/uvedit/uvedit_select.c379
-rw-r--r--source/blender/functions/tests/FN_array_spans_test.cc4
-rw-r--r--source/blender/gpu/CMakeLists.txt11
-rw-r--r--source/blender/gpu/GPU_context.h1
-rw-r--r--source/blender/gpu/GPU_extensions.h1
-rw-r--r--source/blender/gpu/GPU_framebuffer.h1
-rw-r--r--source/blender/gpu/GPU_immediate.h3
-rw-r--r--source/blender/gpu/GPU_material.h20
-rw-r--r--source/blender/gpu/GPU_shader.h50
-rw-r--r--source/blender/gpu/GPU_shader_interface.h117
-rw-r--r--source/blender/gpu/GPU_state.h14
-rw-r--r--source/blender/gpu/GPU_uniform_buffer.h61
-rw-r--r--source/blender/gpu/GPU_uniformbuffer.h53
-rw-r--r--source/blender/gpu/GPU_viewport.h4
-rw-r--r--source/blender/gpu/intern/gpu_attr_binding.cc13
-rw-r--r--source/blender/gpu/intern/gpu_attr_binding_private.h6
-rw-r--r--source/blender/gpu/intern/gpu_backend.hh11
-rw-r--r--source/blender/gpu/intern/gpu_batch_private.hh5
-rw-r--r--source/blender/gpu/intern/gpu_codegen.c2
-rw-r--r--source/blender/gpu/intern/gpu_context_private.hh3
-rw-r--r--source/blender/gpu/intern/gpu_drawlist_private.hh4
-rw-r--r--source/blender/gpu/intern/gpu_extensions.cc21
-rw-r--r--source/blender/gpu/intern/gpu_framebuffer.cc36
-rw-r--r--source/blender/gpu/intern/gpu_immediate.cc84
-rw-r--r--source/blender/gpu/intern/gpu_material.c37
-rw-r--r--source/blender/gpu/intern/gpu_matrix.cc17
-rw-r--r--source/blender/gpu/intern/gpu_node_graph.c8
-rw-r--r--source/blender/gpu/intern/gpu_select_pick.c19
-rw-r--r--source/blender/gpu/intern/gpu_select_sample_query.c39
-rw-r--r--source/blender/gpu/intern/gpu_shader.cc97
-rw-r--r--source/blender/gpu/intern/gpu_shader_builtin.c2
-rw-r--r--source/blender/gpu/intern/gpu_shader_interface.cc532
-rw-r--r--source/blender/gpu/intern/gpu_shader_interface.hh229
-rw-r--r--source/blender/gpu/intern/gpu_shader_private.hh16
-rw-r--r--source/blender/gpu/intern/gpu_state.cc65
-rw-r--r--source/blender/gpu/intern/gpu_state_private.hh25
-rw-r--r--source/blender/gpu/intern/gpu_texture.cc11
-rw-r--r--source/blender/gpu/intern/gpu_uniform_buffer.cc (renamed from source/blender/gpu/intern/gpu_uniformbuffer.cc)192
-rw-r--r--source/blender/gpu/intern/gpu_uniform_buffer_private.hh69
-rw-r--r--source/blender/gpu/intern/gpu_vertex_format.cc2
-rw-r--r--source/blender/gpu/intern/gpu_viewport.c6
-rw-r--r--source/blender/gpu/opengl/gl_backend.hh11
-rw-r--r--source/blender/gpu/opengl/gl_batch.cc57
-rw-r--r--source/blender/gpu/opengl/gl_batch.hh19
-rw-r--r--source/blender/gpu/opengl/gl_context.cc35
-rw-r--r--source/blender/gpu/opengl/gl_context.hh15
-rw-r--r--source/blender/gpu/opengl/gl_drawlist.hh6
-rw-r--r--source/blender/gpu/opengl/gl_shader.cc13
-rw-r--r--source/blender/gpu/opengl/gl_shader.hh10
-rw-r--r--source/blender/gpu/opengl/gl_shader_interface.cc297
-rw-r--r--source/blender/gpu/opengl/gl_shader_interface.hh63
-rw-r--r--source/blender/gpu/opengl/gl_state.cc22
-rw-r--r--source/blender/gpu/opengl/gl_state.hh6
-rw-r--r--source/blender/gpu/opengl/gl_uniform_buffer.cc126
-rw-r--r--source/blender/gpu/opengl/gl_uniform_buffer.hh58
-rw-r--r--source/blender/gpu/opengl/gl_vertex_array.cc14
-rw-r--r--source/blender/gpu/opengl/gl_vertex_array.hh4
-rw-r--r--source/blender/imbuf/intern/colormanagement.c4
-rw-r--r--source/blender/io/alembic/tests/abc_export_test.cc3
-rw-r--r--source/blender/io/collada/BlenderContext.cpp2
-rw-r--r--source/blender/makesdna/DNA_ID.h4
-rw-r--r--source/blender/makesdna/DNA_brush_types.h1
-rw-r--r--source/blender/makesdna/DNA_mesh_types.h1
-rw-r--r--source/blender/makesdna/DNA_modifier_types.h10
-rw-r--r--source/blender/makesdna/DNA_object_force_types.h50
-rw-r--r--source/blender/makesdna/DNA_object_types.h13
-rw-r--r--source/blender/makesdna/DNA_outliner_types.h2
-rw-r--r--source/blender/makesdna/DNA_screen_types.h5
-rw-r--r--source/blender/makesdna/DNA_userdef_types.h4
-rw-r--r--source/blender/makesdna/intern/makesdna.c1
-rw-r--r--source/blender/makesrna/intern/CMakeLists.txt4
-rw-r--r--source/blender/makesrna/intern/rna_brush.c1
-rw-r--r--source/blender/makesrna/intern/rna_color.c8
-rw-r--r--source/blender/makesrna/intern/rna_internal.h4
-rw-r--r--source/blender/makesrna/intern/rna_layer.c7
-rw-r--r--source/blender/makesrna/intern/rna_mesh.c3
-rw-r--r--source/blender/makesrna/intern/rna_modifier.c16
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c3
-rw-r--r--source/blender/makesrna/intern/rna_scene.c11
-rw-r--r--source/blender/makesrna/intern/rna_scene_api.c2
-rw-r--r--source/blender/makesrna/intern/rna_space_api.c2
-rw-r--r--source/blender/makesrna/intern/rna_userdef.c11
-rw-r--r--source/blender/modifiers/CMakeLists.txt4
-rw-r--r--source/blender/modifiers/intern/MOD_boolean.c58
-rw-r--r--source/blender/modifiers/intern/MOD_meshdeform.c37
-rw-r--r--source/blender/nodes/shader/node_shader_util.h2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_rgb.c2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_value.cc2
-rw-r--r--source/blender/python/bmesh/CMakeLists.txt4
-rw-r--r--source/blender/python/bmesh/bmesh_py_ops_call.c4
-rw-r--r--source/blender/python/bmesh/bmesh_py_types.c8
-rw-r--r--source/blender/python/bmesh/bmesh_py_types_customdata.c14
-rw-r--r--source/blender/python/bmesh/bmesh_py_types_select.c4
-rw-r--r--source/blender/python/generic/bgl.c6
-rw-r--r--source/blender/python/generic/idprop_py_api.c12
-rw-r--r--source/blender/python/generic/imbuf_py_api.c8
-rw-r--r--source/blender/python/generic/py_capi_utils.c20
-rw-r--r--source/blender/python/gpu/gpu_py_offscreen.c2
-rw-r--r--source/blender/python/gpu/gpu_py_shader.c8
-rw-r--r--source/blender/python/gpu/gpu_py_vertex_buffer.c2
-rw-r--r--source/blender/python/gpu/gpu_py_vertex_format.c4
-rw-r--r--source/blender/python/intern/bpy_app.c2
-rw-r--r--source/blender/python/intern/bpy_app_handlers.c2
-rw-r--r--source/blender/python/intern/bpy_app_icons.c8
-rw-r--r--source/blender/python/intern/bpy_app_opensubdiv.c2
-rw-r--r--source/blender/python/intern/bpy_app_timers.c4
-rw-r--r--source/blender/python/intern/bpy_app_translations.c2
-rw-r--r--source/blender/python/intern/bpy_driver.c4
-rw-r--r--source/blender/python/intern/bpy_interface.c10
-rw-r--r--source/blender/python/intern/bpy_library_load.c6
-rw-r--r--source/blender/python/intern/bpy_msgbus.c2
-rw-r--r--source/blender/python/intern/bpy_props.c10
-rw-r--r--source/blender/python/intern/bpy_rna.c59
-rw-r--r--source/blender/python/intern/bpy_rna_anim.c4
-rw-r--r--source/blender/python/intern/bpy_rna_array.c14
-rw-r--r--source/blender/python/intern/bpy_rna_callback.c2
-rw-r--r--source/blender/python/intern/bpy_rna_driver.c2
-rw-r--r--source/blender/python/intern/bpy_rna_gizmo.c12
-rw-r--r--source/blender/python/mathutils/mathutils.c8
-rw-r--r--source/blender/python/mathutils/mathutils_Color.c4
-rw-r--r--source/blender/python/mathutils/mathutils_Matrix.c5
-rw-r--r--source/blender/python/mathutils/mathutils_Vector.c4
-rw-r--r--source/blender/python/mathutils/mathutils_bvhtree.c4
-rw-r--r--source/blender/python/mathutils/mathutils_geometry.c5
-rw-r--r--source/blender/python/mathutils/mathutils_kdtree.c2
-rw-r--r--source/blender/render/intern/source/pipeline.c2
-rw-r--r--source/blender/windowmanager/WM_types.h11
-rw-r--r--source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c12
-rw-r--r--source/blender/windowmanager/intern/wm.c6
-rw-r--r--source/blender/windowmanager/intern/wm_draw.c6
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c15
-rw-r--r--source/blender/windowmanager/intern/wm_files.c2
-rw-r--r--source/blender/windowmanager/intern/wm_gesture.c4
-rw-r--r--source/blender/windowmanager/intern/wm_init_exit.c2
-rw-r--r--source/blender/windowmanager/intern/wm_playanim.c6
-rw-r--r--source/blender/windowmanager/intern/wm_window.c5
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_draw.c2
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_session.c2
-rw-r--r--source/creator/CMakeLists.txt10
-rw-r--r--source/creator/creator_args.c22
m---------source/tools0
-rw-r--r--tests/CMakeLists.txt2
-rw-r--r--tests/gtests/testing/testing_main.cc2
-rw-r--r--tests/python/CMakeLists.txt28
-rw-r--r--tests/python/collada/CMakeLists.txt6
-rw-r--r--tests/python/cycles_render_tests.py2
-rw-r--r--tests/python/eevee_render_tests.py2
-rwxr-xr-xtests/python/modules/render_report.py11
-rwxr-xr-xtests/python/modules/test_utils.py2
-rw-r--r--tests/python/opengl_draw_tests.py2
-rw-r--r--tests/python/view_layer/CMakeLists.txt2
-rw-r--r--tests/python/workbench_render_tests.py2
536 files changed, 28935 insertions, 17601 deletions
diff --git a/.clang-format b/.clang-format
index 4db1b664fdb..8a992fea3a9 100644
--- a/.clang-format
+++ b/.clang-format
@@ -253,8 +253,8 @@ ForEachMacros:
- RNA_STRUCT_BEGIN_SKIP_RNA_TYPE
- SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN
- - SEQP_BEGIN
- - SEQ_BEGIN
+ - SEQ_ALL_BEGIN
+ - SEQ_CURRENT_BEGIN
- SURFACE_QUAD_ITER_BEGIN
- foreach
- ED_screen_areas_iter
diff --git a/CMakeLists.txt b/CMakeLists.txt
index cb1c7208c14..e8df9386a4e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -102,6 +102,11 @@ if(POLICY CMP0068)
cmake_policy(SET CMP0068 NEW)
endif()
+# find_package() uses <PackageName>_ROOT variables.
+if(POLICY CMP0074)
+ cmake_policy(SET CMP0074 NEW)
+endif()
+
#-----------------------------------------------------------------------------
# Load some macros.
include(build_files/cmake/macros.cmake)
@@ -183,6 +188,7 @@ if(APPLE)
else()
option(WITH_XR_OPENXR "Enable VR features through the OpenXR specification" ON)
endif()
+option(WITH_GMP "Enable features depending on GMP (Exact Boolean)" ON)
# Compositor
option(WITH_COMPOSITOR "Enable the tile based nodal compositor" ON)
@@ -1569,6 +1575,12 @@ endif()
if(MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++17")
+ # Make MSVC properly report the value of the __cplusplus preprocessor macro
+ # Available MSVC 15.7 (1914) and up, without this it reports 199711L regardless
+ # of the C++ standard chosen above
+ if(MSVC_VERSION GREATER 1913)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:__cplusplus")
+ endif()
elseif(
CMAKE_COMPILER_IS_GNUCC OR
CMAKE_C_COMPILER_ID MATCHES "Clang" OR
@@ -1732,6 +1744,7 @@ if(FIRST_RUN)
info_cfg_option(WITH_TETGEN)
info_cfg_option(WITH_USD)
info_cfg_option(WITH_TBB)
+ info_cfg_option(WITH_GMP)
info_cfg_text("Compiler Options:")
info_cfg_option(WITH_BUILDINFO)
diff --git a/build_files/build_environment/CMakeLists.txt b/build_files/build_environment/CMakeLists.txt
index 6415d270773..6368430235c 100644
--- a/build_files/build_environment/CMakeLists.txt
+++ b/build_files/build_environment/CMakeLists.txt
@@ -120,6 +120,7 @@ endif()
if(NOT WIN32 OR ENABLE_MINGW64)
include(cmake/gmp.cmake)
include(cmake/openjpeg.cmake)
+ include(cmake/gmp.cmake)
if(NOT WIN32 OR BUILD_MODE STREQUAL Release)
if(WIN32)
include(cmake/zlib_mingw.cmake)
diff --git a/build_files/build_environment/cmake/versions.cmake b/build_files/build_environment/cmake/versions.cmake
index 30a0b1184c2..eb5df8204b5 100644
--- a/build_files/build_environment/cmake/versions.cmake
+++ b/build_files/build_environment/cmake/versions.cmake
@@ -312,6 +312,7 @@ set(NASM_HASH f4fd1329b1713e1ccd34b2fc121c4bcd278c9f91cc4cb205ae8fcd2e4728dd14)
set(XR_OPENXR_SDK_VERSION 1.0.8)
set(XR_OPENXR_SDK_URI https://github.com/KhronosGroup/OpenXR-SDK/archive/release-${XR_OPENXR_SDK_VERSION}.tar.gz)
set(XR_OPENXR_SDK_HASH c6de63d2e0f9029aa58dfa97cad8ce07)
+
set(ISPC_VERSION v1.13.0)
set(ISPC_URI https://github.com/ispc/ispc/archive/${ISPC_VERSION}.tar.gz)
set(ISPC_HASH 4bf5e8d0020c4b9980faa702c1a6f25f)
diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh
index 130173d7f01..43606f49c78 100755
--- a/build_files/build_environment/install_deps.sh
+++ b/build_files/build_environment/install_deps.sh
@@ -1087,7 +1087,7 @@ Those libraries should be available as packages in all recent distributions (opt
* libjpeg, libpng, libtiff, [openjpeg2], [libopenal].
* libx11, libxcursor, libxi, libxrandr, libxinerama (and other libx... as needed).
* libsqlite3, libbz2, libssl, libfftw3, libxml2, libtinyxml, yasm, libyaml-cpp.
- * libsdl2, libglew, [libgmp], [libglewmx].\""
+ * libsdl2, libglew, [libgmp], [libglewmx], fontconfig.\""
DEPS_SPECIFIC_INFO="\"BUILDABLE DEPENDENCIES:
@@ -3654,7 +3654,7 @@ install_DEB() {
THEORA_DEV="libtheora-dev"
_packages="gawk cmake cmake-curses-gui build-essential libjpeg-dev libpng-dev libtiff-dev \
- git libfreetype6-dev libx11-dev flex bison libxxf86vm-dev \
+ git libfreetype6-dev libfontconfig-dev libx11-dev flex bison libxxf86vm-dev \
libxcursor-dev libxi-dev wget libsqlite3-dev libxrandr-dev libxinerama-dev \
libbz2-dev libncurses5-dev libssl-dev liblzma-dev libreadline-dev \
libopenal-dev libglew-dev yasm $THEORA_DEV $VORBIS_DEV $OGG_DEV \
@@ -4320,7 +4320,7 @@ install_RPM() {
OGG_DEV="libogg-devel"
THEORA_DEV="libtheora-devel"
- _packages="gcc gcc-c++ git make cmake tar bzip2 xz findutils flex bison \
+ _packages="gcc gcc-c++ git make cmake tar bzip2 xz findutils flex bison fontconfig-devel \
libtiff-devel libjpeg-devel libpng-devel sqlite-devel fftw-devel SDL2-devel \
libX11-devel libXi-devel libXcursor-devel libXrandr-devel libXinerama-devel \
wget ncurses-devel readline-devel $OPENJPEG_DEV openal-soft-devel \
@@ -4898,7 +4898,7 @@ install_ARCH() {
BASE_DEVEL=`pacman -Sgq base-devel | sed -e 's/^gcc$/gcc-multilib/g' | paste -s -d' '`
fi
- _packages="$BASE_DEVEL git cmake \
+ _packages="$BASE_DEVEL git cmake fontconfig \
libxi libxcursor libxrandr libxinerama glew libpng libtiff wget openal \
$OPENJPEG_DEV $VORBIS_DEV $OGG_DEV $THEORA_DEV yasm sdl2 fftw \
libxml2 yaml-cpp tinyxml python-requests jemalloc gmp"
@@ -5577,7 +5577,7 @@ print_info() {
_buildargs="-U *SNDFILE* -U PYTHON* -U *BOOST* -U *Boost* -U *TBB*"
_buildargs="$_buildargs -U *OPENCOLORIO* -U *OPENEXR* -U *OPENIMAGEIO* -U *LLVM* -U *CYCLES*"
- _buildargs="$_buildargs -U *OPENSUBDIV* -U *OPENVDB* -U *COLLADA* -U *FFMPEG* -U *ALEMBIC* -U *USD*"
+ _buildargs="$_buildargs -U *OPENSUBDIV* -U *OPENVDB* -U *BLOSC* -U *COLLADA* -U *FFMPEG* -U *ALEMBIC* -U *USD*"
_buildargs="$_buildargs -U *EMBREE* -U *OPENIMAGEDENOISE* -U *OPENXR*"
_1="-D WITH_CODEC_SNDFILE=ON"
diff --git a/build_files/cmake/Modules/FindEmbree.cmake b/build_files/cmake/Modules/FindEmbree.cmake
index ccd0d6cd40a..2b3cd8e20c4 100644
--- a/build_files/cmake/Modules/FindEmbree.cmake
+++ b/build_files/cmake/Modules/FindEmbree.cmake
@@ -59,6 +59,14 @@ FOREACH(COMPONENT ${_embree_FIND_COMPONENTS})
PATH_SUFFIXES
lib64 lib
)
+ IF (NOT EMBREE_${UPPERCOMPONENT}_LIBRARY)
+ IF (EMBREE_EMBREE3_LIBRARY)
+ # If we can't find all the static libraries, try to fall back to the shared library if found.
+ # This allows building with a shared embree library
+ SET(_embree_LIBRARIES ${EMBREE_EMBREE3_LIBRARY})
+ BREAK()
+ ENDIF ()
+ ENDIF ()
LIST(APPEND _embree_LIBRARIES "${EMBREE_${UPPERCOMPONENT}_LIBRARY}")
ENDFOREACH()
diff --git a/build_files/cmake/Modules/GTestTesting.cmake b/build_files/cmake/Modules/GTestTesting.cmake
index a744f4202da..053d5196f41 100644
--- a/build_files/cmake/Modules/GTestTesting.cmake
+++ b/build_files/cmake/Modules/GTestTesting.cmake
@@ -70,6 +70,9 @@ macro(BLENDER_SRC_GTEST_EX)
if(WITH_TBB)
target_link_libraries(${TARGET_NAME} ${TBB_LIBRARIES})
endif()
+ if(WITH_GMP)
+ target_link_libraries(${TARGET_NAME} ${GMP_LIBRARIES})
+ endif()
get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(GENERATOR_IS_MULTI_CONFIG)
diff --git a/build_files/cmake/config/blender_full.cmake b/build_files/cmake/config/blender_full.cmake
index 7d3284af158..0f27ff61e8b 100644
--- a/build_files/cmake/config/blender_full.cmake
+++ b/build_files/cmake/config/blender_full.cmake
@@ -15,11 +15,12 @@ set(WITH_CYCLES_EMBREE ON CACHE BOOL "" FORCE)
set(WITH_CYCLES_OSL ON CACHE BOOL "" FORCE)
set(WITH_DRACO ON CACHE BOOL "" FORCE)
set(WITH_FFTW3 ON CACHE BOOL "" FORCE)
-set(WITH_GMP OFF CACHE BOOL "" FORCE)
+set(WITH_GMP ON CACHE BOOL "" FORCE)
set(WITH_LIBMV ON CACHE BOOL "" FORCE)
set(WITH_LIBMV_SCHUR_SPECIALIZATIONS ON CACHE BOOL "" FORCE)
set(WITH_COMPOSITOR ON CACHE BOOL "" FORCE)
set(WITH_FREESTYLE ON CACHE BOOL "" FORCE)
+set(WITH_GMP ON CACHE BOOL "" FORCE)
set(WITH_IK_SOLVER ON CACHE BOOL "" FORCE)
set(WITH_IK_ITASC ON CACHE BOOL "" FORCE)
set(WITH_IMAGE_CINEON ON CACHE BOOL "" FORCE)
diff --git a/build_files/cmake/config/blender_lite.cmake b/build_files/cmake/config/blender_lite.cmake
index 16c15961c59..7a664bbc008 100644
--- a/build_files/cmake/config/blender_lite.cmake
+++ b/build_files/cmake/config/blender_lite.cmake
@@ -25,6 +25,7 @@ set(WITH_LIBMV OFF CACHE BOOL "" FORCE)
set(WITH_LLVM OFF CACHE BOOL "" FORCE)
set(WITH_COMPOSITOR OFF CACHE BOOL "" FORCE)
set(WITH_FREESTYLE OFF CACHE BOOL "" FORCE)
+set(WITH_GMP OFF CACHE BOOL "" FORCE)
set(WITH_IK_SOLVER OFF CACHE BOOL "" FORCE)
set(WITH_IK_ITASC OFF CACHE BOOL "" FORCE)
set(WITH_IMAGE_CINEON OFF CACHE BOOL "" FORCE)
diff --git a/build_files/cmake/config/blender_release.cmake b/build_files/cmake/config/blender_release.cmake
index ddd9aa1d766..93962d7ebb0 100644
--- a/build_files/cmake/config/blender_release.cmake
+++ b/build_files/cmake/config/blender_release.cmake
@@ -16,11 +16,12 @@ set(WITH_CYCLES_EMBREE ON CACHE BOOL "" FORCE)
set(WITH_CYCLES_OSL ON CACHE BOOL "" FORCE)
set(WITH_DRACO ON CACHE BOOL "" FORCE)
set(WITH_FFTW3 ON CACHE BOOL "" FORCE)
-set(WITH_GMP OFF CACHE BOOL "" FORCE)
+set(WITH_GMP ON CACHE BOOL "" FORCE)
set(WITH_LIBMV ON CACHE BOOL "" FORCE)
set(WITH_LIBMV_SCHUR_SPECIALIZATIONS ON CACHE BOOL "" FORCE)
set(WITH_COMPOSITOR ON CACHE BOOL "" FORCE)
set(WITH_FREESTYLE ON CACHE BOOL "" FORCE)
+set(WITH_GMP ON CACHE BOOL "" FORCE)
set(WITH_IK_SOLVER ON CACHE BOOL "" FORCE)
set(WITH_IK_ITASC ON CACHE BOOL "" FORCE)
set(WITH_IMAGE_CINEON ON CACHE BOOL "" FORCE)
diff --git a/build_files/cmake/macros.cmake b/build_files/cmake/macros.cmake
index 51cfadecc3e..dcab6d58870 100644
--- a/build_files/cmake/macros.cmake
+++ b/build_files/cmake/macros.cmake
@@ -496,6 +496,10 @@ function(SETUP_LIBDIRS)
link_directories(${ALEMBIC_LIBPATH})
endif()
+ if(WITH_GMP)
+ link_directories(${GMP_LIBPATH})
+ endif()
+
if(WITH_GHOST_WAYLAND)
link_directories(
${wayland-client_LIBRARY_DIRS}
diff --git a/build_files/cmake/platform/platform_apple.cmake b/build_files/cmake/platform/platform_apple.cmake
index ace5de3330c..822110cb88f 100644
--- a/build_files/cmake/platform/platform_apple.cmake
+++ b/build_files/cmake/platform/platform_apple.cmake
@@ -407,6 +407,15 @@ if(WITH_TBB)
find_package(TBB)
endif()
+if(WITH_GMP)
+ find_package(GMP)
+
+ if(NOT GMP_FOUND)
+ set(WITH_GMP OFF)
+ message(STATUS "GMP not found")
+ endif()
+endif()
+
# CMake FindOpenMP doesn't know about AppleClang before 3.12, so provide custom flags.
if(WITH_OPENMP)
if(CMAKE_C_COMPILER_ID MATCHES "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL "7.0")
diff --git a/build_files/cmake/platform/platform_unix.cmake b/build_files/cmake/platform/platform_unix.cmake
index 83909a0cf66..3a7875ca46c 100644
--- a/build_files/cmake/platform/platform_unix.cmake
+++ b/build_files/cmake/platform/platform_unix.cmake
@@ -427,6 +427,15 @@ if(WITH_TBB)
find_package_wrapper(TBB)
endif()
+if(WITH_GMP)
+ find_package(GMP)
+
+ if(NOT GMP_FOUND)
+ set(WITH_GMP OFF)
+ message(STATUS "GMP not found")
+ endif()
+endif()
+
if(WITH_XR_OPENXR)
find_package(XR_OpenXR_SDK)
if(NOT XR_OPENXR_SDK_FOUND)
@@ -590,6 +599,14 @@ endif()
if(CMAKE_COMPILER_IS_GNUCC)
set(PLATFORM_CFLAGS "-pipe -fPIC -funsigned-char -fno-strict-aliasing")
+ # `maybe-uninitialized` is unreliable in release builds, but fine in debug builds.
+ set(GCC_EXTRA_FLAGS_RELEASE "-Wno-maybe-uninitialized")
+ set(CMAKE_C_FLAGS_RELEASE "${GCC_EXTRA_FLAGS_RELEASE} ${CMAKE_C_FLAGS_RELEASE}")
+ set(CMAKE_C_FLAGS_RELWITHDEBINFO "${GCC_EXTRA_FLAGS_RELEASE} ${CMAKE_C_FLAGS_RELWITHDEBINFO}")
+ set(CMAKE_CXX_FLAGS_RELEASE "${GCC_EXTRA_FLAGS_RELEASE} ${CMAKE_CXX_FLAGS_RELEASE}")
+ set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${GCC_EXTRA_FLAGS_RELEASE} ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
+ unset(GCC_EXTRA_FLAGS_RELEASE)
+
if(WITH_LINKER_GOLD)
execute_process(
COMMAND ${CMAKE_C_COMPILER} -fuse-ld=gold -Wl,--version
diff --git a/build_files/cmake/platform/platform_win32.cmake b/build_files/cmake/platform/platform_win32.cmake
index b8af2dfc961..007f77a583f 100644
--- a/build_files/cmake/platform/platform_win32.cmake
+++ b/build_files/cmake/platform/platform_win32.cmake
@@ -136,6 +136,13 @@ add_definitions(
# MSVC11 needs _ALLOW_KEYWORD_MACROS to build
add_definitions(-D_ALLOW_KEYWORD_MACROS)
+# RTTI is on by default even without this switch
+# however having it in the CXX Flags makes it difficult
+# to remove for individual files that want to disable it
+# using the /GR- flag without generating a build warning
+# that both /GR and /GR- are specified.
+remove_cc_flag("/GR")
+
# We want to support Windows 7 level ABI
add_definitions(-D_WIN32_WINNT=0x601)
include(build_files/cmake/platform/platform_win32_bundle_crt.cmake)
diff --git a/doc/python_api/rst/change_log.rst b/doc/python_api/rst/change_log.rst
index f0384379499..957bf8605e3 100644
--- a/doc/python_api/rst/change_log.rst
+++ b/doc/python_api/rst/change_log.rst
@@ -6,386 +6,68 @@ Blender API Change Log
.. note, this document is auto generated by sphinx_changelog_gen.py
-2.79 to 2.80
+2.83 to 2.90
============
-bpy.types.ActionGroup
----------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.ActionGroup.show_expanded_graph`
-
-bpy.types.AnimData
-------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.AnimData.nla_tweak_strip_time_to_scene`
-
-bpy.types.AnimDataDrivers
--------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.AnimDataDrivers.new`
-* :class:`bpy.types.AnimDataDrivers.remove`
-
-bpy.types.AnimViz
------------------
-
-Removed
-^^^^^^^
-
-* **onion_skin_frames**
-
-bpy.types.AnimVizMotionPaths
-----------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.AnimVizMotionPaths.has_motion_paths`
-
-bpy.types.Area
---------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.Area.ui_type`
-
-bpy.types.BlendData
--------------------
+bpy.types.CyclesPreferences
+---------------------------
Added
^^^^^
-* :class:`bpy.types.BlendData.collections`
-* :class:`bpy.types.BlendData.lightprobes`
-* :class:`bpy.types.BlendData.lights`
-* :class:`bpy.types.BlendData.workspaces`
-
-Removed
-^^^^^^^
-
-* **groups**
-* **lamps**
-
-Renamed
-^^^^^^^
-
-* **grease_pencil** -> :class:`bpy.types.BlendData.grease_pencils`
-
-bpy.types.BlendDataActions
---------------------------
+* :class:`bpy.types.CyclesPreferences.peer_memory`
-Removed
-^^^^^^^
-
-* **is_updated**
-
-bpy.types.BlendDataArmatures
-----------------------------
-
-Removed
-^^^^^^^
-
-* **is_updated**
-
-bpy.types.BlendDataBrushes
---------------------------
+bpy.types.BakeSettings
+----------------------
Added
^^^^^
-* :class:`bpy.types.BlendDataBrushes.create_gpencil_data`
-
-Removed
-^^^^^^^
-
-* **is_updated**
-
-bpy.types.BlendDataCacheFiles
------------------------------
-
-Removed
-^^^^^^^
-
-* **is_updated**
-
-bpy.types.BlendDataCameras
---------------------------
-
-Removed
-^^^^^^^
-
-* **is_updated**
-
-bpy.types.BlendDataCurves
--------------------------
-
-Removed
-^^^^^^^
-
-* **is_updated**
-
-bpy.types.BlendDataFonts
-------------------------
-
-Removed
-^^^^^^^
-
-* **is_updated**
-
-bpy.types.BlendDataGreasePencils
---------------------------------
-
-Removed
-^^^^^^^
-
-* **is_updated**
-
-bpy.types.BlendDataImages
--------------------------
-
-Removed
-^^^^^^^
-
-* **is_updated**
-
-Function Arguments
-^^^^^^^^^^^^^^^^^^
-
-* :class:`bpy.types.BlendDataImages.new` (name, width, height, alpha, float_buffer, stereo3d, is_data), *was (name, width, height, alpha, float_buffer, stereo3d)*
-
-bpy.types.BlendDataLattices
----------------------------
-
-Removed
-^^^^^^^
-
-* **is_updated**
+* :class:`bpy.types.BakeSettings.max_ray_distance`
bpy.types.BlendDataLibraries
----------------------------
-Removed
-^^^^^^^
-
-* **is_updated**
-
-bpy.types.BlendDataLineStyles
------------------------------
-
-Removed
-^^^^^^^
-
-* **is_updated**
-
-bpy.types.BlendDataMasks
-------------------------
-
-Removed
-^^^^^^^
-
-* **is_updated**
-
-bpy.types.BlendDataMaterials
-----------------------------
-
Added
^^^^^
-* :class:`bpy.types.BlendDataMaterials.create_gpencil_data`
-* :class:`bpy.types.BlendDataMaterials.remove_gpencil_data`
-
-Removed
-^^^^^^^
-
-* **is_updated**
-
-bpy.types.BlendDataMeshes
--------------------------
-
-Removed
-^^^^^^^
-
-* **is_updated**
-
-Function Arguments
-^^^^^^^^^^^^^^^^^^
-
-* :class:`bpy.types.BlendDataMeshes.new_from_object` (object, preserve_all_data_layers, depsgraph), *was (scene, object, apply_modifiers, settings, calc_tessface, calc_undeformed)*
-
-bpy.types.BlendDataMetaBalls
-----------------------------
-
-Removed
-^^^^^^^
-
-* **is_updated**
-
-bpy.types.BlendDataMovieClips
------------------------------
-
-Removed
-^^^^^^^
-
-* **is_updated**
-
-bpy.types.BlendDataNodeTrees
-----------------------------
-
-Removed
-^^^^^^^
-
-* **is_updated**
-
-bpy.types.BlendDataObjects
---------------------------
-
-Removed
-^^^^^^^
-
-* **is_updated**
-
-bpy.types.BlendDataPaintCurves
-------------------------------
-
-Removed
-^^^^^^^
-
-* **is_updated**
-
-bpy.types.BlendDataPalettes
----------------------------
-
-Removed
-^^^^^^^
-
-* **is_updated**
-
-bpy.types.BlendDataParticles
-----------------------------
-
-Removed
-^^^^^^^
-
-* **is_updated**
-
-bpy.types.BlendDataScenes
--------------------------
-
-Removed
-^^^^^^^
-
-* **is_updated**
-
-bpy.types.BlendDataScreens
---------------------------
-
-Removed
-^^^^^^^
-
-* **is_updated**
-
-bpy.types.BlendDataSounds
--------------------------
-
-Removed
-^^^^^^^
-
-* **is_updated**
-
-bpy.types.BlendDataSpeakers
----------------------------
-
-Removed
-^^^^^^^
-
-* **is_updated**
-
-bpy.types.BlendDataTexts
-------------------------
+* :class:`bpy.types.BlendDataLibraries.remove`
-Removed
-^^^^^^^
-
-* **is_updated**
-
-bpy.types.BlendDataTextures
----------------------------
-
-Removed
-^^^^^^^
-
-* **is_updated**
-
-bpy.types.BlendDataWindowManagers
+bpy.types.BrushCapabilitiesSculpt
---------------------------------
-Removed
-^^^^^^^
-
-* **is_updated**
-
-bpy.types.BlendDataWorlds
--------------------------
-
-Removed
-^^^^^^^
-
-* **is_updated**
-
-bpy.types.Bone
---------------
-
Added
^^^^^
-* :class:`bpy.types.Bone.AxisRollFromMatrix`
-* :class:`bpy.types.Bone.MatrixFromAxisRoll`
-* :class:`bpy.types.Bone.bbone_custom_handle_end`
-* :class:`bpy.types.Bone.bbone_custom_handle_start`
-* :class:`bpy.types.Bone.bbone_easein`
-* :class:`bpy.types.Bone.bbone_easeout`
-* :class:`bpy.types.Bone.bbone_handle_type_end`
-* :class:`bpy.types.Bone.bbone_handle_type_start`
-* :class:`bpy.types.Bone.bbone_scaleinx`
-* :class:`bpy.types.Bone.bbone_scaleiny`
-* :class:`bpy.types.Bone.bbone_scaleoutx`
-* :class:`bpy.types.Bone.bbone_scaleouty`
-* :class:`bpy.types.Bone.convert_local_to_pose`
-
-Removed
-^^^^^^^
+* :class:`bpy.types.BrushCapabilitiesSculpt.has_color`
-* **bbone_in**
-* **bbone_out**
-* **bbone_scalein**
-* **bbone_scaleout**
-
-bpy.types.ClothCollisionSettings
---------------------------------
+bpy.types.BrushGpencilSettings
+------------------------------
Added
^^^^^
-* :class:`bpy.types.ClothCollisionSettings.collection`
-* :class:`bpy.types.ClothCollisionSettings.impulse_clamp`
-* :class:`bpy.types.ClothCollisionSettings.self_impulse_clamp`
-
-Removed
-^^^^^^^
-
-* **distance_repel**
-* **group**
-* **repel_force**
-* **self_collision_quality**
+* :class:`bpy.types.BrushGpencilSettings.curve_random_hue`
+* :class:`bpy.types.BrushGpencilSettings.curve_random_pressure`
+* :class:`bpy.types.BrushGpencilSettings.curve_random_saturation`
+* :class:`bpy.types.BrushGpencilSettings.curve_random_strength`
+* :class:`bpy.types.BrushGpencilSettings.curve_random_uv`
+* :class:`bpy.types.BrushGpencilSettings.curve_random_value`
+* :class:`bpy.types.BrushGpencilSettings.random_hue_factor`
+* :class:`bpy.types.BrushGpencilSettings.random_saturation_factor`
+* :class:`bpy.types.BrushGpencilSettings.random_value_factor`
+* :class:`bpy.types.BrushGpencilSettings.use_random_press_hue`
+* :class:`bpy.types.BrushGpencilSettings.use_random_press_radius`
+* :class:`bpy.types.BrushGpencilSettings.use_random_press_sat`
+* :class:`bpy.types.BrushGpencilSettings.use_random_press_strength`
+* :class:`bpy.types.BrushGpencilSettings.use_random_press_uv`
+* :class:`bpy.types.BrushGpencilSettings.use_random_press_val`
+* :class:`bpy.types.BrushGpencilSettings.use_stroke_random_hue`
+* :class:`bpy.types.BrushGpencilSettings.use_stroke_random_radius`
+* :class:`bpy.types.BrushGpencilSettings.use_stroke_random_sat`
+* :class:`bpy.types.BrushGpencilSettings.use_stroke_random_strength`
+* :class:`bpy.types.BrushGpencilSettings.use_stroke_random_uv`
+* :class:`bpy.types.BrushGpencilSettings.use_stroke_random_val`
bpy.types.ClothSettings
-----------------------
@@ -393,150 +75,7 @@ bpy.types.ClothSettings
Added
^^^^^
-* :class:`bpy.types.ClothSettings.bending_model`
-* :class:`bpy.types.ClothSettings.compression_damping`
-* :class:`bpy.types.ClothSettings.compression_stiffness`
-* :class:`bpy.types.ClothSettings.compression_stiffness_max`
-* :class:`bpy.types.ClothSettings.shear_damping`
-* :class:`bpy.types.ClothSettings.shear_stiffness`
-* :class:`bpy.types.ClothSettings.shear_stiffness_max`
-* :class:`bpy.types.ClothSettings.tension_damping`
-* :class:`bpy.types.ClothSettings.tension_stiffness`
-* :class:`bpy.types.ClothSettings.tension_stiffness_max`
-* :class:`bpy.types.ClothSettings.vertex_group_shear_stiffness`
-
-Removed
-^^^^^^^
-
-* **spring_damping**
-* **structural_stiffness**
-* **structural_stiffness_max**
-* **use_pin_cloth**
-* **use_stiffness_scale**
-* **vel_damping**
-
-bpy.types.CollisionSettings
----------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.CollisionSettings.cloth_friction`
-* :class:`bpy.types.CollisionSettings.use_culling`
-* :class:`bpy.types.CollisionSettings.use_normal`
-
-bpy.types.ColorManagedInputColorspaceSettings
----------------------------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.ColorManagedInputColorspaceSettings.is_data`
-
-bpy.types.CopyScaleConstraint
------------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.CopyScaleConstraint.power`
-* :class:`bpy.types.CopyScaleConstraint.use_add`
-
-bpy.types.FloorConstraint
--------------------------
-
-Removed
-^^^^^^^
-
-* **use_sticky**
-
-bpy.types.MaintainVolumeConstraint
-----------------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.MaintainVolumeConstraint.mode`
-
-bpy.types.ShrinkwrapConstraint
-------------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.ShrinkwrapConstraint.cull_face`
-* :class:`bpy.types.ShrinkwrapConstraint.track_axis`
-* :class:`bpy.types.ShrinkwrapConstraint.use_invert_cull`
-* :class:`bpy.types.ShrinkwrapConstraint.use_project_opposite`
-* :class:`bpy.types.ShrinkwrapConstraint.use_track_normal`
-* :class:`bpy.types.ShrinkwrapConstraint.wrap_mode`
-
-bpy.types.SplineIKConstraint
-----------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.SplineIKConstraint.use_original_scale`
-* :class:`bpy.types.SplineIKConstraint.y_scale_mode`
-
-Removed
-^^^^^^^
-
-* **use_y_stretch**
-
-bpy.types.Context
------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.Context.engine`
-* :class:`bpy.types.Context.evaluated_depsgraph_get`
-
-Renamed
-^^^^^^^
-
-* **user_preferences** -> :class:`bpy.types.Context.collection`
-* **user_preferences** -> :class:`bpy.types.Context.gizmo_group`
-* **user_preferences** -> :class:`bpy.types.Context.layer_collection`
-* **user_preferences** -> :class:`bpy.types.Context.preferences`
-* **user_preferences** -> :class:`bpy.types.Context.view_layer`
-* **user_preferences** -> :class:`bpy.types.Context.workspace`
-
-bpy.types.CurveMapping
-----------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.CurveMapping.tone`
-
-bpy.types.Depsgraph
--------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.Depsgraph.debug_stats_gnuplot`
-* :class:`bpy.types.Depsgraph.id_eval_get`
-* :class:`bpy.types.Depsgraph.id_type_updated`
-* :class:`bpy.types.Depsgraph.ids`
-* :class:`bpy.types.Depsgraph.mode`
-* :class:`bpy.types.Depsgraph.object_instances`
-* :class:`bpy.types.Depsgraph.objects`
-* :class:`bpy.types.Depsgraph.scene`
-* :class:`bpy.types.Depsgraph.scene_eval`
-* :class:`bpy.types.Depsgraph.updates`
-* :class:`bpy.types.Depsgraph.view_layer`
-* :class:`bpy.types.Depsgraph.view_layer_eval`
-
-Renamed
-^^^^^^^
-
-* **debug_graphviz** -> :class:`bpy.types.Depsgraph.debug_relations_graphviz`
-* **debug_rebuild** -> :class:`bpy.types.Depsgraph.debug_tag_update`
-* **debug_rebuild** -> :class:`bpy.types.Depsgraph.update`
+* :class:`bpy.types.ClothSettings.fluid_density`
bpy.types.DopeSheet
-------------------
@@ -544,252 +83,49 @@ bpy.types.DopeSheet
Added
^^^^^
-* :class:`bpy.types.DopeSheet.filter_collection`
-* :class:`bpy.types.DopeSheet.show_cache_files`
-* :class:`bpy.types.DopeSheet.show_lights`
-
-Removed
-^^^^^^^
-
-* **filter_group**
-* **show_lamps**
-* **show_only_group_objects**
-* **show_only_matching_fcurves**
-* **use_filter_text**
-
-bpy.types.Driver
-----------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.Driver.is_simple_expression`
-
-Removed
-^^^^^^^
-
-* **show_debug_info**
-
-bpy.types.DynamicPaintBrushSettings
------------------------------------
-
-Removed
-^^^^^^^
-
-* **material**
-* **use_material**
-
-bpy.types.DynamicPaintSurface
------------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.DynamicPaintSurface.brush_collection`
-
-Removed
-^^^^^^^
-
-* **brush_group**
-* **preview_id**
-* **show_preview**
-* **use_color_preview**
-
-bpy.types.EditBone
-------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.EditBone.bbone_custom_handle_end`
-* :class:`bpy.types.EditBone.bbone_custom_handle_start`
-* :class:`bpy.types.EditBone.bbone_easein`
-* :class:`bpy.types.EditBone.bbone_easeout`
-* :class:`bpy.types.EditBone.bbone_handle_type_end`
-* :class:`bpy.types.EditBone.bbone_handle_type_start`
-* :class:`bpy.types.EditBone.bbone_scaleinx`
-* :class:`bpy.types.EditBone.bbone_scaleiny`
-* :class:`bpy.types.EditBone.bbone_scaleoutx`
-* :class:`bpy.types.EditBone.bbone_scaleouty`
-
-Removed
-^^^^^^^
-
-* **bbone_in**
-* **bbone_out**
-* **bbone_scalein**
-* **bbone_scaleout**
-
-bpy.types.EffectorWeights
--------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.EffectorWeights.collection`
-
-Removed
-^^^^^^^
-
-* **group**
-
-bpy.types.Event
----------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.Event.is_mouse_absolute`
-
-bpy.types.FCurve
-----------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.FCurve.auto_smoothing`
-* :class:`bpy.types.FCurve.is_empty`
-
-bpy.types.FreestyleLineSet
---------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.FreestyleLineSet.collection`
-* :class:`bpy.types.FreestyleLineSet.collection_negation`
-* :class:`bpy.types.FreestyleLineSet.select_by_collection`
-
-Removed
-^^^^^^^
-
-* **group**
-* **group_negation**
-* **select_by_group**
-
-bpy.types.GPUFXSettings
------------------------
-
-Removed
-^^^^^^^
-
-* **dof**
-* **use_dof**
+* :class:`bpy.types.DopeSheet.show_hairs`
+* :class:`bpy.types.DopeSheet.show_pointclouds`
-bpy.types.GPencilFrames
+bpy.types.FieldSettings
-----------------------
-Function Arguments
-^^^^^^^^^^^^^^^^^^
-
-* :class:`bpy.types.GPencilFrames.new` (frame_number, active), *was (frame_number)*
-
-bpy.types.GPencilLayer
-----------------------
-
Added
^^^^^
-* :class:`bpy.types.GPencilLayer.annotation_hide`
-* :class:`bpy.types.GPencilLayer.blend_mode`
-* :class:`bpy.types.GPencilLayer.channel_color`
-* :class:`bpy.types.GPencilLayer.color`
-* :class:`bpy.types.GPencilLayer.lock_material`
-* :class:`bpy.types.GPencilLayer.mask_layer`
-* :class:`bpy.types.GPencilLayer.pass_index`
-* :class:`bpy.types.GPencilLayer.thickness`
-* :class:`bpy.types.GPencilLayer.use_annotation_onion_skinning`
-* :class:`bpy.types.GPencilLayer.use_solo_mode`
-* :class:`bpy.types.GPencilLayer.viewlayer_render`
-
-Removed
-^^^^^^^
-
-* **unlock_color**
-* **use_ghost_custom_colors**
-* **use_ghosts_always**
-* **use_volumetric_strokes**
-
-Renamed
-^^^^^^^
-
-* **after_color** -> :class:`bpy.types.GPencilLayer.annotation_onion_after_color`
-* **before_color** -> :class:`bpy.types.GPencilLayer.annotation_onion_before_color`
-* **ghost_after_range** -> :class:`bpy.types.GPencilLayer.annotation_onion_after_range`
-* **ghost_before_range** -> :class:`bpy.types.GPencilLayer.annotation_onion_before_range`
-* **show_x_ray** -> :class:`bpy.types.GPencilLayer.show_in_front`
+* :class:`bpy.types.FieldSettings.wind_factor`
-bpy.types.GPencilSculptBrush
+bpy.types.FileSelectIDFilter
----------------------------
Added
^^^^^
-* :class:`bpy.types.GPencilSculptBrush.cursor_color_add`
-* :class:`bpy.types.GPencilSculptBrush.cursor_color_sub`
-* :class:`bpy.types.GPencilSculptBrush.use_cursor`
-* :class:`bpy.types.GPencilSculptBrush.use_pressure_radius`
-* :class:`bpy.types.GPencilSculptBrush.weight`
-
-Renamed
-^^^^^^^
+* :class:`bpy.types.FileSelectIDFilter.filter_hair`
+* :class:`bpy.types.FileSelectIDFilter.filter_pointcloud`
+* :class:`bpy.types.FileSelectIDFilter.filter_simulation`
-* **affect_pressure** -> :class:`bpy.types.GPencilSculptBrush.use_edit_pressure`
-
-bpy.types.GPencilSculptSettings
--------------------------------
+bpy.types.FluidDomainSettings
+-----------------------------
Added
^^^^^
-* :class:`bpy.types.GPencilSculptSettings.guide`
-* :class:`bpy.types.GPencilSculptSettings.intersection_threshold`
-* :class:`bpy.types.GPencilSculptSettings.lock_axis`
-* :class:`bpy.types.GPencilSculptSettings.multiframe_falloff_curve`
-* :class:`bpy.types.GPencilSculptSettings.thickness_primitive_curve`
-* :class:`bpy.types.GPencilSculptSettings.use_edit_uv`
-* :class:`bpy.types.GPencilSculptSettings.use_multiframe_falloff`
-* :class:`bpy.types.GPencilSculptSettings.use_thickness_curve`
-* :class:`bpy.types.GPencilSculptSettings.weight_tool`
-
-Removed
-^^^^^^^
-
-* **lockaxis**
-* **selection_alpha**
+* :class:`bpy.types.FluidDomainSettings.cache_frame_offset`
+* :class:`bpy.types.FluidDomainSettings.cache_resumable`
+* :class:`bpy.types.FluidDomainSettings.sys_particle_maximum`
Renamed
^^^^^^^
-* **affect_position** -> :class:`bpy.types.GPencilSculptSettings.use_edit_position`
-* **affect_strength** -> :class:`bpy.types.GPencilSculptSettings.use_edit_strength`
-* **affect_thickness** -> :class:`bpy.types.GPencilSculptSettings.use_edit_thickness`
-* **tool** -> :class:`bpy.types.GPencilSculptSettings.sculpt_tool`
+* **data_depth** -> :class:`bpy.types.FluidDomainSettings.openvdb_data_depth`
-bpy.types.GPencilStroke
------------------------
+bpy.types.GPencilFrame
+----------------------
Added
^^^^^
-* :class:`bpy.types.GPencilStroke.end_cap_mode`
-* :class:`bpy.types.GPencilStroke.gradient_factor`
-* :class:`bpy.types.GPencilStroke.gradient_shape`
-* :class:`bpy.types.GPencilStroke.groups`
-* :class:`bpy.types.GPencilStroke.is_nofill_stroke`
-* :class:`bpy.types.GPencilStroke.material_index`
-* :class:`bpy.types.GPencilStroke.start_cap_mode`
-
-Removed
-^^^^^^^
-
-* **color**
-* **colorname**
-
-Renamed
-^^^^^^^
-
-* **draw_mode** -> :class:`bpy.types.GPencilStroke.display_mode`
+* :class:`bpy.types.GPencilFrame.keyframe_type`
bpy.types.GPencilStrokePoint
----------------------------
@@ -797,346 +133,52 @@ bpy.types.GPencilStrokePoint
Added
^^^^^
-* :class:`bpy.types.GPencilStrokePoint.uv_factor`
-* :class:`bpy.types.GPencilStrokePoint.uv_rotation`
-
-bpy.types.GPencilStrokes
-------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.GPencilStrokes.close`
-
-Function Arguments
-^^^^^^^^^^^^^^^^^^
-
-* :class:`bpy.types.GPencilStrokes.new` (), *was (colorname)*
-
-bpy.types.GPencilTriangle
--------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.GPencilTriangle.uv1`
-* :class:`bpy.types.GPencilTriangle.uv2`
-* :class:`bpy.types.GPencilTriangle.uv3`
-
-bpy.types.GreasePencilLayers
-----------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.GreasePencilLayers.active_note`
-* :class:`bpy.types.GreasePencilLayers.move`
-
-bpy.types.Header
-----------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.Header.bl_region_type`
-
-bpy.types.ID
-------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.ID.evaluated_get`
-* :class:`bpy.types.ID.is_evaluated`
-* :class:`bpy.types.ID.name_full`
-* :class:`bpy.types.ID.original`
-* :class:`bpy.types.ID.override_create`
-* :class:`bpy.types.ID.override_library`
-
-Removed
-^^^^^^^
-
-* **is_updated**
-* **is_updated_data**
-
-bpy.types.Armature
-------------------
-
-Removed
-^^^^^^^
-
-* **deform_method**
-* **ghost_frame_end**
-* **ghost_frame_start**
-* **ghost_size**
-* **ghost_step**
-* **ghost_type**
-* **show_only_ghost_selected**
-* **use_auto_ik**
-* **use_deform_delay**
-
-Renamed
-^^^^^^^
-
-* **draw_type** -> :class:`bpy.types.Armature.display_type`
-
-bpy.types.Brush
----------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.Brush.falloff_angle`
-* :class:`bpy.types.Brush.gpencil_settings`
-* :class:`bpy.types.Brush.gpencil_tool`
-* :class:`bpy.types.Brush.topology_rake_factor`
-* :class:`bpy.types.Brush.use_frontface_falloff`
-* :class:`bpy.types.Brush.use_paint_grease_pencil`
-* :class:`bpy.types.Brush.use_paint_uv_sculpt`
-* :class:`bpy.types.Brush.use_projected`
-* :class:`bpy.types.Brush.uv_sculpt_tool`
-* :class:`bpy.types.Brush.vertex_paint_capabilities`
-* :class:`bpy.types.Brush.weight_paint_capabilities`
-* :class:`bpy.types.Brush.weight_tool`
-
-bpy.types.CacheFile
--------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.CacheFile.frame_offset`
-
-bpy.types.Camera
-----------------
-
-Added
-^^^^^
+* :class:`bpy.types.GPencilStrokePoint.uv_fill`
-* :class:`bpy.types.Camera.background_images`
-* :class:`bpy.types.Camera.display_size`
-* :class:`bpy.types.Camera.show_background_images`
-* :class:`bpy.types.Camera.show_composition_center`
-* :class:`bpy.types.Camera.show_composition_center_diagonal`
-* :class:`bpy.types.Camera.show_composition_golden`
-* :class:`bpy.types.Camera.show_composition_golden_tria_a`
-* :class:`bpy.types.Camera.show_composition_golden_tria_b`
-* :class:`bpy.types.Camera.show_composition_harmony_tri_a`
-* :class:`bpy.types.Camera.show_composition_harmony_tri_b`
-* :class:`bpy.types.Camera.show_composition_thirds`
-
-Removed
-^^^^^^^
-
-* **dof_distance**
-* **dof_object**
-* **draw_size**
-* **show_guide**
-
-Renamed
-^^^^^^^
-
-* **gpu_dof** -> :class:`bpy.types.Camera.dof`
-
-bpy.types.Curve
+bpy.types.Gizmo
---------------
Added
^^^^^
-* :class:`bpy.types.Curve.update_gpu_tag`
-
-Removed
-^^^^^^^
+* :class:`bpy.types.Gizmo.hide_keymap`
+* :class:`bpy.types.Gizmo.use_tooltip`
-* **show_handles**
-* **show_normal_face**
-
-bpy.types.TextCurve
--------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.TextCurve.overflow`
-
-bpy.types.GreasePencil
-----------------------
+bpy.types.BuildGpencilModifier
+------------------------------
Added
^^^^^
-* :class:`bpy.types.GreasePencil.after_color`
-* :class:`bpy.types.GreasePencil.before_color`
-* :class:`bpy.types.GreasePencil.edit_line_color`
-* :class:`bpy.types.GreasePencil.ghost_after_range`
-* :class:`bpy.types.GreasePencil.ghost_before_range`
-* :class:`bpy.types.GreasePencil.grid`
-* :class:`bpy.types.GreasePencil.is_annotation`
-* :class:`bpy.types.GreasePencil.is_stroke_paint_mode`
-* :class:`bpy.types.GreasePencil.is_stroke_sculpt_mode`
-* :class:`bpy.types.GreasePencil.is_stroke_weight_mode`
-* :class:`bpy.types.GreasePencil.onion_factor`
-* :class:`bpy.types.GreasePencil.onion_keyframe_type`
-* :class:`bpy.types.GreasePencil.onion_mode`
-* :class:`bpy.types.GreasePencil.pixel_factor`
-* :class:`bpy.types.GreasePencil.stroke_depth_order`
-* :class:`bpy.types.GreasePencil.stroke_thickness_space`
-* :class:`bpy.types.GreasePencil.use_adaptive_uv`
-* :class:`bpy.types.GreasePencil.use_autolock_layers`
-* :class:`bpy.types.GreasePencil.use_force_fill_recalc`
-* :class:`bpy.types.GreasePencil.use_ghost_custom_colors`
-* :class:`bpy.types.GreasePencil.use_ghosts_always`
-* :class:`bpy.types.GreasePencil.use_multiedit`
-* :class:`bpy.types.GreasePencil.use_onion_fade`
-* :class:`bpy.types.GreasePencil.use_onion_loop`
-* :class:`bpy.types.GreasePencil.zdepth_offset`
+* :class:`bpy.types.BuildGpencilModifier.percentage_factor`
+* :class:`bpy.types.BuildGpencilModifier.use_percentage`
-Renamed
-^^^^^^^
-
-* **palettes** -> :class:`bpy.types.GreasePencil.materials`
-
-bpy.types.Image
+bpy.types.Brush
---------------
-Removed
-^^^^^^^
-
-* **field_order**
-* **fps**
-* **frame_end**
-* **frame_start**
-* **mapping**
-* **tiles_x**
-* **tiles_y**
-* **use_alpha**
-* **use_animation**
-* **use_clamp_x**
-* **use_clamp_y**
-* **use_fields**
-* **use_tiles**
-
-Function Arguments
-^^^^^^^^^^^^^^^^^^
-
-* :class:`bpy.types.Image.gl_load` (frame), *was (frame, filter, mag)*
-* :class:`bpy.types.Image.gl_touch` (frame), *was (frame, filter, mag)*
-* :class:`bpy.types.Image.pack` (data, data_len), *was (as_png, data, data_len)*
-
-bpy.types.Lattice
------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.Lattice.update_gpu_tag`
-
-bpy.types.Library
------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.Library.version`
-
-bpy.types.Material
-------------------
-
Added
^^^^^
-* :class:`bpy.types.Material.alpha_threshold`
-* :class:`bpy.types.Material.blend_method`
-* :class:`bpy.types.Material.grease_pencil`
-* :class:`bpy.types.Material.is_grease_pencil`
-* :class:`bpy.types.Material.metallic`
-* :class:`bpy.types.Material.refraction_depth`
-* :class:`bpy.types.Material.shadow_method`
-* :class:`bpy.types.Material.show_transparent_back`
-* :class:`bpy.types.Material.use_backface_culling`
-* :class:`bpy.types.Material.use_preview_world`
-* :class:`bpy.types.Material.use_screen_refraction`
-* :class:`bpy.types.Material.use_sss_translucency`
-
-Removed
-^^^^^^^
-
-* **active_node_material**
-* **active_texture**
-* **active_texture_index**
-* **alpha**
-* **ambient**
-* **darkness**
-* **diffuse_fresnel**
-* **diffuse_fresnel_factor**
-* **diffuse_intensity**
-* **diffuse_ramp**
-* **diffuse_ramp_blend**
-* **diffuse_ramp_factor**
-* **diffuse_ramp_input**
-* **diffuse_shader**
-* **diffuse_toon_size**
-* **diffuse_toon_smooth**
-* **emit**
-* **game_settings**
-* **halo**
-* **invert_z**
-* **light_group**
-* **mirror_color**
-* **offset_z**
-* **physics**
-* **raytrace_mirror**
-* **raytrace_transparency**
-* **shadow_buffer_bias**
-* **shadow_cast_alpha**
-* **shadow_only_type**
-* **shadow_ray_bias**
-* **specular_alpha**
-* **specular_hardness**
-* **specular_ior**
-* **specular_ramp**
-* **specular_ramp_blend**
-* **specular_ramp_factor**
-* **specular_ramp_input**
-* **specular_shader**
-* **specular_slope**
-* **specular_toon_size**
-* **specular_toon_smooth**
-* **strand**
-* **subsurface_scattering**
-* **texture_slots**
-* **translucency**
-* **transparency_method**
-* **type**
-* **use_cast_approximate**
-* **use_cast_buffer_shadows**
-* **use_cast_shadows**
-* **use_cast_shadows_only**
-* **use_cubic**
-* **use_diffuse_ramp**
-* **use_face_texture**
-* **use_face_texture_alpha**
-* **use_full_oversampling**
-* **use_light_group_exclusive**
-* **use_light_group_local**
-* **use_mist**
-* **use_object_color**
-* **use_only_shadow**
-* **use_ray_shadow_bias**
-* **use_raytrace**
-* **use_shadeless**
-* **use_shadows**
-* **use_sky**
-* **use_specular_ramp**
-* **use_tangent_shading**
-* **use_textures**
-* **use_transparency**
-* **use_transparent_shadows**
-* **use_uv_project**
-* **use_vertex_color_light**
-* **use_vertex_color_paint**
-* **volume**
+* :class:`bpy.types.Brush.density`
+* :class:`bpy.types.Brush.disconnected_distance_max`
+* :class:`bpy.types.Brush.flow`
+* :class:`bpy.types.Brush.invert_density_pressure`
+* :class:`bpy.types.Brush.invert_flow_pressure`
+* :class:`bpy.types.Brush.invert_hardness_pressure`
+* :class:`bpy.types.Brush.invert_wet_mix_pressure`
+* :class:`bpy.types.Brush.invert_wet_persistence_pressure`
+* :class:`bpy.types.Brush.pose_deform_type`
+* :class:`bpy.types.Brush.slide_deform_type`
+* :class:`bpy.types.Brush.smear_deform_type`
+* :class:`bpy.types.Brush.tip_scale_x`
+* :class:`bpy.types.Brush.use_connected_only`
+* :class:`bpy.types.Brush.use_density_pressure`
+* :class:`bpy.types.Brush.use_flow_pressure`
+* :class:`bpy.types.Brush.use_hardness_pressure`
+* :class:`bpy.types.Brush.use_wet_mix_pressure`
+* :class:`bpy.types.Brush.use_wet_persistence_pressure`
+* :class:`bpy.types.Brush.wet_mix`
+* :class:`bpy.types.Brush.wet_persistence`
bpy.types.Mesh
--------------
@@ -1144,265 +186,16 @@ bpy.types.Mesh
Added
^^^^^
-* :class:`bpy.types.Mesh.calc_loop_triangles`
-* :class:`bpy.types.Mesh.count_selected_items`
-* :class:`bpy.types.Mesh.face_maps`
-* :class:`bpy.types.Mesh.loop_triangles`
-* :class:`bpy.types.Mesh.update_gpu_tag`
-
-Removed
-^^^^^^^
-
-* **calc_tessface**
-* **show_double_sided**
-* **show_edge_bevel_weight**
-* **show_edge_crease**
-* **show_edge_seams**
-* **show_edge_sharp**
-* **show_edges**
-* **show_extra_edge_angle**
-* **show_extra_edge_length**
-* **show_extra_face_angle**
-* **show_extra_face_area**
-* **show_extra_indices**
-* **show_faces**
-* **show_freestyle_edge_marks**
-* **show_freestyle_face_marks**
-* **show_normal_face**
-* **show_normal_loop**
-* **show_normal_vertex**
-* **show_statvis**
-* **show_weight**
-* **tessface_uv_textures**
-* **tessface_vertex_colors**
-* **tessfaces**
-* **uv_texture_clone**
-* **uv_texture_clone_index**
-* **uv_texture_stencil**
-* **uv_texture_stencil_index**
-* **uv_textures**
-
-Function Arguments
-^^^^^^^^^^^^^^^^^^
-
-* :class:`bpy.types.Mesh.update` (calc_edges, calc_edges_loose, calc_loop_triangles), *was (calc_edges, calc_tessface)*
-
-bpy.types.MetaBall
-------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.MetaBall.update_gpu_tag`
-
-bpy.types.MovieClip
--------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.MovieClip.fps`
-* :class:`bpy.types.MovieClip.metadata`
-
-bpy.types.ShaderNodeTree
-------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.ShaderNodeTree.get_output_node`
-
-bpy.types.Object
-----------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.Object.display`
-* :class:`bpy.types.Object.display_type`
-* :class:`bpy.types.Object.empty_image_depth`
-* :class:`bpy.types.Object.empty_image_side`
-* :class:`bpy.types.Object.face_maps`
-* :class:`bpy.types.Object.grease_pencil_modifiers`
-* :class:`bpy.types.Object.hide_get`
-* :class:`bpy.types.Object.hide_set`
-* :class:`bpy.types.Object.hide_viewport`
-* :class:`bpy.types.Object.holdout_get`
-* :class:`bpy.types.Object.indirect_only_get`
-* :class:`bpy.types.Object.instance_collection`
-* :class:`bpy.types.Object.instance_faces_scale`
-* :class:`bpy.types.Object.instance_type`
-* :class:`bpy.types.Object.is_from_instancer`
-* :class:`bpy.types.Object.is_from_set`
-* :class:`bpy.types.Object.local_view_get`
-* :class:`bpy.types.Object.local_view_set`
-* :class:`bpy.types.Object.proxy_collection`
-* :class:`bpy.types.Object.select_get`
-* :class:`bpy.types.Object.select_set`
-* :class:`bpy.types.Object.shader_effects`
-* :class:`bpy.types.Object.show_empty_image_orthographic`
-* :class:`bpy.types.Object.show_empty_image_perspective`
-* :class:`bpy.types.Object.show_in_front`
-* :class:`bpy.types.Object.show_instancer_for_render`
-* :class:`bpy.types.Object.show_instancer_for_viewport`
-* :class:`bpy.types.Object.use_empty_image_alpha`
-* :class:`bpy.types.Object.use_instance_faces_scale`
-* :class:`bpy.types.Object.use_instance_vertices_rotation`
-* :class:`bpy.types.Object.users_collection`
-* :class:`bpy.types.Object.visible_get`
-
-Removed
-^^^^^^^
-
-* **draw_type**
-* **dupli_faces_scale**
-* **dupli_frames_end**
-* **dupli_frames_off**
-* **dupli_frames_on**
-* **dupli_frames_start**
-* **dupli_group**
-* **dupli_list**
-* **dupli_list_create**
-* **dupli_type**
-* **game**
-* **grease_pencil**
-* **hide**
-* **is_visible**
-* **layers**
-* **layers_local_view**
-* **lod_levels**
-* **proxy_group**
-* **select**
-* **show_x_ray**
-* **slow_parent_offset**
-* **use_dupli_faces_scale**
-* **use_dupli_frames_speed**
-* **use_dupli_vertices_rotation**
-* **use_extra_recalc_data**
-* **use_extra_recalc_object**
-* **use_slow_parent**
-* **users_group**
-
-Renamed
-^^^^^^^
-
-* **draw_bounds_type** -> :class:`bpy.types.Object.display_bounds_type`
-* **dupli_list_clear** -> :class:`bpy.types.Object.shape_key_clear`
-* **dupli_list_clear** -> :class:`bpy.types.Object.to_mesh_clear`
-* **empty_draw_size** -> :class:`bpy.types.Object.empty_display_size`
-* **empty_draw_type** -> :class:`bpy.types.Object.empty_display_type`
-* **is_duplicator** -> :class:`bpy.types.Object.is_instancer`
-
-Function Arguments
-^^^^^^^^^^^^^^^^^^
-
-* :class:`bpy.types.Object.calc_matrix_camera` (depsgraph, x, y, scale_x, scale_y), *was (x, y, scale_x, scale_y)*
-* :class:`bpy.types.Object.camera_fit_coords` (depsgraph, coordinates), *was (scene, coordinates)*
-* :class:`bpy.types.Object.closest_point_on_mesh` (origin, distance, depsgraph), *was (origin, distance)*
-* :class:`bpy.types.Object.ray_cast` (origin, direction, distance, depsgraph), *was (origin, direction, distance)*
-* :class:`bpy.types.Object.to_mesh` (preserve_all_data_layers, depsgraph), *was (scene, apply_modifiers, settings, calc_tessface, calc_undeformed)*
-
-bpy.types.ParticleSettings
---------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.ParticleSettings.collision_collection`
-* :class:`bpy.types.ParticleSettings.display_size`
-* :class:`bpy.types.ParticleSettings.instance_collection`
-* :class:`bpy.types.ParticleSettings.instance_weights`
-* :class:`bpy.types.ParticleSettings.radius_scale`
-* :class:`bpy.types.ParticleSettings.root_radius`
-* :class:`bpy.types.ParticleSettings.shape`
-* :class:`bpy.types.ParticleSettings.tip_radius`
-* :class:`bpy.types.ParticleSettings.twist`
-* :class:`bpy.types.ParticleSettings.twist_curve`
-* :class:`bpy.types.ParticleSettings.use_close_tip`
-* :class:`bpy.types.ParticleSettings.use_twist_curve`
-* :class:`bpy.types.ParticleSettings.use_whole_collection`
-
-Removed
-^^^^^^^
-
-* **billboard_align**
-* **billboard_animation**
-* **billboard_object**
-* **billboard_offset**
-* **billboard_offset_split**
-* **billboard_size**
-* **billboard_tilt**
-* **billboard_tilt_random**
-* **billboard_uv_split**
-* **billboard_velocity_head**
-* **billboard_velocity_tail**
-* **collision_group**
-* **cycles**
-* **draw_size**
-* **dupli_group**
-* **dupli_weights**
-* **lock_billboard**
-* **simplify_rate**
-* **simplify_refsize**
-* **simplify_transition**
-* **simplify_viewport**
-* **use_render_emitter**
-* **use_simplify**
-* **use_simplify_viewport**
-* **use_whole_group**
-
-Renamed
-^^^^^^^
-
-* **active_dupliweight** -> :class:`bpy.types.ParticleSettings.active_instanceweight`
-* **active_dupliweight_index** -> :class:`bpy.types.ParticleSettings.active_instanceweight_index`
-* **draw_color** -> :class:`bpy.types.ParticleSettings.display_color`
-* **draw_method** -> :class:`bpy.types.ParticleSettings.display_method`
-* **draw_percentage** -> :class:`bpy.types.ParticleSettings.display_percentage`
-* **draw_step** -> :class:`bpy.types.ParticleSettings.display_step`
-* **dupli_object** -> :class:`bpy.types.ParticleSettings.instance_object`
-* **regrow_hair** -> :class:`bpy.types.ParticleSettings.use_regrow_hair`
-* **use_global_dupli** -> :class:`bpy.types.ParticleSettings.use_global_instance`
-* **use_group_count** -> :class:`bpy.types.ParticleSettings.use_collection_count`
-* **use_group_pick_random** -> :class:`bpy.types.ParticleSettings.use_collection_pick_random`
-* **use_rotation_dupli** -> :class:`bpy.types.ParticleSettings.use_rotation_instance`
-* **use_scale_dupli** -> :class:`bpy.types.ParticleSettings.use_scale_instance`
+* :class:`bpy.types.Mesh.sculpt_vertex_colors`
+* :class:`bpy.types.Mesh.use_remesh_preserve_vertex_colors`
bpy.types.Scene
---------------
-Added
-^^^^^
-
-* :class:`bpy.types.Scene.collection`
-* :class:`bpy.types.Scene.display`
-* :class:`bpy.types.Scene.eevee`
-
-Removed
-^^^^^^^
-
-* **active_layer**
-* **collada_export**
-* **cursor_location**
-* **depsgraph**
-* **layers**
-* **orientations**
-* **update**
-* **use_audio_sync**
-* **use_frame_drop**
-
-Renamed
-^^^^^^^
-
-* **game_settings** -> :class:`bpy.types.Scene.cursor`
-* **object_bases** -> :class:`bpy.types.Scene.transform_orientation_slots`
-* **object_bases** -> :class:`bpy.types.Scene.view_layers`
-
Function Arguments
^^^^^^^^^^^^^^^^^^
-* :class:`bpy.types.Scene.ray_cast` (view_layer, origin, direction, distance), *was (origin, direction, distance)*
-* :class:`bpy.types.Scene.statistics` (view_layer), *was ()*
+* :class:`bpy.types.Scene.alembic_export` (filepath, frame_start, frame_end, xform_samples, geom_samples, shutter_open, shutter_close, selected_only, uvs, normals, vcolors, apply_subdiv, flatten, visible_objects_only, renderable_only, face_sets, subdiv_schema, export_hair, export_particles, packuv, scale, triangulate, quad_method, ngon_method), *was (filepath, frame_start, frame_end, xform_samples, geom_samples, shutter_open, shutter_close, selected_only, uvs, normals, vcolors, apply_subdiv, flatten, visible_objects_only, renderable_only, face_sets, subdiv_schema, export_hair, export_particles, compression_type, packuv, scale, triangulate, quad_method, ngon_method)*
bpy.types.Screen
----------------
@@ -1410,190 +203,16 @@ bpy.types.Screen
Added
^^^^^
-* :class:`bpy.types.Screen.is_temporary`
-* :class:`bpy.types.Screen.show_statusbar`
-
-Removed
-^^^^^^^
-
-* **scene**
-
-bpy.types.Speaker
------------------
-
-Removed
-^^^^^^^
-
-* **relative**
-
-bpy.types.Text
---------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.Text.as_module`
-
-Removed
-^^^^^^^
-
-* **users_logic**
-
-bpy.types.ImageTexture
-----------------------
-
-Removed
-^^^^^^^
-
-* **use_derivative_map**
-
-Renamed
-^^^^^^^
-
-* **filter_probes** -> :class:`bpy.types.ImageTexture.filter_lightprobes`
-
-bpy.types.WindowManager
------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.WindowManager.gizmo_group_type_ensure`
-* :class:`bpy.types.WindowManager.gizmo_group_type_unlink_delayed`
-* :class:`bpy.types.WindowManager.operator_properties_last`
-* :class:`bpy.types.WindowManager.popover`
-* :class:`bpy.types.WindowManager.popover_begin__internal`
-* :class:`bpy.types.WindowManager.popover_end__internal`
-* :class:`bpy.types.WindowManager.preset_name`
-* :class:`bpy.types.WindowManager.print_undo_steps`
-
-Renamed
-^^^^^^^
-
-* **pupmenu_begin__internal** -> :class:`bpy.types.WindowManager.popmenu_begin__internal`
-* **pupmenu_end__internal** -> :class:`bpy.types.WindowManager.popmenu_end__internal`
-
-bpy.types.World
----------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.World.color`
-
-Removed
-^^^^^^^
+* :class:`bpy.types.Screen.is_scrubbing`
+* :class:`bpy.types.Screen.statusbar_info`
-* **active_texture**
-* **active_texture_index**
-* **ambient_color**
-* **color_range**
-* **exposure**
-* **horizon_color**
-* **texture_slots**
-* **use_sky_blend**
-* **use_sky_paper**
-* **use_sky_real**
-* **zenith_color**
-
-bpy.types.ImageUser
--------------------
+bpy.types.IDOverrideLibrary
+---------------------------
Removed
^^^^^^^
-* **fields_per_frame**
-
-bpy.types.KeyConfig
--------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.KeyConfig.preferences`
-
-bpy.types.KeyConfigurations
----------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.KeyConfigurations.find_item_from_operator`
-* :class:`bpy.types.KeyConfigurations.update`
-
-bpy.types.KeyMap
-----------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.KeyMap.bl_owner_id`
-
-bpy.types.KeyMapItem
---------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.KeyMapItem.to_string`
-
-bpy.types.KeyMapItems
----------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.KeyMapItems.find_from_operator`
-* :class:`bpy.types.KeyMapItems.new_from_item`
-
-bpy.types.KeyMaps
------------------
-
-Function Arguments
-^^^^^^^^^^^^^^^^^^
-
-* :class:`bpy.types.KeyMaps.new` (name, space_type, region_type, modal, tool), *was (name, space_type, region_type, modal)*
-
-bpy.types.LoopColors
---------------------
-
-Function Arguments
-^^^^^^^^^^^^^^^^^^
-
-* :class:`bpy.types.LoopColors.new` (name, do_init), *was (name)*
-
-bpy.types.Menu
---------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.Menu.bl_owner_id`
-
-Function Arguments
-^^^^^^^^^^^^^^^^^^
-
-* :class:`bpy.types.Menu.draw_preset` (self, _context), *was (self, context)*
-* :class:`bpy.types.Menu.path_menu` (self, searchpaths, operator, props_default, prop_filepath, filter_ext, filter_path, display_name, add_operator), *was (self, searchpaths, operator, props_default, prop_filepath, filter_ext, filter_path, display_name)*
-
-bpy.types.MeshUVLoopLayer
--------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.MeshUVLoopLayer.active`
-* :class:`bpy.types.MeshUVLoopLayer.active_clone`
-* :class:`bpy.types.MeshUVLoopLayer.active_render`
-
-bpy.types.ArrayModifier
------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.ArrayModifier.offset_u`
-* :class:`bpy.types.ArrayModifier.offset_v`
+* **auto_generate**
bpy.types.BevelModifier
-----------------------
@@ -1601,63 +220,14 @@ bpy.types.BevelModifier
Added
^^^^^
-* :class:`bpy.types.BevelModifier.face_strength_mode`
-* :class:`bpy.types.BevelModifier.harden_normals`
-* :class:`bpy.types.BevelModifier.mark_seam`
-* :class:`bpy.types.BevelModifier.mark_sharp`
-* :class:`bpy.types.BevelModifier.miter_inner`
-* :class:`bpy.types.BevelModifier.miter_outer`
-* :class:`bpy.types.BevelModifier.spread`
-* :class:`bpy.types.BevelModifier.width_pct`
-
-Removed
-^^^^^^^
-
-* **edge_weight_method**
-
-bpy.types.BooleanModifier
--------------------------
+* :class:`bpy.types.BevelModifier.affect`
+* :class:`bpy.types.BevelModifier.profile_type`
Removed
^^^^^^^
-* **solver**
-
-bpy.types.HookModifier
-----------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.HookModifier.vertex_indices`
-* :class:`bpy.types.HookModifier.vertex_indices_set`
-
-bpy.types.MaskModifier
-----------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.MaskModifier.threshold`
-
-bpy.types.MirrorModifier
-------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.MirrorModifier.offset_u`
-* :class:`bpy.types.MirrorModifier.offset_v`
-* :class:`bpy.types.MirrorModifier.use_axis`
-* :class:`bpy.types.MirrorModifier.use_bisect_axis`
-* :class:`bpy.types.MirrorModifier.use_bisect_flip_axis`
-
-Removed
-^^^^^^^
-
-* **use_x**
-* **use_y**
-* **use_z**
+* **use_custom_profile**
+* **use_only_vertices**
bpy.types.MultiresModifier
--------------------------
@@ -1665,60 +235,17 @@ bpy.types.MultiresModifier
Added
^^^^^
-* :class:`bpy.types.MultiresModifier.quality`
-* :class:`bpy.types.MultiresModifier.use_creases`
-* :class:`bpy.types.MultiresModifier.uv_smooth`
-
-Removed
-^^^^^^^
-
-* **use_subsurf_uv**
-
-bpy.types.NormalEditModifier
-----------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.NormalEditModifier.no_polynors_fix`
-
-bpy.types.ParticleInstanceModifier
-----------------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.ParticleInstanceModifier.index_layer_name`
-* :class:`bpy.types.ParticleInstanceModifier.particle_amount`
-* :class:`bpy.types.ParticleInstanceModifier.particle_offset`
-* :class:`bpy.types.ParticleInstanceModifier.particle_system`
-* :class:`bpy.types.ParticleInstanceModifier.random_rotation`
-* :class:`bpy.types.ParticleInstanceModifier.rotation`
-* :class:`bpy.types.ParticleInstanceModifier.space`
-* :class:`bpy.types.ParticleInstanceModifier.value_layer_name`
-
-bpy.types.ShrinkwrapModifier
-----------------------------
+* :class:`bpy.types.MultiresModifier.use_custom_normals`
-Added
-^^^^^
-
-* :class:`bpy.types.ShrinkwrapModifier.use_invert_cull`
-* :class:`bpy.types.ShrinkwrapModifier.wrap_mode`
-
-Removed
-^^^^^^^
-
-* **use_keep_above_surface**
-
-bpy.types.SimpleDeformModifier
-------------------------------
+bpy.types.OceanModifier
+-----------------------
Added
^^^^^
-* :class:`bpy.types.SimpleDeformModifier.deform_axis`
-* :class:`bpy.types.SimpleDeformModifier.lock_z`
+* :class:`bpy.types.OceanModifier.invert_spray`
+* :class:`bpy.types.OceanModifier.spray_layer_name`
+* :class:`bpy.types.OceanModifier.use_spray`
bpy.types.SubsurfModifier
-------------------------
@@ -1726,353 +253,186 @@ bpy.types.SubsurfModifier
Added
^^^^^
-* :class:`bpy.types.SubsurfModifier.quality`
-* :class:`bpy.types.SubsurfModifier.use_creases`
-* :class:`bpy.types.SubsurfModifier.uv_smooth`
+* :class:`bpy.types.SubsurfModifier.use_custom_normals`
-Removed
-^^^^^^^
-
-* **use_opensubdiv**
-* **use_subsurf_uv**
-
-bpy.types.TriangulateModifier
------------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.TriangulateModifier.keep_custom_normals`
-* :class:`bpy.types.TriangulateModifier.min_vertices`
-
-bpy.types.UVProjectModifier
----------------------------
-
-Removed
-^^^^^^^
-
-* **image**
-* **use_image_override**
-
-bpy.types.Node
---------------
-
-Removed
-^^^^^^^
-
-* **shading_compatibility**
-
-Function Arguments
-^^^^^^^^^^^^^^^^^^
-
-* :class:`bpy.types.Node.poll` (_ntree), *was (ntree)*
-
-bpy.types.NodeCustomGroup
--------------------------
-
-Function Arguments
-^^^^^^^^^^^^^^^^^^
-
-* :class:`bpy.types.NodeCustomGroup.poll` (_ntree), *was (ntree)*
-
-bpy.types.CompositorNodeMask
-----------------------------
-
-Removed
-^^^^^^^
-
-* **use_antialiasing**
-
-bpy.types.ShaderNodeAmbientOcclusion
-------------------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.ShaderNodeAmbientOcclusion.inside`
-* :class:`bpy.types.ShaderNodeAmbientOcclusion.only_local`
-* :class:`bpy.types.ShaderNodeAmbientOcclusion.samples`
-
-bpy.types.ShaderNodeBsdfPrincipled
+bpy.types.VertexWeightEditModifier
----------------------------------
Added
^^^^^
-* :class:`bpy.types.ShaderNodeBsdfPrincipled.subsurface_method`
+* :class:`bpy.types.VertexWeightEditModifier.normalize`
-bpy.types.ShaderNodeOutputLineStyle
------------------------------------
+bpy.types.VertexWeightMixModifier
+---------------------------------
Added
^^^^^
-* :class:`bpy.types.ShaderNodeOutputLineStyle.target`
+* :class:`bpy.types.VertexWeightMixModifier.invert_vertex_group_a`
+* :class:`bpy.types.VertexWeightMixModifier.invert_vertex_group_b`
+* :class:`bpy.types.VertexWeightMixModifier.normalize`
-bpy.types.ShaderNodeOutputMaterial
-----------------------------------
+bpy.types.VertexWeightProximityModifier
+---------------------------------------
Added
^^^^^
-* :class:`bpy.types.ShaderNodeOutputMaterial.target`
+* :class:`bpy.types.VertexWeightProximityModifier.normalize`
-bpy.types.ShaderNodeOutputWorld
--------------------------------
+bpy.types.MovieTrackingCamera
+-----------------------------
Added
^^^^^
-* :class:`bpy.types.ShaderNodeOutputWorld.target`
-
-bpy.types.ShaderNodeTexCoord
-----------------------------
-
-Renamed
-^^^^^^^
-
-* **from_dupli** -> :class:`bpy.types.ShaderNodeTexCoord.from_instancer`
-
-bpy.types.ShaderNodeTexEnvironment
-----------------------------------
-
-Removed
-^^^^^^^
-
-* **color_space**
-
-bpy.types.ShaderNodeTexImage
-----------------------------
-
-Removed
-^^^^^^^
-
-* **color_space**
-
-bpy.types.ShaderNodeTexPointDensity
------------------------------------
-
-Function Arguments
-^^^^^^^^^^^^^^^^^^
-
-* :class:`bpy.types.ShaderNodeTexPointDensity.cache_point_density` (depsgraph), *was (scene, settings)*
-* :class:`bpy.types.ShaderNodeTexPointDensity.calc_point_density` (depsgraph), *was (scene, settings)*
-* :class:`bpy.types.ShaderNodeTexPointDensity.calc_point_density_minmax` (depsgraph), *was (scene, settings)*
+* :class:`bpy.types.MovieTrackingCamera.nuke_k1`
+* :class:`bpy.types.MovieTrackingCamera.nuke_k2`
-bpy.types.ShaderNodeTexVoronoi
-------------------------------
+bpy.types.ShaderNodeTexSky
+--------------------------
Added
^^^^^
-* :class:`bpy.types.ShaderNodeTexVoronoi.distance`
-* :class:`bpy.types.ShaderNodeTexVoronoi.feature`
-
-bpy.types.ShaderNodeUVMap
--------------------------
-
-Renamed
-^^^^^^^
-
-* **from_dupli** -> :class:`bpy.types.ShaderNodeUVMap.from_instancer`
+* :class:`bpy.types.ShaderNodeTexSky.air_density`
+* :class:`bpy.types.ShaderNodeTexSky.altitude`
+* :class:`bpy.types.ShaderNodeTexSky.dust_density`
+* :class:`bpy.types.ShaderNodeTexSky.ozone_density`
+* :class:`bpy.types.ShaderNodeTexSky.sun_disc`
+* :class:`bpy.types.ShaderNodeTexSky.sun_elevation`
+* :class:`bpy.types.ShaderNodeTexSky.sun_intensity`
+* :class:`bpy.types.ShaderNodeTexSky.sun_rotation`
+* :class:`bpy.types.ShaderNodeTexSky.sun_size`
-bpy.types.NodeSocket
---------------------
+bpy.types.NodeSocketInterface
+-----------------------------
Added
^^^^^
-* :class:`bpy.types.NodeSocket.draw_shape`
+* :class:`bpy.types.NodeSocketInterface.NWViewerSocket`
+* :class:`bpy.types.NodeSocketInterface.hide_value`
-bpy.types.ObjectBase
---------------------
+bpy.types.ObjectConstraints
+---------------------------
Added
^^^^^
-* :class:`bpy.types.ObjectBase.hide_viewport`
+* :class:`bpy.types.ObjectConstraints.copy`
+
+bpy.types.Sculpt
+----------------
Removed
^^^^^^^
-* **layers**
-* **layers_from_view**
-* **layers_local_view**
-
-bpy.types.OperatorOptions
--------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.OperatorOptions.is_repeat`
-* :class:`bpy.types.OperatorOptions.is_repeat_last`
+* **use_threaded**
-bpy.types.Paint
+bpy.types.Panel
---------------
Added
^^^^^
-* :class:`bpy.types.Paint.tool_slots`
+* :class:`bpy.types.Panel.list_panel_index`
-bpy.types.ImagePaint
---------------------
+bpy.types.PoseBoneConstraints
+-----------------------------
Added
^^^^^
-* :class:`bpy.types.ImagePaint.interpolation`
+* :class:`bpy.types.PoseBoneConstraints.copy`
-bpy.types.Sculpt
-----------------
+bpy.types.PreferencesEdit
+-------------------------
Added
^^^^^
-* :class:`bpy.types.Sculpt.show_mask`
+* :class:`bpy.types.PreferencesEdit.collection_instance_empty_size`
+* :class:`bpy.types.PreferencesEdit.use_duplicate_hair`
+* :class:`bpy.types.PreferencesEdit.use_duplicate_pointcloud`
-bpy.types.VertexPaint
----------------------
+bpy.types.PreferencesExperimental
+---------------------------------
Added
^^^^^
-* :class:`bpy.types.VertexPaint.radial_symmetry`
+* :class:`bpy.types.PreferencesExperimental.use_cycles_debug`
+* :class:`bpy.types.PreferencesExperimental.use_new_hair_type`
+* :class:`bpy.types.PreferencesExperimental.use_new_particle_system`
+* :class:`bpy.types.PreferencesExperimental.use_sculpt_vertex_colors`
Removed
^^^^^^^
-* **use_normal**
-* **use_spray**
+* **use_menu_search**
-bpy.types.Panel
----------------
+bpy.types.PreferencesView
+-------------------------
Added
^^^^^
-* :class:`bpy.types.Panel.bl_order`
-* :class:`bpy.types.Panel.bl_owner_id`
-* :class:`bpy.types.Panel.bl_parent_id`
-* :class:`bpy.types.Panel.bl_ui_units_x`
-* :class:`bpy.types.Panel.draw_header_preset`
-* :class:`bpy.types.Panel.is_popover`
-
-bpy.types.ParticleEdit
-----------------------
+* :class:`bpy.types.PreferencesView.show_statusbar_memory`
+* :class:`bpy.types.PreferencesView.show_statusbar_stats`
+* :class:`bpy.types.PreferencesView.show_statusbar_version`
+* :class:`bpy.types.PreferencesView.show_statusbar_vram`
-Renamed
-^^^^^^^
-
-* **draw_step** -> :class:`bpy.types.ParticleEdit.display_step`
-
-bpy.types.ParticleSystem
-------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.ParticleSystem.invert_vertex_group_twist`
-* :class:`bpy.types.ParticleSystem.vertex_group_twist`
+bpy.types.CyclesCurveRenderSettings
+-----------------------------------
Removed
^^^^^^^
-* **billboard_normal_uv**
-* **billboard_split_uv**
-* **billboard_time_index_uv**
-* **set_resolution**
+* **cull_backfacing**
+* **primitive**
+* **resolution**
+* **use_curves**
-bpy.types.Pose
---------------
+bpy.types.CyclesObjectSettings
+------------------------------
Added
^^^^^
-* :class:`bpy.types.Pose.use_auto_ik`
-* :class:`bpy.types.Pose.use_mirror_relative`
-* :class:`bpy.types.Pose.use_mirror_x`
+* :class:`bpy.types.CyclesObjectSettings.shadow_terminator_offset`
-bpy.types.PoseBone
-------------------
+bpy.types.CyclesRenderLayerSettings
+-----------------------------------
Added
^^^^^
-* :class:`bpy.types.PoseBone.bbone_easein`
-* :class:`bpy.types.PoseBone.bbone_easeout`
-* :class:`bpy.types.PoseBone.bbone_scaleinx`
-* :class:`bpy.types.PoseBone.bbone_scaleiny`
-* :class:`bpy.types.PoseBone.bbone_scaleoutx`
-* :class:`bpy.types.PoseBone.bbone_scaleouty`
-* :class:`bpy.types.PoseBone.bbone_segment_matrix`
-* :class:`bpy.types.PoseBone.compute_bbone_handles`
+* :class:`bpy.types.CyclesRenderLayerSettings.denoising_openimagedenoise_input_passes`
Removed
^^^^^^^
-* **bbone_scalein**
-* **bbone_scaleout**
-* **use_bbone_custom_handles**
-* **use_bbone_relative_end_handle**
-* **use_bbone_relative_start_handle**
-
-bpy.types.Property
-------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.Property.is_overridable`
-* :class:`bpy.types.Property.tags`
-
-bpy.types.BoolProperty
-----------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.BoolProperty.array_dimensions`
-
-bpy.types.FloatProperty
------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.FloatProperty.array_dimensions`
+* **use_optix_denoising**
-bpy.types.IntProperty
----------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.IntProperty.array_dimensions`
-
-bpy.types.Region
-----------------
+bpy.types.CyclesRenderSettings
+------------------------------
Added
^^^^^
-* :class:`bpy.types.Region.alignment`
+* :class:`bpy.types.CyclesRenderSettings.debug_optix_curves_api`
+* :class:`bpy.types.CyclesRenderSettings.denoiser`
+* :class:`bpy.types.CyclesRenderSettings.preview_denoiser`
+* :class:`bpy.types.CyclesRenderSettings.use_denoising`
+* :class:`bpy.types.CyclesRenderSettings.use_preview_denoising`
Removed
^^^^^^^
-* **id**
-
-bpy.types.RegionView3D
-----------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.RegionView3D.clip_planes`
-* :class:`bpy.types.RegionView3D.is_orthographic_side_view`
-* :class:`bpy.types.RegionView3D.use_clip_planes`
+* **preview_denoising**
+* **use_bvh_embree**
bpy.types.RenderEngine
----------------------
@@ -2080,64 +440,20 @@ bpy.types.RenderEngine
Added
^^^^^
-* :class:`bpy.types.RenderEngine.bl_use_eevee_viewport`
-* :class:`bpy.types.RenderEngine.free_blender_memory`
-* :class:`bpy.types.RenderEngine.get_preview_pixel_size`
-* :class:`bpy.types.RenderEngine.get_result`
-
-Removed
-^^^^^^^
-
-* **bl_use_exclude_layers**
-* **bl_use_shading_nodes**
-* **bl_use_texture_preview**
+* :class:`bpy.types.RenderEngine.bl_use_gpu_context`
Function Arguments
^^^^^^^^^^^^^^^^^^
-* :class:`bpy.types.RenderEngine.bake` (depsgraph, object, pass_type, pass_filter, object_id, pixel_array, num_pixels, depth, result), *was (scene, object, pass_type, pass_filter, object_id, pixel_array, num_pixels, depth, result)*
-* :class:`bpy.types.RenderEngine.camera_model_matrix` (camera, use_spherical_stereo), *was (camera, use_spherical_stereo, r_model_matrix)*
-* :class:`bpy.types.RenderEngine.register_pass` (scene, view_layer, name, channels, chanid, type), *was (scene, srl, name, channels, chanid, type)*
-* :class:`bpy.types.RenderEngine.render` (depsgraph), *was (scene)*
-* :class:`bpy.types.RenderEngine.update` (data, depsgraph), *was (data, scene)*
-* :class:`bpy.types.RenderEngine.view_draw` (context, depsgraph), *was (context)*
-* :class:`bpy.types.RenderEngine.view_update` (context, depsgraph), *was (context)*
-
-bpy.types.RenderLayer
----------------------
-
-Removed
-^^^^^^^
+* :class:`bpy.types.RenderEngine.bake` (depsgraph, object, pass_type, pass_filter, width, height), *was (depsgraph, object, pass_type, pass_filter, object_id, pixel_array, num_pixels, depth, result)*
-* **exclude_ambient_occlusion**
-* **exclude_emit**
-* **exclude_environment**
-* **exclude_indirect**
-* **exclude_reflection**
-* **exclude_refraction**
-* **exclude_shadow**
-* **exclude_specular**
-* **layers**
-* **layers_exclude**
-* **layers_zmask**
-* **light_override**
-* **material_override**
-* **use**
-* **use_freestyle**
-* **use_pass_color**
-* **use_pass_diffuse**
-* **use_pass_indirect**
-* **use_pass_reflection**
-* **use_pass_refraction**
-* **use_pass_specular**
-
-bpy.types.RenderResult
-----------------------
+bpy.types.CYCLES
+----------------
-Added
-^^^^^
+Function Arguments
+^^^^^^^^^^^^^^^^^^
-* :class:`bpy.types.RenderResult.stamp_data_add_field`
+* :class:`bpy.types.CYCLES.bake` (self, depsgraph, obj, pass_type, pass_filter, width, height), *was (self, depsgraph, obj, pass_type, pass_filter, object_id, pixel_array, num_pixels, depth, result)*
bpy.types.RenderSettings
------------------------
@@ -2145,132 +461,27 @@ bpy.types.RenderSettings
Added
^^^^^
-* :class:`bpy.types.RenderSettings.film_transparent`
-* :class:`bpy.types.RenderSettings.hair_subdiv`
-* :class:`bpy.types.RenderSettings.hair_type`
-* :class:`bpy.types.RenderSettings.preview_pixel_size`
-* :class:`bpy.types.RenderSettings.simplify_gpencil`
-* :class:`bpy.types.RenderSettings.simplify_gpencil_blend`
-* :class:`bpy.types.RenderSettings.simplify_gpencil_onplay`
-* :class:`bpy.types.RenderSettings.simplify_gpencil_remove_lines`
-* :class:`bpy.types.RenderSettings.simplify_gpencil_shader_fx`
-* :class:`bpy.types.RenderSettings.simplify_gpencil_view_fill`
-* :class:`bpy.types.RenderSettings.simplify_gpencil_view_modifier`
-* :class:`bpy.types.RenderSettings.use_sequencer_override_scene_strip`
-* :class:`bpy.types.RenderSettings.use_simplify_smoke_highres`
-* :class:`bpy.types.RenderSettings.use_stamp_frame_range`
-* :class:`bpy.types.RenderSettings.use_stamp_hostname`
+* :class:`bpy.types.RenderSettings.metadata_input`
Removed
^^^^^^^
-* **alpha_mode**
-* **antialiasing_samples**
-* **bake_aa_mode**
-* **bake_distance**
-* **bake_normal_space**
-* **bake_quad_split**
-* **edge_color**
-* **edge_threshold**
-* **field_order**
-* **layers**
-* **motion_blur_samples**
-* **octree_resolution**
-* **pixel_filter_type**
-* **raytrace_method**
-* **simplify_ao_sss**
-* **simplify_shadow_samples**
-* **use_antialiasing**
-* **use_bake_antialiasing**
-* **use_bake_normalize**
-* **use_bake_to_vertex_color**
-* **use_edge_enhance**
-* **use_envmaps**
-* **use_fields**
-* **use_fields_still**
-* **use_free_image_textures**
-* **use_game_engine**
-* **use_instances**
-* **use_local_coords**
-* **use_raytrace**
-* **use_sequencer_gl_textured_solid**
-* **use_shading_nodes**
-* **use_shadows**
-* **use_simplify_triangulate**
-* **use_sss**
-* **use_textures**
-* **use_world_space_shading**
-
-bpy.types.RenderSlot
---------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.RenderSlot.clear`
+* **use_stamp_strip_meta**
-bpy.types.RenderSlots
----------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.RenderSlots.new`
-
-bpy.types.RigidBodyConstraint
------------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.RigidBodyConstraint.spring_type`
-
-bpy.types.RigidBodyObject
--------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.RigidBodyObject.collision_collections`
-
-Removed
-^^^^^^^
-
-* **collision_groups**
-
-bpy.types.RigidBodyWorld
-------------------------
+bpy.types.SceneEEVEE
+--------------------
Added
^^^^^
-* :class:`bpy.types.RigidBodyWorld.collection`
+* :class:`bpy.types.SceneEEVEE.motion_blur_depth_scale`
+* :class:`bpy.types.SceneEEVEE.motion_blur_max`
+* :class:`bpy.types.SceneEEVEE.motion_blur_steps`
Removed
^^^^^^^
-* **group**
-
-bpy.types.SPHFluidSettings
---------------------------
-
-Renamed
-^^^^^^^
-
-* **factor_radius** -> :class:`bpy.types.SPHFluidSettings.use_factor_radius`
-* **factor_repulsion** -> :class:`bpy.types.SPHFluidSettings.use_factor_repulsion`
-* **factor_rest_length** -> :class:`bpy.types.SPHFluidSettings.use_factor_rest_length`
-* **factor_stiff_viscosity** -> :class:`bpy.types.SPHFluidSettings.use_factor_stiff_viscosity`
-
-bpy.types.SceneObjects
-----------------------
-
-Removed
-^^^^^^^
-
-* **active**
-* **link**
-* **unlink**
+* **motion_blur_samples**
bpy.types.Sequence
------------------
@@ -2278,92 +489,15 @@ bpy.types.Sequence
Added
^^^^^
-* :class:`bpy.types.Sequence.override_cache_settings`
-* :class:`bpy.types.Sequence.use_cache_composite`
-* :class:`bpy.types.Sequence.use_cache_preprocessed`
-* :class:`bpy.types.Sequence.use_cache_raw`
-
-bpy.types.EffectSequence
-------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.EffectSequence.playback_direction`
-
-Removed
-^^^^^^^
-
-* **use_reverse_frames**
+* :class:`bpy.types.Sequence.invalidate_cache`
bpy.types.SpeedControlSequence
------------------------------
-Renamed
-^^^^^^^
-
-* **scale_to_length** -> :class:`bpy.types.SpeedControlSequence.use_scale_to_length`
-
-bpy.types.TextSequence
-----------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.TextSequence.font`
-
-bpy.types.ImageSequence
------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.ImageSequence.playback_direction`
-
-Removed
-^^^^^^^
-
-* **use_reverse_frames**
-
-bpy.types.MaskSequence
-----------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.MaskSequence.playback_direction`
-
-Removed
-^^^^^^^
-
-* **use_reverse_frames**
-
-bpy.types.MetaSequence
-----------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.MetaSequence.playback_direction`
-
-Removed
-^^^^^^^
-
-* **use_reverse_frames**
-
-bpy.types.MovieClipSequence
----------------------------
-
Added
^^^^^
-* :class:`bpy.types.MovieClipSequence.fps`
-* :class:`bpy.types.MovieClipSequence.playback_direction`
-
-Removed
-^^^^^^^
-
-* **use_reverse_frames**
+* :class:`bpy.types.SpeedControlSequence.frame_interpolation_mode`
bpy.types.MovieSequence
-----------------------
@@ -2371,290 +505,20 @@ bpy.types.MovieSequence
Added
^^^^^
-* :class:`bpy.types.MovieSequence.fps`
-* :class:`bpy.types.MovieSequence.metadata`
-* :class:`bpy.types.MovieSequence.playback_direction`
-
-Removed
-^^^^^^^
+* :class:`bpy.types.MovieSequence.reload_if_needed`
-* **use_reverse_frames**
-
-bpy.types.SceneSequence
+bpy.types.ShaderFxPixel
-----------------------
Added
^^^^^
-* :class:`bpy.types.SceneSequence.fps`
-* :class:`bpy.types.SceneSequence.playback_direction`
-* :class:`bpy.types.SceneSequence.scene_input`
+* :class:`bpy.types.ShaderFxPixel.use_antialiasing`
Removed
^^^^^^^
-* **use_reverse_frames**
-* **use_sequence**
-
-bpy.types.SequenceEditor
-------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.SequenceEditor.recycle_max_cost`
-* :class:`bpy.types.SequenceEditor.show_cache`
-* :class:`bpy.types.SequenceEditor.show_cache_composite`
-* :class:`bpy.types.SequenceEditor.show_cache_final_out`
-* :class:`bpy.types.SequenceEditor.show_cache_preprocessed`
-* :class:`bpy.types.SequenceEditor.show_cache_raw`
-* :class:`bpy.types.SequenceEditor.use_cache_composite`
-* :class:`bpy.types.SequenceEditor.use_cache_final`
-* :class:`bpy.types.SequenceEditor.use_cache_preprocessed`
-* :class:`bpy.types.SequenceEditor.use_cache_raw`
-
-bpy.types.ShapeKeyBezierPoint
------------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.ShapeKeyBezierPoint.radius`
-* :class:`bpy.types.ShapeKeyBezierPoint.tilt`
-
-bpy.types.ShapeKeyCurvePoint
-----------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.ShapeKeyCurvePoint.radius`
-
-bpy.types.SmokeDomainSettings
------------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.SmokeDomainSettings.clipping`
-* :class:`bpy.types.SmokeDomainSettings.collision_collection`
-* :class:`bpy.types.SmokeDomainSettings.display_interpolation`
-* :class:`bpy.types.SmokeDomainSettings.effector_collection`
-* :class:`bpy.types.SmokeDomainSettings.fluid_collection`
-* :class:`bpy.types.SmokeDomainSettings.temperature_grid`
-
-Removed
-^^^^^^^
-
-* **collision_group**
-* **effector_group**
-* **fluid_group**
-
-Renamed
-^^^^^^^
-
-* **draw_velocity** -> :class:`bpy.types.SmokeDomainSettings.show_velocity`
-* **vector_draw_type** -> :class:`bpy.types.SmokeDomainSettings.vector_display_type`
-
-bpy.types.SoftBodySettings
---------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.SoftBodySettings.collision_collection`
-
-Removed
-^^^^^^^
-
-* **collision_group**
-
-bpy.types.Space
----------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.Space.show_region_header`
-
-bpy.types.SpaceClipEditor
--------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.SpaceClipEditor.mask_display_type`
-* :class:`bpy.types.SpaceClipEditor.show_annotation`
-* :class:`bpy.types.SpaceClipEditor.show_region_hud`
-* :class:`bpy.types.SpaceClipEditor.show_region_toolbar`
-* :class:`bpy.types.SpaceClipEditor.show_region_ui`
-
-Removed
-^^^^^^^
-
-* **mask_draw_type**
-* **show_grease_pencil**
-
-bpy.types.SpaceDopeSheetEditor
-------------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.SpaceDopeSheetEditor.cache_cloth`
-* :class:`bpy.types.SpaceDopeSheetEditor.cache_dynamicpaint`
-* :class:`bpy.types.SpaceDopeSheetEditor.cache_particles`
-* :class:`bpy.types.SpaceDopeSheetEditor.cache_rigidbody`
-* :class:`bpy.types.SpaceDopeSheetEditor.cache_smoke`
-* :class:`bpy.types.SpaceDopeSheetEditor.cache_softbody`
-* :class:`bpy.types.SpaceDopeSheetEditor.show_cache`
-* :class:`bpy.types.SpaceDopeSheetEditor.show_extremes`
-* :class:`bpy.types.SpaceDopeSheetEditor.show_interpolation`
-* :class:`bpy.types.SpaceDopeSheetEditor.show_marker_lines`
-* :class:`bpy.types.SpaceDopeSheetEditor.show_region_ui`
-* :class:`bpy.types.SpaceDopeSheetEditor.ui_mode`
-
-bpy.types.SpaceFileBrowser
---------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.SpaceFileBrowser.show_region_toolbar`
-* :class:`bpy.types.SpaceFileBrowser.show_region_ui`
-
-bpy.types.SpaceGraphEditor
---------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.SpaceGraphEditor.show_marker_lines`
-* :class:`bpy.types.SpaceGraphEditor.show_region_ui`
-
-bpy.types.SpaceImageEditor
---------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.SpaceImageEditor.mask_display_type`
-* :class:`bpy.types.SpaceImageEditor.show_annotation`
-* :class:`bpy.types.SpaceImageEditor.show_region_hud`
-* :class:`bpy.types.SpaceImageEditor.show_region_tool_header`
-* :class:`bpy.types.SpaceImageEditor.show_region_toolbar`
-* :class:`bpy.types.SpaceImageEditor.show_region_ui`
-* :class:`bpy.types.SpaceImageEditor.ui_mode`
-
-Removed
-^^^^^^^
-
-* **mask_draw_type**
-* **show_grease_pencil**
-
-Renamed
-^^^^^^^
-
-* **draw_channels** -> :class:`bpy.types.SpaceImageEditor.display_channels`
-
-bpy.types.SpaceNLA
-------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.SpaceNLA.show_marker_lines`
-* :class:`bpy.types.SpaceNLA.show_region_ui`
-
-bpy.types.SpaceNodeEditor
--------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.SpaceNodeEditor.backdrop_offset`
-* :class:`bpy.types.SpaceNodeEditor.show_annotation`
-* :class:`bpy.types.SpaceNodeEditor.show_region_toolbar`
-* :class:`bpy.types.SpaceNodeEditor.show_region_ui`
-
-Removed
-^^^^^^^
-
-* **backdrop_x**
-* **backdrop_y**
-* **show_grease_pencil**
-
-bpy.types.SpaceOutliner
------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.SpaceOutliner.filter_id_type`
-* :class:`bpy.types.SpaceOutliner.filter_state`
-* :class:`bpy.types.SpaceOutliner.show_restrict_column_enable`
-* :class:`bpy.types.SpaceOutliner.show_restrict_column_hide`
-* :class:`bpy.types.SpaceOutliner.show_restrict_column_holdout`
-* :class:`bpy.types.SpaceOutliner.show_restrict_column_indirect_only`
-* :class:`bpy.types.SpaceOutliner.show_restrict_column_render`
-* :class:`bpy.types.SpaceOutliner.show_restrict_column_select`
-* :class:`bpy.types.SpaceOutliner.show_restrict_column_viewport`
-* :class:`bpy.types.SpaceOutliner.use_filter_children`
-* :class:`bpy.types.SpaceOutliner.use_filter_collection`
-* :class:`bpy.types.SpaceOutliner.use_filter_id_type`
-* :class:`bpy.types.SpaceOutliner.use_filter_object`
-* :class:`bpy.types.SpaceOutliner.use_filter_object_armature`
-* :class:`bpy.types.SpaceOutliner.use_filter_object_camera`
-* :class:`bpy.types.SpaceOutliner.use_filter_object_content`
-* :class:`bpy.types.SpaceOutliner.use_filter_object_empty`
-* :class:`bpy.types.SpaceOutliner.use_filter_object_light`
-* :class:`bpy.types.SpaceOutliner.use_filter_object_mesh`
-* :class:`bpy.types.SpaceOutliner.use_filter_object_others`
-
-Removed
-^^^^^^^
-
-* **show_restrict_columns**
-
-bpy.types.SpaceProperties
--------------------------
-
-Removed
-^^^^^^^
-
-* **align**
-* **texture_context**
-* **use_limited_texture_context**
-
-bpy.types.SpaceSequenceEditor
------------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.SpaceSequenceEditor.show_annotation`
-* :class:`bpy.types.SpaceSequenceEditor.show_marker_lines`
-* :class:`bpy.types.SpaceSequenceEditor.show_region_ui`
-
-Removed
-^^^^^^^
-
-* **show_grease_pencil**
-
-Renamed
-^^^^^^^
-
-* **draw_overexposed** -> :class:`bpy.types.SpaceSequenceEditor.show_overexposed`
-* **waveform_draw_type** -> :class:`bpy.types.SpaceSequenceEditor.waveform_display_type`
-
-bpy.types.SpaceTextEditor
--------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.SpaceTextEditor.show_region_footer`
-* :class:`bpy.types.SpaceTextEditor.show_region_ui`
+* **color**
bpy.types.SpaceView3D
---------------------
@@ -2662,99 +526,10 @@ bpy.types.SpaceView3D
Added
^^^^^
-* :class:`bpy.types.SpaceView3D.icon_from_show_object_viewport`
-* :class:`bpy.types.SpaceView3D.overlay`
-* :class:`bpy.types.SpaceView3D.shading`
-* :class:`bpy.types.SpaceView3D.show_gizmo`
-* :class:`bpy.types.SpaceView3D.show_gizmo_camera_dof_distance`
-* :class:`bpy.types.SpaceView3D.show_gizmo_camera_lens`
-* :class:`bpy.types.SpaceView3D.show_gizmo_context`
-* :class:`bpy.types.SpaceView3D.show_gizmo_empty_force_field`
-* :class:`bpy.types.SpaceView3D.show_gizmo_empty_image`
-* :class:`bpy.types.SpaceView3D.show_gizmo_light_look_at`
-* :class:`bpy.types.SpaceView3D.show_gizmo_light_size`
-* :class:`bpy.types.SpaceView3D.show_gizmo_navigate`
-* :class:`bpy.types.SpaceView3D.show_gizmo_object_rotate`
-* :class:`bpy.types.SpaceView3D.show_gizmo_object_scale`
-* :class:`bpy.types.SpaceView3D.show_gizmo_object_translate`
-* :class:`bpy.types.SpaceView3D.show_gizmo_tool`
-* :class:`bpy.types.SpaceView3D.show_object_select_armature`
-* :class:`bpy.types.SpaceView3D.show_object_select_camera`
-* :class:`bpy.types.SpaceView3D.show_object_select_curve`
-* :class:`bpy.types.SpaceView3D.show_object_select_empty`
-* :class:`bpy.types.SpaceView3D.show_object_select_font`
-* :class:`bpy.types.SpaceView3D.show_object_select_grease_pencil`
-* :class:`bpy.types.SpaceView3D.show_object_select_lattice`
-* :class:`bpy.types.SpaceView3D.show_object_select_light`
-* :class:`bpy.types.SpaceView3D.show_object_select_light_probe`
-* :class:`bpy.types.SpaceView3D.show_object_select_mesh`
-* :class:`bpy.types.SpaceView3D.show_object_select_meta`
-* :class:`bpy.types.SpaceView3D.show_object_select_speaker`
-* :class:`bpy.types.SpaceView3D.show_object_select_surf`
-* :class:`bpy.types.SpaceView3D.show_object_viewport_armature`
-* :class:`bpy.types.SpaceView3D.show_object_viewport_camera`
-* :class:`bpy.types.SpaceView3D.show_object_viewport_curve`
-* :class:`bpy.types.SpaceView3D.show_object_viewport_empty`
-* :class:`bpy.types.SpaceView3D.show_object_viewport_font`
-* :class:`bpy.types.SpaceView3D.show_object_viewport_grease_pencil`
-* :class:`bpy.types.SpaceView3D.show_object_viewport_lattice`
-* :class:`bpy.types.SpaceView3D.show_object_viewport_light`
-* :class:`bpy.types.SpaceView3D.show_object_viewport_light_probe`
-* :class:`bpy.types.SpaceView3D.show_object_viewport_mesh`
-* :class:`bpy.types.SpaceView3D.show_object_viewport_meta`
-* :class:`bpy.types.SpaceView3D.show_object_viewport_speaker`
-* :class:`bpy.types.SpaceView3D.show_object_viewport_surf`
-* :class:`bpy.types.SpaceView3D.show_region_hud`
-* :class:`bpy.types.SpaceView3D.show_region_tool_header`
-* :class:`bpy.types.SpaceView3D.show_region_toolbar`
-* :class:`bpy.types.SpaceView3D.show_region_ui`
-* :class:`bpy.types.SpaceView3D.use_local_camera`
-
-Removed
-^^^^^^^
-
-* **active_layer**
-* **background_images**
-* **current_orientation**
-* **cursor_location**
-* **grid_lines**
-* **grid_scale**
-* **grid_scale_unit**
-* **grid_subdivisions**
-* **layers**
-* **layers_local_view**
-* **layers_used**
-* **lock_camera_and_layers**
-* **matcap_icon**
-* **pivot_point**
-* **show_all_objects_origin**
-* **show_axis_x**
-* **show_axis_y**
-* **show_axis_z**
-* **show_backface_culling**
-* **show_background_images**
-* **show_floor**
-* **show_grease_pencil**
-* **show_manipulator**
-* **show_occlude_wire**
-* **show_only_render**
-* **show_outline_selected**
-* **show_relationship_lines**
-* **show_textured_shadeless**
-* **show_textured_solid**
-* **show_world**
-* **transform_manipulators**
-* **transform_orientation**
-* **use_matcap**
-* **use_occlude_geometry**
-* **use_pivot_point_align**
-* **viewport_shade**
-
-Renamed
-^^^^^^^
-
-* **tracks_draw_size** -> :class:`bpy.types.SpaceView3D.tracks_display_size`
-* **tracks_draw_type** -> :class:`bpy.types.SpaceView3D.tracks_display_type`
+* :class:`bpy.types.SpaceView3D.show_object_select_hair`
+* :class:`bpy.types.SpaceView3D.show_object_select_pointcloud`
+* :class:`bpy.types.SpaceView3D.show_object_viewport_hair`
+* :class:`bpy.types.SpaceView3D.show_object_viewport_pointcloud`
bpy.types.SpaceUVEditor
-----------------------
@@ -2762,298 +537,17 @@ bpy.types.SpaceUVEditor
Added
^^^^^
-* :class:`bpy.types.SpaceUVEditor.edge_display_type`
-* :class:`bpy.types.SpaceUVEditor.pixel_snap_mode`
-* :class:`bpy.types.SpaceUVEditor.show_edges`
-* :class:`bpy.types.SpaceUVEditor.show_pixel_coords`
-
-Removed
-^^^^^^^
-
-* **edge_draw_type**
-* **other_uv_filter**
-* **show_normalized_coords**
-* **show_other_objects**
-* **use_snap_to_pixels**
-
-Renamed
-^^^^^^^
-
-* **draw_stretch_type** -> :class:`bpy.types.SpaceUVEditor.display_stretch_type`
-
-bpy.types.Spline
-----------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.Spline.calc_length`
-
-bpy.types.Struct
-----------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.Struct.property_tags`
-
-bpy.types.TexPaintSlot
-----------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.TexPaintSlot.is_valid`
-
-Removed
-^^^^^^^
-
-* **index**
-
-bpy.types.TextCharacterFormat
------------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.TextCharacterFormat.kerning`
-
-bpy.types.TextureSlot
----------------------
-
-Removed
-^^^^^^^
-
-* **invert**
-* **use_rgb_to_intensity**
-* **use_stencil**
-
-bpy.types.LineStyleTextureSlot
-------------------------------
-
-Removed
-^^^^^^^
-
-* **use_tips**
-
-bpy.types.ParticleSettingsTextureSlot
--------------------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.ParticleSettingsTextureSlot.twist_factor`
-* :class:`bpy.types.ParticleSettingsTextureSlot.use_map_twist`
-
-bpy.types.Theme
----------------
-
-Removed
-^^^^^^^
-
-* **logic_editor**
-* **timeline**
+* :class:`bpy.types.SpaceUVEditor.uv_opacity`
-Renamed
-^^^^^^^
-
-* **user_preferences** -> :class:`bpy.types.Theme.preferences`
-* **user_preferences** -> :class:`bpy.types.Theme.statusbar`
-* **user_preferences** -> :class:`bpy.types.Theme.topbar`
-
-bpy.types.ThemeClipEditor
--------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.ThemeClipEditor.time_scrub_background`
-
-Removed
-^^^^^^^
-
-* **gp_vertex_select**
-* **gp_vertex_size**
-
-Renamed
-^^^^^^^
-
-* **gp_vertex** -> :class:`bpy.types.ThemeClipEditor.metadatabg`
-* **gp_vertex** -> :class:`bpy.types.ThemeClipEditor.metadatatext`
-
-bpy.types.ThemeDopeSheet
-------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.ThemeDopeSheet.interpolation_line`
-* :class:`bpy.types.ThemeDopeSheet.keyframe_movehold`
-* :class:`bpy.types.ThemeDopeSheet.keyframe_movehold_selected`
-* :class:`bpy.types.ThemeDopeSheet.preview_range`
-* :class:`bpy.types.ThemeDopeSheet.time_scrub_background`
-
-bpy.types.ThemeGraphEditor
---------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.ThemeGraphEditor.preview_range`
-* :class:`bpy.types.ThemeGraphEditor.time_scrub_background`
-
-bpy.types.ThemeImageEditor
---------------------------
-
-Removed
-^^^^^^^
-
-* **gp_vertex**
-* **gp_vertex_select**
-* **gp_vertex_size**
-
-bpy.types.ThemeNLAEditor
-------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.ThemeNLAEditor.preview_range`
-* :class:`bpy.types.ThemeNLAEditor.time_scrub_background`
-
-bpy.types.ThemeNodeEditor
--------------------------
-
-Removed
-^^^^^^^
-
-* **gp_vertex**
-* **gp_vertex_select**
-* **gp_vertex_size**
-
-bpy.types.ThemeOutliner
------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.ThemeOutliner.active_object`
-* :class:`bpy.types.ThemeOutliner.edited_object`
-* :class:`bpy.types.ThemeOutliner.row_alternate`
-* :class:`bpy.types.ThemeOutliner.selected_object`
-
-bpy.types.ThemePanelColors
---------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.ThemePanelColors.sub_back`
-
-Removed
-^^^^^^^
-
-* **show_back**
-* **show_header**
-
-bpy.types.ThemeSequenceEditor
------------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.ThemeSequenceEditor.time_scrub_background`
-
-Removed
-^^^^^^^
-
-* **gp_vertex**
-* **gp_vertex_select**
-* **gp_vertex_size**
-
-bpy.types.ThemeSpaceGeneric
----------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.ThemeSpaceGeneric.execution_buts`
-* :class:`bpy.types.ThemeSpaceGeneric.navigation_bar`
-
-bpy.types.ThemeSpaceGradient
-----------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.ThemeSpaceGradient.execution_buts`
-* :class:`bpy.types.ThemeSpaceGradient.navigation_bar`
-
-bpy.types.ThemeUserInterface
-----------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.ThemeUserInterface.editor_outline`
-* :class:`bpy.types.ThemeUserInterface.gizmo_a`
-* :class:`bpy.types.ThemeUserInterface.gizmo_b`
-* :class:`bpy.types.ThemeUserInterface.gizmo_hi`
-* :class:`bpy.types.ThemeUserInterface.gizmo_primary`
-* :class:`bpy.types.ThemeUserInterface.gizmo_secondary`
-* :class:`bpy.types.ThemeUserInterface.icon_border_intensity`
-* :class:`bpy.types.ThemeUserInterface.icon_collection`
-* :class:`bpy.types.ThemeUserInterface.icon_modifier`
-* :class:`bpy.types.ThemeUserInterface.icon_object`
-* :class:`bpy.types.ThemeUserInterface.icon_object_data`
-* :class:`bpy.types.ThemeUserInterface.icon_saturation`
-* :class:`bpy.types.ThemeUserInterface.icon_scene`
-* :class:`bpy.types.ThemeUserInterface.icon_shading`
-* :class:`bpy.types.ThemeUserInterface.wcol_tab`
-* :class:`bpy.types.ThemeUserInterface.wcol_toolbar_item`
-
-Removed
-^^^^^^^
-
-* **icon_file**
-
-bpy.types.ThemeView3D
----------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.ThemeView3D.object_origin_size`
+bpy.types.ThemeInfo
+-------------------
Removed
^^^^^^^
-* **object_grouped**
-* **object_grouped_active**
-
-Renamed
-^^^^^^^
-
-* **lamp** -> :class:`bpy.types.ThemeView3D.light`
-
-bpy.types.ThemeWidgetColors
----------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.ThemeWidgetColors.roundness`
-
-bpy.types.ThemeWidgetStateColors
---------------------------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.ThemeWidgetStateColors.inner_changed`
-* :class:`bpy.types.ThemeWidgetStateColors.inner_changed_sel`
-* :class:`bpy.types.ThemeWidgetStateColors.inner_overridden`
-* :class:`bpy.types.ThemeWidgetStateColors.inner_overridden_sel`
+* **info_report_error**
+* **info_report_info**
+* **info_report_warning**
bpy.types.ToolSettings
----------------------
@@ -3061,60 +555,8 @@ bpy.types.ToolSettings
Added
^^^^^
-* :class:`bpy.types.ToolSettings.annotation_stroke_placement_view3d`
-* :class:`bpy.types.ToolSettings.annotation_thickness`
-* :class:`bpy.types.ToolSettings.gpencil_paint`
-* :class:`bpy.types.ToolSettings.gpencil_selectmode`
-* :class:`bpy.types.ToolSettings.gpencil_stroke_snap_mode`
-* :class:`bpy.types.ToolSettings.lock_object_mode`
-* :class:`bpy.types.ToolSettings.normal_vector`
-* :class:`bpy.types.ToolSettings.transform_pivot_point`
-* :class:`bpy.types.ToolSettings.use_gpencil_thumbnail_list`
-* :class:`bpy.types.ToolSettings.use_gpencil_weight_data_add`
-* :class:`bpy.types.ToolSettings.use_keyframe_cycle_aware`
-* :class:`bpy.types.ToolSettings.use_proportional_connected`
-* :class:`bpy.types.ToolSettings.use_proportional_edit`
-* :class:`bpy.types.ToolSettings.use_proportional_projected`
-* :class:`bpy.types.ToolSettings.use_snap_rotate`
-* :class:`bpy.types.ToolSettings.use_snap_scale`
-* :class:`bpy.types.ToolSettings.use_snap_translate`
-* :class:`bpy.types.ToolSettings.use_transform_pivot_point_align`
-
-Removed
-^^^^^^^
-
-* **edge_path_mode**
-* **etch_adaptive_limit**
-* **etch_convert_mode**
-* **etch_length_limit**
-* **etch_number**
-* **etch_roll_mode**
-* **etch_side**
-* **etch_subdivision_number**
-* **etch_template**
-* **gpencil_brushes**
-* **gpencil_stroke_placement_image_editor**
-* **gpencil_stroke_placement_view2d**
-* **grease_pencil_source**
-* **normal_size**
-* **proportional_edit**
-* **use_bone_sketching**
-* **use_etch_autoname**
-* **use_etch_overdraw**
-* **use_etch_quick**
-* **use_gpencil_continuous_drawing**
-* **use_uv_sculpt**
-* **uv_sculpt_tool**
-
-Renamed
-^^^^^^^
-
-* **edge_path_live_unwrap** -> :class:`bpy.types.ToolSettings.use_edge_path_live_unwrap`
-* **gpencil_stroke_placement_sequencer_preview** -> :class:`bpy.types.ToolSettings.annotation_stroke_placement_image_editor`
-* **gpencil_stroke_placement_sequencer_preview** -> :class:`bpy.types.ToolSettings.annotation_stroke_placement_sequencer_preview`
-* **gpencil_stroke_placement_sequencer_preview** -> :class:`bpy.types.ToolSettings.annotation_stroke_placement_view2d`
-* **snap_element** -> :class:`bpy.types.ToolSettings.snap_elements`
-* **use_gpencil_additive_drawing** -> :class:`bpy.types.ToolSettings.use_gpencil_draw_additive`
+* :class:`bpy.types.ToolSettings.use_transform_correct_face_attributes`
+* :class:`bpy.types.ToolSettings.use_transform_correct_keep_connected`
bpy.types.UILayout
------------------
@@ -3122,229 +564,45 @@ bpy.types.UILayout
Added
^^^^^
-* :class:`bpy.types.UILayout.activate_init`
-* :class:`bpy.types.UILayout.active_default`
-* :class:`bpy.types.UILayout.direction`
-* :class:`bpy.types.UILayout.emboss`
-* :class:`bpy.types.UILayout.grid_flow`
-* :class:`bpy.types.UILayout.menu_contents`
-* :class:`bpy.types.UILayout.operator_menu_hold`
-* :class:`bpy.types.UILayout.popover`
-* :class:`bpy.types.UILayout.popover_group`
-* :class:`bpy.types.UILayout.prop_tabs_enum`
-* :class:`bpy.types.UILayout.prop_with_menu`
-* :class:`bpy.types.UILayout.prop_with_popover`
-* :class:`bpy.types.UILayout.template_ID_tabs`
-* :class:`bpy.types.UILayout.template_greasepencil_color`
-* :class:`bpy.types.UILayout.template_greasepencil_modifier`
-* :class:`bpy.types.UILayout.template_icon`
-* :class:`bpy.types.UILayout.template_recent_files`
-* :class:`bpy.types.UILayout.template_search`
-* :class:`bpy.types.UILayout.template_search_preview`
-* :class:`bpy.types.UILayout.template_shaderfx`
-* :class:`bpy.types.UILayout.ui_units_x`
-* :class:`bpy.types.UILayout.ui_units_y`
-* :class:`bpy.types.UILayout.use_property_decorate`
-* :class:`bpy.types.UILayout.use_property_split`
+* :class:`bpy.types.UILayout.prop_decorator`
+* :class:`bpy.types.UILayout.template_constraint_header`
+* :class:`bpy.types.UILayout.template_constraints`
+* :class:`bpy.types.UILayout.template_grease_pencil_modifiers`
+* :class:`bpy.types.UILayout.template_modifiers`
Removed
^^^^^^^
-* **introspect**
-
-Renamed
-^^^^^^^
-
-* **template_header_3D** -> :class:`bpy.types.UILayout.separator_spacer`
-* **template_header_3D** -> :class:`bpy.types.UILayout.template_header_3D_mode`
-* **template_header_3D** -> :class:`bpy.types.UILayout.template_input_status`
+* **template_constraint**
+* **template_greasepencil_modifier**
+* **template_modifier**
Function Arguments
^^^^^^^^^^^^^^^^^^
-* :class:`bpy.types.UILayout.operator` (operator, text, text_ctxt, translate, icon, emboss, depress, icon_value), *was (operator, text, text_ctxt, translate, icon, emboss, icon_value)*
-* :class:`bpy.types.UILayout.prop` (data, property, text, text_ctxt, translate, icon, expand, slider, toggle, icon_only, event, full_event, emboss, index, icon_value, invert_checkbox), *was (data, property, text, text_ctxt, translate, icon, expand, slider, toggle, icon_only, event, full_event, emboss, index, icon_value)*
-* :class:`bpy.types.UILayout.separator` (factor), *was ()*
-* :class:`bpy.types.UILayout.split` (factor, align), *was (percentage, align)*
-* :class:`bpy.types.UILayout.template_ID` (data, property, new, open, unlink, filter, live_icon), *was (data, property, new, open, unlink)*
-* :class:`bpy.types.UILayout.template_ID_preview` (data, property, new, open, unlink, rows, cols, filter, hide_buttons), *was (data, property, new, open, unlink, rows, cols)*
-* :class:`bpy.types.UILayout.template_curve_mapping` (data, property, type, levels, brush, use_negative_slope, show_tone), *was (data, property, type, levels, brush, use_negative_slope)*
-* :class:`bpy.types.UILayout.template_icon_view` (data, property, show_labels, scale, scale_popup), *was (data, property, show_labels, scale)*
-* :class:`bpy.types.UILayout.template_list` (listtype_name, list_id, dataptr, propname, active_dataptr, active_propname, item_dyntip_propname, rows, maxrows, type, columns, sort_reverse, sort_lock), *was (listtype_name, list_id, dataptr, propname, active_dataptr, active_propname, item_dyntip_propname, rows, maxrows, type, columns)*
-
-bpy.types.UIList
-----------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.UIList.use_filter_sort_lock`
-
-bpy.types.CLIP_UL_tracking_objects
-----------------------------------
-
-Function Arguments
-^^^^^^^^^^^^^^^^^^
+* :class:`bpy.types.UILayout.column` (align, heading, heading_ctxt, translate), *was (align)*
+* :class:`bpy.types.UILayout.row` (align, heading, heading_ctxt, translate), *was (align)*
+* :class:`bpy.types.UILayout.template_shaderfx` (), *was (data)*
-* :class:`bpy.types.CLIP_UL_tracking_objects.draw_item` (self, _context, layout, _data, item, _icon, _active_data, _active_propname, _index), *was (self, context, layout, data, item, icon, active_data, active_propname, index)*
-
-bpy.types.FILEBROWSER_UL_dir
-----------------------------
-
-Function Arguments
-^^^^^^^^^^^^^^^^^^
-
-* :class:`bpy.types.FILEBROWSER_UL_dir.draw_item` (self, _context, layout, _data, item, icon, _active_data, active_propname, _index), *was (self, context, layout, data, item, icon, active_data, active_propname, index)*
-
-bpy.types.GPENCIL_UL_layer
---------------------------
-
-Function Arguments
-^^^^^^^^^^^^^^^^^^
-
-* :class:`bpy.types.GPENCIL_UL_layer.draw_item` (self, _context, layout, _data, item, icon, _active_data, _active_propname, _index), *was (self, context, layout, data, item, icon, active_data, active_propname, index)*
-
-bpy.types.MASK_UL_layers
-------------------------
-
-Function Arguments
-^^^^^^^^^^^^^^^^^^
-
-* :class:`bpy.types.MASK_UL_layers.draw_item` (self, _context, layout, _data, item, icon, _active_data, _active_propname, _index), *was (self, context, layout, data, item, icon, active_data, active_propname, index)*
-
-bpy.types.MATERIAL_UL_matslots
-------------------------------
-
-Function Arguments
-^^^^^^^^^^^^^^^^^^
-
-* :class:`bpy.types.MATERIAL_UL_matslots.draw_item` (self, _context, layout, _data, item, icon, _active_data, _active_propname, _index), *was (self, context, layout, data, item, icon, active_data, active_propname, index)*
-
-bpy.types.MESH_UL_shape_keys
-----------------------------
-
-Function Arguments
-^^^^^^^^^^^^^^^^^^
-
-* :class:`bpy.types.MESH_UL_shape_keys.draw_item` (self, _context, layout, _data, item, icon, active_data, _active_propname, index), *was (self, context, layout, data, item, icon, active_data, active_propname, index)*
-
-bpy.types.MESH_UL_vgroups
--------------------------
-
-Function Arguments
-^^^^^^^^^^^^^^^^^^
-
-* :class:`bpy.types.MESH_UL_vgroups.draw_item` (self, _context, layout, _data, item, icon, _active_data_, _active_propname, _index), *was (self, context, layout, data, item, icon, active_data, active_propname, index)*
-
-bpy.types.NODE_UL_interface_sockets
------------------------------------
-
-Function Arguments
-^^^^^^^^^^^^^^^^^^
-
-* :class:`bpy.types.NODE_UL_interface_sockets.draw_item` (self, context, layout, _data, item, icon, _active_data, _active_propname, _index), *was (self, context, layout, data, item, icon, active_data, active_propname, index)*
-
-bpy.types.PARTICLE_UL_particle_systems
---------------------------------------
-
-Function Arguments
-^^^^^^^^^^^^^^^^^^
-
-* :class:`bpy.types.PARTICLE_UL_particle_systems.draw_item` (self, _context, layout, data, item, icon, _active_data, _active_propname, _index, _flt_flag), *was (self, context, layout, data, item, icon, active_data, active_propname, index, flt_flag)*
-
-bpy.types.PHYSICS_UL_dynapaint_surfaces
----------------------------------------
-
-Function Arguments
-^^^^^^^^^^^^^^^^^^
-
-* :class:`bpy.types.PHYSICS_UL_dynapaint_surfaces.draw_item` (self, _context, layout, _data, item, icon, _active_data, _active_propname, _index), *was (self, context, layout, data, item, icon, active_data, active_propname, index)*
-
-bpy.types.SCENE_UL_keying_set_paths
------------------------------------
-
-Function Arguments
-^^^^^^^^^^^^^^^^^^
-
-* :class:`bpy.types.SCENE_UL_keying_set_paths.draw_item` (self, _context, layout, _data, item, icon, _active_data, _active_propname, _index), *was (self, context, layout, data, item, icon, active_data, active_propname, index)*
-
-bpy.types.TEXTURE_UL_texpaintslots
-----------------------------------
-
-Function Arguments
-^^^^^^^^^^^^^^^^^^
-
-* :class:`bpy.types.TEXTURE_UL_texpaintslots.draw_item` (self, _context, layout, _data, item, icon, _active_data, _active_propname, _index), *was (self, context, layout, data, item, icon, active_data, active_propname, index)*
-
-bpy.types.TEXTURE_UL_texslots
------------------------------
-
-Function Arguments
-^^^^^^^^^^^^^^^^^^
-
-* :class:`bpy.types.TEXTURE_UL_texslots.draw_item` (self, _context, layout, _data, item, icon, _active_data, _active_propname, _index), *was (self, context, layout, data, item, icon, active_data, active_propname, index)*
-
-bpy.types.UVLoopLayers
-----------------------
+bpy.types.View3DOverlay
+-----------------------
Added
^^^^^
-* :class:`bpy.types.UVLoopLayers.new`
-* :class:`bpy.types.UVLoopLayers.remove`
-
-bpy.types.UnitSettings
-----------------------
+* :class:`bpy.types.View3DOverlay.display_handle`
+* :class:`bpy.types.View3DOverlay.show_stats`
+* :class:`bpy.types.View3DOverlay.use_gpencil_canvas_xray`
-Added
-^^^^^
+Removed
+^^^^^^^
-* :class:`bpy.types.UnitSettings.length_unit`
-* :class:`bpy.types.UnitSettings.mass_unit`
-* :class:`bpy.types.UnitSettings.time_unit`
+* **show_curve_handles**
-bpy.types.UserSolidLight
+bpy.types.XrSessionState
------------------------
Added
^^^^^
-* :class:`bpy.types.UserSolidLight.smooth`
-
-bpy.types.Window
-----------------
-
-Added
-^^^^^
-
-* :class:`bpy.types.Window.event_simulate`
-* :class:`bpy.types.Window.parent`
-* :class:`bpy.types.Window.scene`
-* :class:`bpy.types.Window.view_layer`
-* :class:`bpy.types.Window.workspace`
-
-bpy.types.WorldLighting
------------------------
-
-Removed
-^^^^^^^
-
-* **adapt_to_speed**
-* **ao_blend_type**
-* **bias**
-* **correction**
-* **environment_color**
-* **environment_energy**
-* **error_threshold**
-* **falloff_strength**
-* **gather_method**
-* **indirect_bounces**
-* **indirect_factor**
-* **passes**
-* **sample_method**
-* **samples**
-* **threshold**
-* **use_cache**
-* **use_environment_light**
-* **use_falloff**
-* **use_indirect_light**
+* :class:`bpy.types.XrSessionState.reset_to_base_pose`
diff --git a/extern/bullet2/CMakeLists.txt b/extern/bullet2/CMakeLists.txt
index b386aa4035b..9d0557ded7d 100644
--- a/extern/bullet2/CMakeLists.txt
+++ b/extern/bullet2/CMakeLists.txt
@@ -419,7 +419,8 @@ if(MSVC)
# bullet is responsible for quite a few silly warnings
# suppress all of them. Not great, but they really needed
# to sort that out themselves.
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W0")
+ remove_cc_flag("/W3")
+ add_c_flag("/W0")
endif()
blender_add_lib(extern_bullet "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/extern/discregrid/discregrid/include/Discregrid/cubic_lagrange_discrete_grid.hpp b/extern/discregrid/discregrid/include/Discregrid/cubic_lagrange_discrete_grid.hpp
index d6d87d3a791..ba8cea8079f 100755
--- a/extern/discregrid/discregrid/include/Discregrid/cubic_lagrange_discrete_grid.hpp
+++ b/extern/discregrid/discregrid/include/Discregrid/cubic_lagrange_discrete_grid.hpp
@@ -9,7 +9,7 @@ class CubicLagrangeDiscreteGrid : public DiscreteGrid
{
public:
- CubicLagrangeDiscreteGrid(){}
+ CubicLagrangeDiscreteGrid(){ this->m_n_cells = 0; }
CubicLagrangeDiscreteGrid(std::string const& filename);
CubicLagrangeDiscreteGrid(Eigen::AlignedBox3d const& domain,
std::array<unsigned int, 3> const& resolution);
diff --git a/extern/mantaflow/CMakeLists.txt b/extern/mantaflow/CMakeLists.txt
index 989f45c69b4..8bb0bc69f98 100644
--- a/extern/mantaflow/CMakeLists.txt
+++ b/extern/mantaflow/CMakeLists.txt
@@ -64,9 +64,6 @@ endif()
if(WITH_OPENVDB)
add_definitions(-DOPENVDB=1)
- if(NOT WIN32)
- add_definitions(-DOPENVDB_STATICLIB)
- endif()
endif()
if(WITH_OPENVDB_BLOSC)
diff --git a/extern/quadriflow/CMakeLists.txt b/extern/quadriflow/CMakeLists.txt
index bf64e5edb67..e24151d8bf6 100644
--- a/extern/quadriflow/CMakeLists.txt
+++ b/extern/quadriflow/CMakeLists.txt
@@ -28,9 +28,6 @@ if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang")
)
endif()
-set(CMAKE_CXX_STANDARD 14)
-set(CMAKE_CXX_STANDARD_REQUIRED ON)
-
if(WIN32)
add_definitions(-D_USE_MATH_DEFINES)
endif()
diff --git a/extern/quadriflow/patches/blender.patch b/extern/quadriflow/patches/blender.patch
index eceda489393..6405a47e262 100644
--- a/extern/quadriflow/patches/blender.patch
+++ b/extern/quadriflow/patches/blender.patch
@@ -1,3 +1,15 @@
+diff --git a/extern/quadriflow/src/loader.cpp b/extern/quadriflow/src/loader.cpp
+--- a/extern/quadriflow/src/loader.cpp
++++ b/extern/quadriflow/src/loader.cpp
+@@ -69,7 +69,7 @@
+ };
+
+ /// Hash function for obj_vertex
+- struct obj_vertexHash : std::unary_function<obj_vertex, size_t> {
++ struct obj_vertexHash {
+ std::size_t operator()(const obj_vertex &v) const {
+ size_t hash = std::hash<uint32_t>()(v.p);
+ hash = hash * 37 + std::hash<uint32_t>()(v.uv);
diff --git a/extern/quadriflow/src/config.hpp b/extern/quadriflow/src/config.hpp
index 842b885..bf597ad 100644
--- a/extern/quadriflow/src/config.hpp
diff --git a/extern/quadriflow/src/loader.cpp b/extern/quadriflow/src/loader.cpp
index aa27066e6e4..a1596eeff9a 100644
--- a/extern/quadriflow/src/loader.cpp
+++ b/extern/quadriflow/src/loader.cpp
@@ -69,7 +69,7 @@ void load(const char* filename, MatrixXd& V, MatrixXi& F)
};
/// Hash function for obj_vertex
- struct obj_vertexHash : std::unary_function<obj_vertex, size_t> {
+ struct obj_vertexHash {
std::size_t operator()(const obj_vertex &v) const {
size_t hash = std::hash<uint32_t>()(v.p);
hash = hash * 37 + std::hash<uint32_t>()(v.uv);
diff --git a/extern/softbody/src/admmpd_bvh.cpp b/extern/softbody/src/admmpd_bvh.cpp
index b9e49a29668..e56729c579f 100644
--- a/extern/softbody/src/admmpd_bvh.cpp
+++ b/extern/softbody/src/admmpd_bvh.cpp
@@ -267,9 +267,14 @@ void Octree<T,DIM>::init(const MatrixXT *V, const Eigen::MatrixXi *F, int stopde
boxes[i].extend(V->row(f[0]).transpose());
boxes[i].extend(V->row(f[1]).transpose());
boxes[i].extend(V->row(f[2]).transpose());
+ boxes[i].extend(boxes[i].min()-VecType::Ones()*1e-4);
+ boxes[i].extend(boxes[i].max()+VecType::Ones()*1e-4);
global_box.extend(boxes[i]);
}
+ global_box.extend(global_box.min()-VecType::Ones()*1e-2);
+ global_box.extend(global_box.max()+VecType::Ones()*1e-2);
+
T halfwidth = global_box.sizes().maxCoeff()*0.5;
m_root.reset(
create_children(global_box.center(),halfwidth,stopdepth,V,F,queue,boxes)
diff --git a/extern/softbody/src/admmpd_bvh.h b/extern/softbody/src/admmpd_bvh.h
index 647442bc4d7..2ad53d0fad8 100644
--- a/extern/softbody/src/admmpd_bvh.h
+++ b/extern/softbody/src/admmpd_bvh.h
@@ -61,6 +61,12 @@ public:
}
};
+ // Return AABB of root
+ Eigen::AlignedBox<T,DIM> bounds() const {
+ if (m_root) { return m_root->aabb; }
+ return Eigen::AlignedBox<T,DIM>();
+ }
+
// Return ptr to the root node
// Becomes invalidated after init()
std::shared_ptr<Node> root() { return m_root; }
diff --git a/extern/softbody/src/admmpd_collision.cpp b/extern/softbody/src/admmpd_collision.cpp
index 78889b810b8..c6751b3c279 100644
--- a/extern/softbody/src/admmpd_collision.cpp
+++ b/extern/softbody/src/admmpd_collision.cpp
@@ -26,83 +26,110 @@ VFCollisionPair::VFCollisionPair() :
q_bary(0,0,0)
{}
-void Collision::ObstacleData::clear() {
- V = MatrixXd();
- F = MatrixXi();
- sdf = Discregrid::CubicLagrangeDiscreteGrid();
-}
-
-
-bool Collision::set_obstacles(
- const float *v0,
- const float *v1,
- int nv,
- const unsigned int *faces,
- int nf,
- std::string *err)
+bool Collision::ObstacleData::compute_sdf(int idx)
{
- (void)(v0);
- if (nv==0 || nf==0)
- {
- if (err) { *err = "Collision obstacle has no verts or faces"; }
- obsdata.clear();
+ if (idx < 0 || idx >x1.size()) {
return false;
}
- std::vector<double> v1_dbl(nv*3);
- Eigen::AlignedBox<double,3> domain;
-
- if (obsdata.V.rows() != nv)
- obsdata.V.resize(nv,3);
- for (int i=0; i<nv; ++i)
- {
- for (int j=0; j<3; ++j)
- {
- obsdata.V(i,j) = v1[i*3+j];
- v1_dbl[i*3+j] = v1[i*3+j];
- }
- domain.extend(obsdata.V.row(i).transpose());
- }
-
- if (obsdata.F.rows() != nf)
- obsdata.F.resize(nf,3);
- for (int i=0; i<nf; ++i)
- {
- for (int j=0; j<3; ++j)
- obsdata.F(i,j) = faces[i*3+j];
+ // There was an error in init
+ if (box[idx].isEmpty()) {
+ return false;
}
- // Is the mesh closed?
- Discregrid::TriangleMesh tm(v1_dbl.data(), faces, nv, nf);
+ // Test that the mesh is closed
+ Discregrid::TriangleMesh tm(
+ (double const*)x1[idx].data(),
+ (unsigned int const*)F[idx].data(),
+ x1[idx].rows(), F[idx].rows());
if (!tm.is_closed()) {
- if (err) { *err = "Collision obstacle not a closed mesh - ignoring"; }
- obsdata.clear();
return false;
}
// Generate signed distance field
- {
- Discregrid::MeshDistance md(tm);
- domain.max() += 1e-3 * domain.diagonal().norm() * Eigen::Vector3d::Ones();
- domain.min() -= 1e-3 * domain.diagonal().norm() * Eigen::Vector3d::Ones();
- std::array<unsigned int, 3> resolution;
- resolution[0] = 30; resolution[1] = 30; resolution[2] = 30;
- obsdata.sdf = Discregrid::CubicLagrangeDiscreteGrid(domain, resolution);
- auto func = Discregrid::DiscreteGrid::ContinuousFunction{};
- std::vector<std::thread::id> thread_map;
- md.set_thread_map(&thread_map);
- func = [&md](Eigen::Vector3d const& xi) {
- return md.signedDistanceCached(xi);
- };
- obsdata.sdf.addFunction(func, &thread_map, false);
+ Discregrid::MeshDistance md(tm);
+ std::array<unsigned int, 3> resolution;
+ resolution[0] = 30; resolution[1] = 30; resolution[2] = 30;
+ sdf[idx] = Discregrid::CubicLagrangeDiscreteGrid(box[idx], resolution);
+ auto func = Discregrid::DiscreteGrid::ContinuousFunction{};
+ std::vector<std::thread::id> thread_map;
+ md.set_thread_map(&thread_map);
+ func = [&md](Eigen::Vector3d const& xi) {
+ return md.signedDistanceCached(xi);
+ };
+ sdf[idx].addFunction(func, &thread_map, false);
+
+ if (sdf[idx].nCells()==0) {
+ return false;
}
+ return true;
+}
- if (obsdata.sdf.nCells()==0) {
- if (err) { *err = "SDF gen failed for collision obstacle"; }
- obsdata.clear();
+bool Collision::set_obstacles(
+ std::vector<Eigen::MatrixXd> &v0,
+ std::vector<Eigen::MatrixXd> &v1,
+ std::vector<Eigen::MatrixXi> &F,
+ std::string *err)
+{
+ if (v0.size() != v1.size() || v0.size() != F.size()) {
+ if (err) { *err = "Bad dimensions on obstacle input"; }
return false;
}
+ // Copy the obstacle data from the input to the stored
+ // data container. If the vertex locations have changed,
+ // we need to recompute the SDF. Otherwise, leave it as is.
+ int n_obs_new = v0.size();
+ int n_obs_old = obsdata.x0.size();
+ obsdata.sdf.resize(n_obs_new);
+ obsdata.x0.resize(n_obs_new);
+ obsdata.x1.resize(n_obs_new);
+ obsdata.F.resize(n_obs_new);
+ obsdata.box.resize(n_obs_new);
+
+ // We can use isApprox for testing if the obstacle has
+ // moved from the last call to set_obstacles. The SDF
+ // has limited accuracy anyway...
+ double approx_eps = 1e-6;
+ for (int i=0; i<n_obs_new; ++i) {
+
+ bool reset_obs = false;
+ if (i >= n_obs_old) {
+ reset_obs=true; // is new obs
+ }
+ else if (!obsdata.x1[i].isApprox(v1[i],approx_eps) ||
+ !obsdata.x0[i].isApprox(v0[i],approx_eps)) {
+ reset_obs = true; // is different than before
+ }
+
+ if (reset_obs) {
+
+ obsdata.box[i].setEmpty();
+ int nv = v1[i].rows();
+ for (int j=0; j<nv; ++j) {
+ obsdata.box[i].extend(v1[i].row(j).transpose());
+ }
+ obsdata.box[i].max() += 1e-3 * obsdata.box[i].diagonal().norm() * Eigen::Vector3d::Ones();
+ obsdata.box[i].min() -= 1e-3 * obsdata.box[i].diagonal().norm() * Eigen::Vector3d::Ones();
+
+ obsdata.sdf[i] = SDFType(); // clear old sdf
+ obsdata.x0[i] = v0[i];
+ obsdata.x1[i] = v1[i];
+ obsdata.F[i] = F[i].cast<unsigned int>();
+
+ // Determine if the triangle mesh is closed or not.
+ // We want to provide a warning if it is.
+ Discregrid::TriangleMesh tm(
+ (double const*)obsdata.x1[i].data(),
+ (unsigned int const*)obsdata.F[i].data(),
+ obsdata.x1[i].rows(), obsdata.F[i].rows());
+ if (!tm.is_closed()) {
+ obsdata.box[i].setEmpty();
+ if (err) { *err = "Collision obstacle not a closed mesh - ignoring"; }
+ }
+ }
+ }
+
return true;
} // end add obstacle
@@ -121,29 +148,31 @@ Collision::detect_against_obs(
(void)(data);
(void)(pt_t0);
- std::pair<bool,VFCollisionPair> ret =
- std::make_pair(false, VFCollisionPair());
-
- if (!obs->has_obs())
- return ret;
+ int n_obs = obs->num_obs();
+ if (n_obs==0) {
+ return std::make_pair(false, VFCollisionPair());
+ }
- // So I feel bad because we're using the SDF only
- // for inside/outside query. Unfortunately this implementation
- // doesn't store the face indices within the grid cells, so
- // the interpolate function won't return the nearest
- // face at the gradient + distance.
- Vector3d n;
- double dist = obs->sdf.interpolate(0, pt_t1, &n);
- if (dist > 0)
+ for (int i=0; i<n_obs; ++i)
+ {
+ if (obs->sdf[i].nCells()==0) {
+ continue; // not initialized
+ }
+ Vector3d n;
+ double dist = obs->sdf[i].interpolate(0, pt_t1, &n);
+ if (dist > 0) { continue; } // not colliding
+
+ std::pair<bool,VFCollisionPair> ret =
+ std::make_pair(true, VFCollisionPair());
+ ret.first = true;
+ ret.second.q_idx = -1;
+ ret.second.q_is_obs = true;
+ ret.second.q_bary.setZero();
+ ret.second.q_pt = pt_t1 - dist*n;
+ ret.second.q_n = n.normalized();
return ret;
-
- ret.first = true;
- ret.second.q_idx = -1;
- ret.second.q_is_obs = true;
- ret.second.q_bary.setZero();
- ret.second.q_pt = pt_t1 - dist*n;
- ret.second.q_n = n.normalized();
- return ret;
+ }
+ return std::make_pair(false, VFCollisionPair());
}
int EmbeddedMeshCollision::detect(
@@ -153,17 +182,35 @@ int EmbeddedMeshCollision::detect(
const Eigen::MatrixXd *x0,
const Eigen::MatrixXd *x1)
{
- if (!mesh)
+ if (!mesh) {
return 0;
+ }
- if (mesh->type() != MESHTYPE_EMBEDDED)
+ if (mesh->type() != MESHTYPE_EMBEDDED) {
return 0;
+ }
- // Do we even need to process collisions?
- if (!this->obsdata.has_obs() && !options->self_collision)
- {
- if (x1->col(1).minCoeff() > options->floor)
- {
+ // Compute SDFs if the mesh is intersecting
+ // the associated obstacle. The sdf generation is internally threaded,
+ // but it might be faster to thread the different SDFs.
+ bool has_obs_intersection = false;
+ int n_obs = obsdata.num_obs();
+ AlignedBox<double,3> mesh_box = data->col.prim_tree.bounds();
+ for (int i=0; i<n_obs; ++i) {
+ AlignedBox<double,3> &box = obsdata.box[i];
+ if (box.isEmpty()) { continue; }
+ if (!box.intersects(mesh_box)) { continue; }
+ has_obs_intersection = true;
+ // Do we need to generate a new SDF?
+ if (obsdata.sdf[i].nCells()==0) {
+ obsdata.compute_sdf(i);
+ }
+ }
+
+ // Do we even need to process collisions and launch
+ // the per-vertex threads?
+ if (!has_obs_intersection && !options->self_collision) {
+ if (x1->col(2).minCoeff() > options->floor) {
return 0;
}
}
@@ -171,8 +218,9 @@ int EmbeddedMeshCollision::detect(
// We store the results of the collisions in a per-vertex buffer.
// This is a workaround so we can create them in threads.
int nev = mesh->rest_facet_verts()->rows();
- if ((int)per_vertex_pairs.size() != nev)
+ if ((int)per_vertex_pairs.size() != nev) {
per_vertex_pairs.resize(nev, std::vector<VFCollisionPair>());
+ }
//
// Thread data for detection
@@ -215,6 +263,10 @@ int EmbeddedMeshCollision::detect(
Vector3d pt_t0 = td->mesh->get_mapped_facet_vertex(td->x0,vi);
Vector3d pt_t1 = td->mesh->get_mapped_facet_vertex(td->x1,vi);
+// if (td->options->log_level >= LOGLEVEL_DEBUG) {
+// printf("\tDetecting collisions for emb vertex %d: %f %f %f\n", vi, pt_t1[0], pt_t1[1], pt_t1[2]);
+// }
+
// Special case, check if we are below the floor
if (pt_t1[2] < td->options->floor)
{
@@ -231,7 +283,8 @@ int EmbeddedMeshCollision::detect(
}
// Detect against obstacles
- if (td->obsdata->has_obs())
+ bool had_obstacle_collision = false;
+ if (td->obsdata->num_obs()>0)
{
std::pair<bool,VFCollisionPair> pt_hit_obs =
td->collision->detect_against_obs(
@@ -247,13 +300,14 @@ int EmbeddedMeshCollision::detect(
pt_hit_obs.second.p_is_obs = false;
pt_res.emplace_back(vi,vi_pairs.size());
vi_pairs.emplace_back(pt_hit_obs.second);
+ had_obstacle_collision = true;
}
}
- // We perform self collision if the self_collision flag is true and either:
- // a) the set of vertices (vertex group) to do self collision is empty
- // b) the vertex is in the set of self collision vertices
- bool do_self_collision = td->options->self_collision;
+ // We perform self collision if the self_collision flag is true and:
+ // a) there was no obstacle collision
+ // b) the vertex is in the set of self collision vertices (or its empty)
+ bool do_self_collision = !had_obstacle_collision && td->options->self_collision;
if (do_self_collision) {
if (td->data->col.selfcollision_verts.size()>0) {
do_self_collision = td->data->col.selfcollision_verts.count(vi)>0;
@@ -304,15 +358,24 @@ int EmbeddedMeshCollision::detect(
// all of the BVH traversals and the other threads do none.
// I haven't actually profiled this, so maybe I'm wrong.
int max_threads = std::max(1, std::min(nev, admmpd::get_max_threads(options)));
+ if (options->log_level >= LOGLEVEL_DEBUG) {
+ max_threads = 1;
+ }
const auto & per_thread_function = [&per_embedded_vertex_detect,&max_threads,&nev]
(DetectThreadData *td, int thread_idx)
{
- int slice = std::max((int)std::round((nev+1)/double(max_threads)),1);
+ float slice_f = float(nev+1) / float(max_threads);
+ int slice = std::max((int)std::ceil(slice_f),1);
for (int i=0; i<slice; ++i)
{
int vi = i*max_threads + thread_idx;
- if (vi >= nev)
+
+ // Yeah okay I know this is dumb and I can just do a better job
+ // of calculating the slice. We can save thread optimization
+ // for the future, since this will be written different anyway.
+ if (vi >= nev) {
break;
+ }
per_embedded_vertex_detect(td,thread_idx,vi);
}
@@ -321,8 +384,12 @@ int EmbeddedMeshCollision::detect(
// Launch threads
std::vector<std::thread> pool;
per_thread_results.resize(max_threads, std::vector<Vector2i>());
- for (int i=0; i<max_threads; ++i)
+ for (int i=0; i<max_threads; ++i) {
+ per_thread_results[i].reserve(std::max(1,nev/max_threads));
+ }
+ for (int i=0; i<max_threads; ++i) {
pool.emplace_back(per_thread_function,&thread_data,i);
+ }
// Combine parallel results
vf_pairs.clear();
@@ -439,8 +506,9 @@ EmbeddedMeshCollision::detect_against_self(
x1->row(tet[1]),
x1->row(tet[2]),
x1->row(tet[3]));
- if (barys.minCoeff()<-1e-8 || barys.sum() > 1+1e-8)
+ if (barys.minCoeff()<-1e-8 || barys.sum() > 1+1e-8) {
throw std::runtime_error("EmbeddedMeshCollision: Bad tet barys");
+ }
const MatrixXd *rest_V0 = mesh->rest_prim_verts();
Vector3d rest_pt =
@@ -454,8 +522,9 @@ EmbeddedMeshCollision::detect_against_self(
if (rest_emb_sdf)
{
double dist = rest_emb_sdf->interpolate(0, rest_pt);
- if (dist > 0)
+ if (dist > 0) {
return ret; // nope
+ }
}
// Find triangle surface projection that doesn't
@@ -469,23 +538,28 @@ EmbeddedMeshCollision::detect_against_self(
skip_tri_inds);
mesh->emb_rest_tree()->traverse(nearest_tri);
- if (nearest_tri.output.prim<0)
+ if (nearest_tri.output.prim<0) {
throw std::runtime_error("EmbeddedMeshCollision: Failed to find triangle");
+ }
ret.first = true;
ret.second.p_idx = pt_idx;
ret.second.p_is_obs = false;
ret.second.q_idx = nearest_tri.output.prim;
ret.second.q_is_obs = false;
- ret.second.q_pt = nearest_tri.output.pt_on_tri;
// Compute barycoords of projection
RowVector3i f = mesh->facets()->row(nearest_tri.output.prim);
Vector3d v3[3] = { emb_V0->row(f[0]), emb_V0->row(f[1]), emb_V0->row(f[2]) };
ret.second.q_bary = geom::point_triangle_barys<double>(
nearest_tri.output.pt_on_tri, v3[0], v3[1], v3[2]);
- if (ret.second.q_bary.minCoeff()<-1e-8 || ret.second.q_bary.sum() > 1+1e-8)
+ if (ret.second.q_bary.minCoeff()<-1e-8 || ret.second.q_bary.sum() > 1+1e-8) {
throw std::runtime_error("EmbeddedMeshCollision: Bad triangle barys");
+ }
+
+ // q_pt is not used for self collisions, but we'll use it
+ // to define the tet constraint stencil.
+ ret.second.q_pt = pt_t1;
return ret;
}
@@ -581,7 +655,7 @@ void EmbeddedMeshCollision::linearize(
//int nx = x->rows();
d->reserve((int)d->size() + np);
trips->reserve((int)trips->size() + np*3*4);
- double eta = std::max(0.0,options->collision_thickness);
+ double eta = 0;//std::max(0.0,options->collision_thickness);
for (int i=0; i<np; ++i)
{
diff --git a/extern/softbody/src/admmpd_collision.h b/extern/softbody/src/admmpd_collision.h
index 25d9feb295b..3c9a0a329a5 100644
--- a/extern/softbody/src/admmpd_collision.h
+++ b/extern/softbody/src/admmpd_collision.h
@@ -23,11 +23,14 @@ struct VFCollisionPair {
class Collision {
public:
struct ObstacleData {
- bool has_obs() const { return F.rows()>0; }
- void clear();
- Eigen::MatrixXd V;
- Eigen::MatrixXi F;
- SDFType sdf;
+ int num_obs() const { return sdf.size(); }
+ bool compute_sdf(int idx);
+ std::vector<SDFType> sdf;
+ // Obstacle data stored in custom matrix type to interop with DiscreGrid
+ std::vector<Eigen::Matrix<double,Eigen::Dynamic,3,Eigen::RowMajor> > x0;
+ std::vector<Eigen::Matrix<double,Eigen::Dynamic,3,Eigen::RowMajor> > x1;
+ std::vector<Eigen::Matrix<unsigned int,Eigen::Dynamic,3,Eigen::RowMajor> > F;
+ std::vector<Eigen::AlignedBox<double,3> > box;
} obsdata;
virtual ~Collision() {}
@@ -56,15 +59,13 @@ public:
const admmpd::Mesh *mesh,
std::vector<std::set<int> > &g) = 0;
- // Set the soup of obstacles for this time step.
- // Returns true on success (SDF generation).
- // If err not nullptr, it's set with what caused the error.
+ // Updates the collision obstacles. If the
+ // obstacles are new or have moved, the SDF
+ // is recomputed on the next call to detect(...)
virtual bool set_obstacles(
- const float *v0,
- const float *v1,
- int nv,
- const unsigned int *faces,
- int nf,
+ std::vector<Eigen::MatrixXd> &v0,
+ std::vector<Eigen::MatrixXd> &v1,
+ std::vector<Eigen::MatrixXi> &F,
std::string *err=nullptr);
// Linearizes active collision pairs about x
diff --git a/extern/softbody/src/admmpd_linsolve.cpp b/extern/softbody/src/admmpd_linsolve.cpp
index bd06f5910a7..6313393d232 100644
--- a/extern/softbody/src/admmpd_linsolve.cpp
+++ b/extern/softbody/src/admmpd_linsolve.cpp
@@ -74,14 +74,17 @@ void LDLT::init_solve(
data->ls.Ptq = options->pk * P.transpose() * q;
}
- if (!data->ls.ldlt_A_PtP)
+ bool new_ptr = false;
+ if (!data->ls.ldlt_A_PtP) {
data->ls.ldlt_A_PtP = std::make_unique<Cholesky>();
+ new_ptr = true;
+ }
// Compute A + P'P and factorize:
// 1) A not computed
// 2) P has changed
// 3) factorization not set
- if ( data->ls.ldlt_A_PtP->info() != Eigen::Success ||
+ if ( new_ptr ||
data->ls.A_PtP.nonZeros()==0 ||
new_P)
{
diff --git a/extern/softbody/src/admmpd_log.cpp b/extern/softbody/src/admmpd_log.cpp
index 28c196928a6..c979cbd8ef5 100644
--- a/extern/softbody/src/admmpd_log.cpp
+++ b/extern/softbody/src/admmpd_log.cpp
@@ -56,6 +56,7 @@ std::string Logger::state_string(int state)
{
default: break;
case SOLVERSTATE_INIT: str="init"; break;
+ case SOLVERSTATE_MESHCREATE: str="mesh_init"; break;
case SOLVERSTATE_SOLVE: str="solve"; break;
case SOLVERSTATE_INIT_SOLVE: str="init_solve"; break;
case SOLVERSTATE_LOCAL_STEP: str="local_step"; break;
diff --git a/extern/softbody/src/admmpd_mesh.cpp b/extern/softbody/src/admmpd_mesh.cpp
index 4afa2f5c228..6e9884ca4c9 100644
--- a/extern/softbody/src/admmpd_mesh.cpp
+++ b/extern/softbody/src/admmpd_mesh.cpp
@@ -4,6 +4,7 @@
#include "admmpd_mesh.h"
#include "admmpd_geom.h"
#include "admmpd_timer.h"
+#include "admmpd_log.h"
#include <unordered_map>
#include <set>
#include <iostream>
@@ -62,6 +63,7 @@ static void gather_octree_tets(
} // end gather octree tets
bool EmbeddedMesh::create(
+ const Options *options,
const float *verts, // size nv*3
int nv,
const unsigned int *faces, // size nf*3
@@ -69,11 +71,20 @@ bool EmbeddedMesh::create(
const unsigned int *tets, // ignored
int nt)
{
+ admmpd::Logger log(options->log_level);
+ log.start_state(SOLVERSTATE_MESHCREATE);
+
P_updated = true;
- if (nv<=0 || verts == nullptr)
+ mesh_is_closed = false;
+
+ if (nv<=0 || verts == nullptr) {
+ log.stop_state(SOLVERSTATE_MESHCREATE);
return false;
- if (nf<=0 || faces == nullptr)
+ }
+ if (nf<=0 || faces == nullptr) {
+ log.stop_state(SOLVERSTATE_MESHCREATE);
return false;
+ }
(void)(tets);
(void)(nt);
@@ -153,8 +164,12 @@ bool EmbeddedMesh::create(
compute_sdf(&emb_V0, &emb_F, &emb_sdf);
emb_rest_facet_tree.init(emb_leaves);
- compute_lattice();
- compute_embedding();
+ if (!compute_lattice(options)) {
+ throw_err("create","Failed lattice generation");
+ }
+ if (!compute_embedding(options)) {
+ throw_err("create","Failed embedding calculation");
+ }
// Verify embedding is correct
for (int i=0; i<nv; ++i)
@@ -179,14 +194,23 @@ bool EmbeddedMesh::create(
if (emb_V0.rows()==0)
throw_err("create","Did not set verts");
+ log.stop_state(SOLVERSTATE_MESHCREATE);
+ if (options->log_level >= LOGLEVEL_DEBUG) {
+ printf("%s\n",log.to_string().c_str());
+ }
+
return true;
}
void EmbeddedMesh::compute_sdf(
const Eigen::MatrixXd *emb_v,
const Eigen::MatrixXi *emb_f,
- SDFType *sdf) const
+ SDFType *sdf)
{
+ if (emb_f->rows()==0) {
+ return;
+ }
+
Matrix<double,Dynamic,Dynamic,RowMajor> v_rm = *emb_v;
Matrix<unsigned int,Dynamic,Dynamic,RowMajor> f_rm = emb_f->cast<unsigned int>();
@@ -198,10 +222,16 @@ void EmbeddedMesh::compute_sdf(
domain.max() += 1e-3 * domain.diagonal().norm() * Eigen::Vector3d::Ones();
domain.min() -= 1e-3 * domain.diagonal().norm() * Eigen::Vector3d::Ones();
+ // Decide an SDF resolution. We want it to scale: high resolution
+ // for lower resolution meshes. This is because it becomes WAY too
+ // expensive to compute SDFs for high res meshes.
+ int nf = emb_f->rows();
+ unsigned int res = std::max(10, 20-(int)std::log(nf));
+ std::array<unsigned int, 3> resolution;
+ resolution[0] = res; resolution[1] = res; resolution[2] = res;
Discregrid::TriangleMesh tm(v_rm.data(), f_rm.data(), v_rm.rows(), f_rm.rows());
+ mesh_is_closed = tm.is_closed();
Discregrid::MeshDistance md(tm);
- std::array<unsigned int, 3> resolution;
- resolution[0] = 30; resolution[1] = 30; resolution[2] = 30;
*sdf = Discregrid::CubicLagrangeDiscreteGrid(domain, resolution);
auto func = Discregrid::DiscreteGrid::ContinuousFunction{};
std::vector<std::thread::id> thread_map;
@@ -212,7 +242,7 @@ void EmbeddedMesh::compute_sdf(
sdf->addFunction(func, &thread_map, false);
}
-bool EmbeddedMesh::compute_lattice()
+bool EmbeddedMesh::compute_lattice(const admmpd::Options *options)
{
// Create subset of faces
// if (emb_faces.size()==0) { return false; }
@@ -228,7 +258,7 @@ bool EmbeddedMesh::compute_lattice()
// Create an octree to generate the tets from
Octree<double,3> octree;
- octree.init(&emb_V0,&F,options.max_subdiv_levels);
+ octree.init(&emb_V0,&F,options->lattice_subdiv);
std::vector<Vector3d> lat_verts;
std::vector<RowVector4i> lat_tets;
@@ -258,9 +288,10 @@ bool EmbeddedMesh::compute_lattice()
return nlt>0;
}
-bool EmbeddedMesh::compute_embedding()
+bool EmbeddedMesh::compute_embedding(const admmpd::Options *options)
{
struct FindTetThreadData {
+ const Options *opt;
AABBTree<double,3> *tree;
EmbeddedMesh *emb_mesh; // thread sets vtx_to_tet and barys
MatrixXd *lat_V0;
@@ -269,6 +300,10 @@ bool EmbeddedMesh::compute_embedding()
VectorXi *emb_v_to_tet;
};
+ if (options->log_level >= LOGLEVEL_DEBUG) {
+ printf("Computing embedding for %d verts\n", (int)emb_V0.rows());
+ }
+
auto parallel_point_in_tet = [](
void *__restrict userdata,
const int i,
@@ -297,6 +332,15 @@ bool EmbeddedMesh::compute_embedding()
td->emb_v_to_tet->operator[](i) = tet_idx;
Vector4d b = geom::point_tet_barys<double>(pt,t[0],t[1],t[2],t[3]);
td->emb_barys->row(i) = b;
+
+ if (td->opt->log_level >= LOGLEVEL_DEBUG) {
+ printf("\tFound embedding for %d: %f %f %f %f\n", i, b[0],b[1],b[2],b[3]);
+ }
+ }
+ else {
+ if (td->opt->log_level >= LOGLEVEL_DEBUG) {
+ printf("\tDid NOT find embedding for %d!\n", i);
+ }
}
}; // end parallel find tet
@@ -309,14 +353,15 @@ bool EmbeddedMesh::compute_embedding()
emb_barys.resize(nv,4);
emb_barys.setZero();
emb_v_to_tet.resize(nv);
- emb_v_to_tet.array() = -1;
+ emb_v_to_tet.setOnes();
+ emb_v_to_tet *= -1;
int nt = lat_T.rows();
// BVH tree for finding point-in-tet and computing
// barycoords for each embedded vertex
std::vector<AlignedBox<double,3> > tet_aabbs;
tet_aabbs.resize(nt);
- Vector3d veta = Vector3d::Ones()*1e-12;
+ Vector3d veta = Vector3d::Ones()*1e-4;
for (int i=0; i<nt; ++i)
{
tet_aabbs[i].setEmpty();
@@ -332,6 +377,7 @@ bool EmbeddedMesh::compute_embedding()
tree.init(tet_aabbs);
FindTetThreadData thread_data = {
+ options,
&tree,
this,
&lat_V0,
@@ -341,6 +387,9 @@ bool EmbeddedMesh::compute_embedding()
};
TaskParallelSettings settings;
BLI_parallel_range_settings_defaults(&settings);
+ if (options->log_level >= LOGLEVEL_DEBUG) {
+ settings.use_threading = false;
+ }
BLI_task_parallel_range(0, nv, &thread_data, parallel_point_in_tet, &settings);
// Double check we set (valid) barycoords for every embedded vertex
@@ -497,6 +546,7 @@ bool EmbeddedMesh::linearize_pins(
}
bool TetMesh::create(
+ const Options *options,
const float *verts, // size nv*3
int nv,
const unsigned int *faces, // size nf*3 (surface faces)
@@ -505,6 +555,7 @@ bool TetMesh::create(
int nt) // must be > 0
{
P_updated = true;
+
if (nv<=0 || verts == nullptr)
return false;
if (nf<=0 || faces == nullptr)
@@ -664,6 +715,7 @@ bool TetMesh::linearize_pins(
}
bool TriangleMesh::create(
+ const Options *options,
const float *verts,
int nv,
const unsigned int *faces,
@@ -671,7 +723,9 @@ bool TriangleMesh::create(
const unsigned int *tets,
int nt)
{
+ (void)(options);
P_updated = true;
+
(void)(tets); (void)(nt);
if (nv<=0 || verts == nullptr)
return false;
diff --git a/extern/softbody/src/admmpd_mesh.h b/extern/softbody/src/admmpd_mesh.h
index 364355a8a06..01e0f644418 100644
--- a/extern/softbody/src/admmpd_mesh.h
+++ b/extern/softbody/src/admmpd_mesh.h
@@ -18,6 +18,7 @@ public:
// Copy buffers to internal data,
// calculates BVH/SDF, etc...
virtual bool create(
+ const Options *options,
const float *verts, // size nv*3
int nv,
const unsigned int *faces, // size nf*3
@@ -35,6 +36,8 @@ public:
virtual const Eigen::MatrixXd *rest_facet_verts() const = 0;
virtual const SDFType *rest_facet_sdf() const = 0;
+ virtual bool self_collision_allowed() const = 0;
+
// Maps primitive vertex to facet vertex. For standard tet meshes
// it's just one-to-one, but embedded meshes use bary weighting.
virtual Eigen::Vector3d get_mapped_facet_vertex(
@@ -85,32 +88,27 @@ protected:
std::unordered_map<int,double> emb_pin_k;
std::unordered_map<int,Eigen::Vector3d> emb_pin_pos;
admmpd::AABBTree<double,3> emb_rest_facet_tree;
+ bool mesh_is_closed;
SDFType emb_sdf;
mutable bool P_updated; // set to false on linearize_pins
- bool compute_embedding();
+ bool compute_embedding(const admmpd::Options *options);
// Computes the tet mesh on a subset of faces
- bool compute_lattice();
+ bool compute_lattice(const admmpd::Options *options);
+ // Sets mesh_is_closed
void compute_sdf(
const Eigen::MatrixXd *emb_v,
const Eigen::MatrixXi *emb_f,
- SDFType *sdf) const;
+ SDFType *sdf);
public:
int type() const { return MESHTYPE_EMBEDDED; }
- struct Options
- {
- int max_subdiv_levels;
- Options() :
- max_subdiv_levels(3)
- {}
- } options;
-
bool create(
+ const Options *options,
const float *verts, // size nv*3
int nv,
const unsigned int *faces, // size nf*3
@@ -127,6 +125,8 @@ public:
const SDFType *rest_facet_sdf() const { return &emb_sdf; }
const admmpd::AABBTree<double,3> *emb_rest_tree() const { return &emb_rest_facet_tree; }
+ bool self_collision_allowed() const { return mesh_is_closed; }
+
Eigen::Vector3d get_mapped_facet_vertex(
const Eigen::MatrixXd *prim_verts,
int facet_vertex_idx) const;
@@ -177,6 +177,7 @@ public:
int type() const { return MESHTYPE_TET; }
bool create(
+ const Options *options,
const float *verts, // size nv*3
int nv,
const unsigned int *faces, // size nf*3 (surface faces)
@@ -190,6 +191,9 @@ public:
const Eigen::MatrixXd *rest_prim_verts() const { return &V0; }
const SDFType *rest_facet_sdf() const { return &rest_sdf; }
+ // Not yet implemented
+ bool self_collision_allowed() const { return false; }
+
Eigen::Vector3d get_mapped_facet_vertex(
const Eigen::MatrixXd *prim_verts,
int facet_vertex_idx) const
@@ -237,6 +241,7 @@ public:
int type() const { return MESHTYPE_TRIANGLE; }
bool create(
+ const Options *options,
const float *verts, // size nv*3
int nv,
const unsigned int *faces, // size nf*3
@@ -250,6 +255,9 @@ public:
const Eigen::MatrixXd *rest_facet_verts() const { return &V0; }
const SDFType *rest_facet_sdf() const { return nullptr; }
+ // Not yet implemented
+ bool self_collision_allowed() const { return false; }
+
Eigen::Vector3d get_mapped_facet_vertex(
const Eigen::MatrixXd *prim_verts,
int facet_vertex_idx) const {
diff --git a/extern/softbody/src/admmpd_types.h b/extern/softbody/src/admmpd_types.h
index 85dbc1f77b3..5e512fb1dce 100644
--- a/extern/softbody/src/admmpd_types.h
+++ b/extern/softbody/src/admmpd_types.h
@@ -36,13 +36,14 @@ typedef Discregrid::CubicLagrangeDiscreteGrid SDFType;
#define COLLISIONMODE_NUM 2
#define SOLVERSTATE_INIT 0
-#define SOLVERSTATE_SOLVE 1
-#define SOLVERSTATE_INIT_SOLVE 2
-#define SOLVERSTATE_LOCAL_STEP 3
-#define SOLVERSTATE_GLOBAL_STEP 4
-#define SOLVERSTATE_COLLISION_UPDATE 5
-#define SOLVERSTATE_TEST_CONVERGED 6
-#define SOLVERSTATE_NUM 7
+#define SOLVERSTATE_MESHCREATE 1
+#define SOLVERSTATE_SOLVE 2
+#define SOLVERSTATE_INIT_SOLVE 3
+#define SOLVERSTATE_LOCAL_STEP 4
+#define SOLVERSTATE_GLOBAL_STEP 5
+#define SOLVERSTATE_COLLISION_UPDATE 6
+#define SOLVERSTATE_TEST_CONVERGED 7
+#define SOLVERSTATE_NUM 8
#define LOGLEVEL_NONE 0
#define LOGLEVEL_LOW 1
@@ -57,6 +58,7 @@ typedef Discregrid::CubicLagrangeDiscreteGrid SDFType;
struct Options {
double timestep_s;
+ int lattice_subdiv; // max subdiv levels for lattice gen
int log_level;
int linsolver;
int max_admm_iters;
@@ -74,12 +76,13 @@ struct Options {
double poisson; // Poisson ratio // TODO variable per-tet
double density_kgm3; // density of mesh
double floor; // floor location
- double collision_thickness;
+ //double collision_thickness;
bool self_collision; // process self collisions
Eigen::Vector2d strain_limit; // min=[-inf,1], max=[1,inf]
Eigen::Vector3d grav;
Options() :
timestep_s(1.0/24.0),
+ lattice_subdiv(3),
log_level(LOGLEVEL_NONE),
linsolver(LINSOLVER_PCG),
max_admm_iters(20),
@@ -97,7 +100,7 @@ struct Options {
poisson(0.399),
density_kgm3(1522),
floor(-std::numeric_limits<double>::max()),
- collision_thickness(1e-6),
+ //collision_thickness(1e-6),
self_collision(false),
strain_limit(0,100),
grav(0,0,-9.8)
diff --git a/intern/clog/CLG_log.h b/intern/clog/CLG_log.h
index 7418623755c..a2841c5c8b3 100644
--- a/intern/clog/CLG_log.h
+++ b/intern/clog/CLG_log.h
@@ -132,13 +132,14 @@ void CLG_logf(CLG_LogType *lg,
const char *format,
...) _CLOG_ATTR_NONNULL(1, 3, 4, 5) _CLOG_ATTR_PRINTF_FORMAT(5, 6);
-/* Main initializer and distructor (per session, not logger). */
+/* Main initializer and destructor (per session, not logger). */
void CLG_init(void);
void CLG_exit(void);
void CLG_output_set(void *file_handle);
void CLG_output_use_basename_set(int value);
void CLG_output_use_timestamp_set(int value);
+void CLG_error_fn_set(void (*error_fn)(void *file_handle));
void CLG_fatal_fn_set(void (*fatal_fn)(void *file_handle));
void CLG_backtrace_fn_set(void (*fatal_fn)(void *file_handle));
diff --git a/intern/clog/clog.c b/intern/clog/clog.c
index d384b9a89e6..84b850f5042 100644
--- a/intern/clog/clog.c
+++ b/intern/clog/clog.c
@@ -98,6 +98,7 @@ typedef struct CLogContext {
} default_type;
struct {
+ void (*error_fn)(void *file_handle);
void (*fatal_fn)(void *file_handle);
void (*backtrace_fn)(void *file_handle);
} callbacks;
@@ -352,6 +353,13 @@ static CLG_LogType *clg_ctx_type_register(CLogContext *ctx, const char *identifi
return ty;
}
+static void clg_ctx_error_action(CLogContext *ctx)
+{
+ if (ctx->callbacks.error_fn != NULL) {
+ ctx->callbacks.error_fn(ctx->output_file);
+ }
+}
+
static void clg_ctx_fatal_action(CLogContext *ctx)
{
if (ctx->callbacks.fatal_fn != NULL) {
@@ -522,6 +530,10 @@ void CLG_logf(CLG_LogType *lg,
clg_ctx_backtrace(lg->ctx);
}
+ if (severity == CLG_SEVERITY_ERROR) {
+ clg_ctx_error_action(lg->ctx);
+ }
+
if (severity == CLG_SEVERITY_FATAL) {
clg_ctx_fatal_action(lg->ctx);
}
@@ -555,6 +567,12 @@ static void CLG_ctx_output_use_timestamp_set(CLogContext *ctx, int value)
}
}
+/** Action on error severity. */
+static void CLT_ctx_error_fn_set(CLogContext *ctx, void (*error_fn)(void *file_handle))
+{
+ ctx->callbacks.error_fn = error_fn;
+}
+
/** Action on fatal severity. */
static void CLG_ctx_fatal_fn_set(CLogContext *ctx, void (*fatal_fn)(void *file_handle))
{
@@ -674,6 +692,11 @@ void CLG_output_use_timestamp_set(int value)
CLG_ctx_output_use_timestamp_set(g_ctx, value);
}
+void CLG_error_fn_set(void (*error_fn)(void *file_handle))
+{
+ CLT_ctx_error_fn_set(g_ctx, error_fn);
+}
+
void CLG_fatal_fn_set(void (*fatal_fn)(void *file_handle))
{
CLG_ctx_fatal_fn_set(g_ctx, fatal_fn);
diff --git a/intern/cycles/blender/blender_mesh.cpp b/intern/cycles/blender/blender_mesh.cpp
index f4354d5166e..e40e1f5f001 100644
--- a/intern/cycles/blender/blender_mesh.cpp
+++ b/intern/cycles/blender/blender_mesh.cpp
@@ -923,48 +923,34 @@ static void create_subd_mesh(Scene *scene,
/* Sync */
-static BL::MeshSequenceCacheModifier object_mesh_cache_find(BL::Object &b_ob,
- BL::Scene /*b_scene*/)
+static BL::MeshSequenceCacheModifier object_mesh_cache_find(BL::Object &b_ob)
{
- BL::Object::modifiers_iterator b_mod;
+ if (b_ob.modifiers.length() > 0) {
+ BL::Modifier b_mod = b_ob.modifiers[b_ob.modifiers.length() - 1];
- for (b_ob.modifiers.begin(b_mod); b_mod != b_ob.modifiers.end(); ++b_mod) {
- if (!b_mod->is_a(&RNA_MeshSequenceCacheModifier)) {
- continue;
- }
-
- BL::MeshSequenceCacheModifier mesh_cache = BL::MeshSequenceCacheModifier(*b_mod);
+ if (b_mod.type() == BL::Modifier::type_MESH_SEQUENCE_CACHE) {
+ BL::MeshSequenceCacheModifier mesh_cache = BL::MeshSequenceCacheModifier(b_mod);
- if (MeshSequenceCacheModifier_has_velocity_get(&mesh_cache.ptr)) {
- return mesh_cache;
+ if (MeshSequenceCacheModifier_has_velocity_get(&mesh_cache.ptr)) {
+ return mesh_cache;
+ }
}
}
return BL::MeshSequenceCacheModifier(PointerRNA_NULL);
}
-static void sync_mesh_cached_velocities(BL::Object &b_ob,
- BL::Scene b_scene,
- Scene *scene,
- Mesh *mesh)
+static void sync_mesh_cached_velocities(BL::Object &b_ob, Scene *scene, Mesh *mesh)
{
if (scene->need_motion() == Scene::MOTION_NONE)
return;
- BL::MeshSequenceCacheModifier b_mesh_cache = object_mesh_cache_find(b_ob, b_scene);
+ BL::MeshSequenceCacheModifier b_mesh_cache = object_mesh_cache_find(b_ob);
if (!b_mesh_cache) {
return;
}
- /* Find or add attribute */
- float3 *P = &mesh->verts[0];
- Attribute *attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
-
- if (!attr_mP) {
- attr_mP = mesh->attributes.add(ATTR_STD_MOTION_VERTEX_POSITION);
- }
-
if (!MeshSequenceCacheModifier_read_velocity_get(&b_mesh_cache.ptr)) {
return;
}
@@ -975,6 +961,14 @@ static void sync_mesh_cached_velocities(BL::Object &b_ob,
return;
}
+ /* Find or add attribute */
+ float3 *P = &mesh->verts[0];
+ Attribute *attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
+
+ if (!attr_mP) {
+ attr_mP = mesh->attributes.add(ATTR_STD_MOTION_VERTEX_POSITION);
+ }
+
/* Only export previous and next frame, we don't have any in between data. */
float motion_times[2] = {-1.0f, 1.0f};
for (int step = 0; step < 2; step++) {
@@ -1071,7 +1065,7 @@ void BlenderSync::sync_mesh(BL::Depsgraph b_depsgraph,
}
/* cached velocities (e.g. from alembic archive) */
- sync_mesh_cached_velocities(b_ob, b_depsgraph.scene(), scene, mesh);
+ sync_mesh_cached_velocities(b_ob, scene, mesh);
/* mesh fluid motion mantaflow */
sync_mesh_fluid_motion(b_ob, scene, mesh);
@@ -1095,7 +1089,7 @@ void BlenderSync::sync_mesh_motion(BL::Depsgraph b_depsgraph,
}
/* Cached motion blur already exported. */
- BL::MeshSequenceCacheModifier mesh_cache = object_mesh_cache_find(b_ob, b_scene);
+ BL::MeshSequenceCacheModifier mesh_cache = object_mesh_cache_find(b_ob);
if (mesh_cache) {
return;
}
diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp
index fb704b2a24a..bf9fc784d79 100644
--- a/intern/cycles/blender/blender_session.cpp
+++ b/intern/cycles/blender/blender_session.cpp
@@ -363,7 +363,8 @@ void BlenderSession::do_write_update_render_tile(RenderTile &rtile,
PassType pass_type = BlenderSync::get_pass_type(b_pass);
int components = b_pass.channels();
- rtile.buffers->set_pass_rect(pass_type, components, (float *)b_pass.rect());
+ rtile.buffers->set_pass_rect(
+ pass_type, components, (float *)b_pass.rect(), rtile.num_samples);
}
end_render_result(b_engine, b_rr, false, false, false);
diff --git a/intern/cycles/device/CMakeLists.txt b/intern/cycles/device/CMakeLists.txt
index ca366722eb7..466872e0557 100644
--- a/intern/cycles/device/CMakeLists.txt
+++ b/intern/cycles/device/CMakeLists.txt
@@ -67,6 +67,7 @@ set(LIB
cycles_render
cycles_kernel
cycles_util
+ ${BLENDER_GL_LIBRARIES}
)
if(WITH_CUDA_DYNLOAD)
diff --git a/intern/cycles/device/opencl/device_opencl_impl.cpp b/intern/cycles/device/opencl/device_opencl_impl.cpp
index e851749949d..f0683d12f1f 100644
--- a/intern/cycles/device/opencl/device_opencl_impl.cpp
+++ b/intern/cycles/device/opencl/device_opencl_impl.cpp
@@ -864,6 +864,11 @@ void OpenCLDevice::load_preview_kernels()
bool OpenCLDevice::wait_for_availability(const DeviceRequestedFeatures &requested_features)
{
+ if (requested_features.use_baking) {
+ /* For baking, kernels have already been loaded in load_required_kernels(). */
+ return true;
+ }
+
if (background) {
load_kernel_task_pool.wait_work();
use_preview_kernels = false;
@@ -1933,13 +1938,12 @@ void OpenCLDevice::bake(DeviceTask &task, RenderTile &rtile)
kernel_set_args(kernel, start_arg_index, sample);
enqueue_kernel(kernel, d_w, d_h);
+ clFinish(cqCommandQueue);
rtile.sample = sample + 1;
task.update_progress(&rtile, rtile.w * rtile.h);
}
-
- clFinish(cqCommandQueue);
}
static bool kernel_build_opencl_2(cl_device_id cdDevice)
diff --git a/intern/cycles/kernel/kernel_film.h b/intern/cycles/kernel/kernel_film.h
index 8344f4b4f47..17b69b6198b 100644
--- a/intern/cycles/kernel/kernel_film.h
+++ b/intern/cycles/kernel/kernel_film.h
@@ -47,7 +47,7 @@ ccl_device float4 film_get_pass_result(KernelGlobals *kg,
if (kernel_data.film.use_display_exposure) {
float exposure = kernel_data.film.exposure;
- pass_result *= make_float4(exposure, exposure, exposure, alpha);
+ pass_result *= make_float4(exposure, exposure, exposure, 1.0f);
}
}
else if (display_pass_components == 1) {
diff --git a/intern/cycles/kernel/kernels/opencl/kernel_bake.cl b/intern/cycles/kernel/kernels/opencl/kernel_bake.cl
index 041312b53cb..7b81e387467 100644
--- a/intern/cycles/kernel/kernels/opencl/kernel_bake.cl
+++ b/intern/cycles/kernel/kernels/opencl/kernel_bake.cl
@@ -12,12 +12,11 @@
__kernel void kernel_ocl_bake(
ccl_constant KernelData *data,
- ccl_global uint4 *input,
- ccl_global float4 *output,
+ ccl_global float *buffer,
KERNEL_BUFFER_PARAMS,
- int type, int filter, int sx, int sw, int offset, int sample)
+ int sx, int sy, int sw, int sh, int offset, int stride, int sample)
{
KernelGlobals kglobals, *kg = &kglobals;
@@ -27,12 +26,11 @@ __kernel void kernel_ocl_bake(
kernel_set_buffer_info(kg);
int x = sx + ccl_global_id(0);
+ int y = sy + ccl_global_id(1);
- if(x < sx + sw) {
-#ifdef __NO_BAKING__
- output[x] = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
-#else
- kernel_bake_evaluate(kg, input, output, (ShaderEvalType)type, filter, x, offset, sample);
+ if(x < sx + sw && y < sy + sh) {
+#ifndef __NO_BAKING__
+ kernel_bake_evaluate(kg, buffer, sample, x, y, offset, stride);
#endif
}
}
diff --git a/intern/cycles/kernel/osl/osl_globals.h b/intern/cycles/kernel/osl/osl_globals.h
index c06c9abd4c1..caca3c28c8d 100644
--- a/intern/cycles/kernel/osl/osl_globals.h
+++ b/intern/cycles/kernel/osl/osl_globals.h
@@ -90,6 +90,7 @@ struct OSLTraceData {
ShaderData sd;
bool setup;
bool init;
+ bool hit;
};
/* thread key for thread specific data lookup */
diff --git a/intern/cycles/kernel/osl/osl_services.cpp b/intern/cycles/kernel/osl/osl_services.cpp
index 5292b5f8055..aee1e3a244e 100644
--- a/intern/cycles/kernel/osl/osl_services.cpp
+++ b/intern/cycles/kernel/osl/osl_services.cpp
@@ -1481,6 +1481,7 @@ bool OSLRenderServices::trace(TraceOpt &options,
tracedata->ray = ray;
tracedata->setup = false;
tracedata->init = true;
+ tracedata->hit = false;
tracedata->sd.osl_globals = sd->osl_globals;
KernelGlobals *kg = sd->osl_globals;
@@ -1492,7 +1493,8 @@ bool OSLRenderServices::trace(TraceOpt &options,
/* Raytrace, leaving out shadow opaque to avoid early exit. */
uint visibility = PATH_RAY_ALL_VISIBILITY - PATH_RAY_SHADOW_OPAQUE;
- return scene_intersect(kg, &ray, visibility, &tracedata->isect);
+ tracedata->hit = scene_intersect(kg, &ray, visibility, &tracedata->isect);
+ return tracedata->hit;
}
bool OSLRenderServices::getmessage(OSL::ShaderGlobals *sg,
@@ -1506,9 +1508,9 @@ bool OSLRenderServices::getmessage(OSL::ShaderGlobals *sg,
if (source == u_trace && tracedata->init) {
if (name == u_hit) {
- return set_attribute_int((tracedata->isect.prim != PRIM_NONE), type, derivatives, val);
+ return set_attribute_int(tracedata->hit, type, derivatives, val);
}
- else if (tracedata->isect.prim != PRIM_NONE) {
+ else if (tracedata->hit) {
if (name == u_hitdist) {
float f[3] = {tracedata->isect.t, 0.0f, 0.0f};
return set_attribute_float(f, type, derivatives, val);
diff --git a/intern/cycles/render/buffers.cpp b/intern/cycles/render/buffers.cpp
index b26366af852..3607300cee6 100644
--- a/intern/cycles/render/buffers.cpp
+++ b/intern/cycles/render/buffers.cpp
@@ -459,7 +459,7 @@ bool RenderBuffers::get_pass_rect(
return false;
}
-bool RenderBuffers::set_pass_rect(PassType type, int components, float *pixels)
+bool RenderBuffers::set_pass_rect(PassType type, int components, float *pixels, int samples)
{
if (buffer.data() == NULL) {
return false;
@@ -482,8 +482,17 @@ bool RenderBuffers::set_pass_rect(PassType type, int components, float *pixels)
assert(pass.components == components);
for (int i = 0; i < size; i++, out += pass_stride, pixels += components) {
- for (int j = 0; j < components; j++) {
- out[j] = pixels[j];
+ if (pass.filter) {
+ /* Scale by the number of samples, inverse of what we do in get_pass_rect.
+ * A better solution would be to remove the need for set_pass_rect entirely,
+ * and change baking to bake multiple objects in a tile at once. */
+ for (int j = 0; j < components; j++) {
+ out[j] = pixels[j] * samples;
+ }
+ }
+ else {
+ /* For non-filtered passes just straight copy, these may contain non-float data. */
+ memcpy(out, pixels, sizeof(float) * components);
}
}
diff --git a/intern/cycles/render/buffers.h b/intern/cycles/render/buffers.h
index 06b6094e6c9..425400a2c08 100644
--- a/intern/cycles/render/buffers.h
+++ b/intern/cycles/render/buffers.h
@@ -92,7 +92,7 @@ class RenderBuffers {
const string &name, float exposure, int sample, int components, float *pixels);
bool get_denoising_pass_rect(
int offset, float exposure, int sample, int components, float *pixels);
- bool set_pass_rect(PassType type, int components, float *pixels);
+ bool set_pass_rect(PassType type, int components, float *pixels, int samples);
};
/* Display Buffer
diff --git a/intern/cycles/render/film.cpp b/intern/cycles/render/film.cpp
index 7072fff4892..2da28222a7f 100644
--- a/intern/cycles/render/film.cpp
+++ b/intern/cycles/render/film.cpp
@@ -253,6 +253,8 @@ void Pass::add(PassType type, vector<Pass> &passes, const char *name)
case PASS_BAKE_PRIMITIVE:
case PASS_BAKE_DIFFERENTIAL:
pass.components = 4;
+ pass.exposure = false;
+ pass.filter = false;
break;
default:
assert(false);
diff --git a/intern/ghost/intern/GHOST_WindowCocoa.mm b/intern/ghost/intern/GHOST_WindowCocoa.mm
index 62b0e48a7c1..f8e2f96d111 100644
--- a/intern/ghost/intern/GHOST_WindowCocoa.mm
+++ b/intern/ghost/intern/GHOST_WindowCocoa.mm
@@ -415,7 +415,7 @@ GHOST_WindowCocoa::GHOST_WindowCocoa(GHOST_SystemCocoa *systemCocoa,
[parentWindow->getCocoaWindow() addChildWindow:m_window ordered:NSWindowAbove];
[m_window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary];
}
- else if (state != GHOST_kWindowStateFullScreen) {
+ else {
[m_window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
}
diff --git a/intern/guardedalloc/MEM_guardedalloc.h b/intern/guardedalloc/MEM_guardedalloc.h
index 9c62b2396f6..c05bda030ad 100644
--- a/intern/guardedalloc/MEM_guardedalloc.h
+++ b/intern/guardedalloc/MEM_guardedalloc.h
@@ -215,6 +215,11 @@ extern const char *(*MEM_name_ptr)(void *vmemh);
* about memory leaks will be printed on exit. */
void MEM_init_memleak_detection(void);
+/** When this has been called and memory leaks have been detected, the process will have an exit
+ * code that indicates failure. This can be used for when checking for memory leaks with automated
+ * tests. */
+void MEM_enable_fail_on_memleak(void);
+
/* Switch allocator to slower but fully guarded mode. */
void MEM_use_guarded_allocator(void);
diff --git a/intern/guardedalloc/intern/leak_detector.cc b/intern/guardedalloc/intern/leak_detector.cc
index d7b6f749742..0ecf2ed8ba7 100644
--- a/intern/guardedalloc/intern/leak_detector.cc
+++ b/intern/guardedalloc/intern/leak_detector.cc
@@ -18,6 +18,8 @@
* \ingroup MEM
*/
+#include <cstdlib>
+
#include "MEM_guardedalloc.h"
#include "mallocn_intern.h"
@@ -28,6 +30,9 @@ char free_after_leak_detection_message[] =
"error, use the 'construct on first use' idiom.";
namespace {
+
+bool fail_on_memleak = false;
+
class MemLeakPrinter {
public:
~MemLeakPrinter()
@@ -42,6 +47,15 @@ class MemLeakPrinter {
leaked_blocks,
(double)mem_in_use / 1024 / 1024);
MEM_printmemlist();
+
+ if (fail_on_memleak) {
+ /* There are many other ways to change the exit code to failure here:
+ * - Make the destructor noexcept(false) and throw an exception.
+ * - Call exit(EXIT_FAILURE).
+ * - Call terminate().
+ */
+ abort();
+ }
}
};
} // namespace
@@ -59,3 +73,8 @@ void MEM_init_memleak_detection(void)
*/
static MemLeakPrinter printer;
}
+
+void MEM_enable_fail_on_memleak(void)
+{
+ fail_on_memleak = true;
+}
diff --git a/intern/mantaflow/intern/manta_fluid_API.cpp b/intern/mantaflow/intern/manta_fluid_API.cpp
index 530dbd49b7c..7f96a315a8e 100644
--- a/intern/mantaflow/intern/manta_fluid_API.cpp
+++ b/intern/mantaflow/intern/manta_fluid_API.cpp
@@ -456,7 +456,7 @@ int manta_smoke_ensure_fire(MANTA *smoke, struct FluidModifierData *fmd)
if (!smoke || !fmd)
return 0;
- int result = smoke->initFire(fmd);
+ bool result = smoke->initFire(fmd);
if (smoke->usingNoise()) {
result &= smoke->initFireHigh(fmd);
}
@@ -468,7 +468,7 @@ int manta_smoke_ensure_colors(MANTA *smoke, struct FluidModifierData *fmd)
if (!smoke || !fmd)
return 0;
- int result = smoke->initColors(fmd);
+ bool result = smoke->initColors(fmd);
if (smoke->usingNoise()) {
result &= smoke->initColorsHigh(fmd);
}
diff --git a/intern/numaapi/source/build_config.h b/intern/numaapi/source/build_config.h
index c6392532914..8e351f1c718 100644
--- a/intern/numaapi/source/build_config.h
+++ b/intern/numaapi/source/build_config.h
@@ -96,7 +96,7 @@
#elif defined(__QNXNTO__)
# define OS_QNX 1
#elif defined(__asmjs__) || defined(__wasm__)
-# define OS_ASMJS
+# define OS_ASMJS 1
#else
# error Please add support for your platform in build_config.h
#endif
diff --git a/intern/softbody/admmpd_api.cpp b/intern/softbody/admmpd_api.cpp
index 85d4718bf7f..a9699eb936b 100644
--- a/intern/softbody/admmpd_api.cpp
+++ b/intern/softbody/admmpd_api.cpp
@@ -31,32 +31,18 @@
#endif
#include "DNA_mesh_types.h" // Mesh
#include "DNA_meshdata_types.h" // MVert
+#include "DNA_modifier_types.h" // CollisionModifierData
#include "DNA_object_force_types.h" // Enums
#include "BKE_mesh.h" // BKE_mesh_free
#include "BKE_softbody.h" // BodyPoint
#include "BKE_deform.h" // BKE_defvert_find_index
+#include "BKE_modifier.h" // BKE_modifiers_findby_type
#include "MEM_guardedalloc.h"
#include <iostream>
#include <memory>
#include <algorithm>
-// Collision obstacles are cached until
-// solve(...) is called. If we are substepping,
-// the obstacle is interpolated from start to end.
-struct CollisionObstacle
-{
- Eigen::VectorXf x0, x1;
- std::vector<unsigned int> F;
- bool needs_sdf_recompute;
- void reset() {
- x0 = Eigen::VectorXf();
- x1 = Eigen::VectorXf();
- F.clear();
- needs_sdf_recompute = true;
- }
-};
-
struct ADMMPDInternalData
{
// Created in admmpd_update_mesh
@@ -66,7 +52,8 @@ struct ADMMPDInternalData
std::shared_ptr<admmpd::Options> options;
std::shared_ptr<admmpd::SolverData> data;
// Created in set_obstacles
- CollisionObstacle obs;
+ std::vector<Eigen::MatrixXd> obs_x0, obs_x1;
+ std::vector<Eigen::MatrixXi> obs_F;
};
@@ -104,6 +91,7 @@ static inline void options_from_object(
op->linsolver = std::max(0, std::min(LINSOLVER_NUM-1, sb->admmpd_linsolver));
op->strain_limit[0] = std::min(1.f, sb->admmpd_strainlimit_min);
op->strain_limit[1] = std::max(1.f, sb->admmpd_strainlimit_max);
+ op->lattice_subdiv = std::max(1,sb->admmpd_embed_res);
if (!skip_require_reset)
{
@@ -186,10 +174,8 @@ static inline int admmpd_init_with_lattice(ADMMPDInterfaceData *iface, Object *o
std::vector<unsigned int> f;
vecs_from_object(ob,vertexCos,v,f);
iface->idata->mesh = std::make_shared<admmpd::EmbeddedMesh>();
-
- admmpd::EmbeddedMesh* emb_msh = static_cast<admmpd::EmbeddedMesh*>(iface->idata->mesh.get());
- emb_msh->options.max_subdiv_levels = ob->soft->admmpd_embed_res;
bool success = iface->idata->mesh->create(
+ iface->idata->options.get(),
v.data(),
v.size()/3,
f.data(),
@@ -197,7 +183,7 @@ static inline int admmpd_init_with_lattice(ADMMPDInterfaceData *iface, Object *o
nullptr,
0);
- if (!success) {
+ if (!success) { // soft unknown fail
strcpy_error(iface, "EmbeddedMesh failed on creation");
return 0;
}
@@ -214,6 +200,7 @@ static inline int admmpd_init_as_cloth(ADMMPDInterfaceData *iface, Object *ob, f
iface->idata->mesh = std::make_shared<admmpd::TriangleMesh>();
bool success = iface->idata->mesh->create(
+ iface->idata->options.get(),
v.data(),
v.size()/3,
f.data(),
@@ -230,6 +217,53 @@ static inline int admmpd_init_as_cloth(ADMMPDInterfaceData *iface, Object *ob, f
return 1;
}
+void admmpd_compute_lattice(
+ int subdiv,
+ float *in_verts, int in_nv,
+ unsigned int *in_faces, int in_nf,
+ float **out_verts, int *out_nv,
+ unsigned int **out_tets, int *out_nt)
+{
+
+ admmpd::EmbeddedMesh emesh;
+ admmpd::Options opt;
+ opt.lattice_subdiv = subdiv;
+ bool success = emesh.create(
+ &opt,
+ in_verts, in_nv,
+ in_faces, in_nf,
+ nullptr,
+ 0);
+
+ if (!success) {
+ return;
+ }
+
+ const Eigen::MatrixXd &vt = *emesh.rest_prim_verts();
+ const Eigen::MatrixXi &t = *emesh.prims();
+ if (vt.rows()==0 || t.rows()==0) {
+ return;
+ }
+
+ *out_nv = vt.rows();
+ *out_verts = (float*)MEM_callocN(sizeof(float)*3*(vt.rows()), "ADMMPD_lattice_verts");
+ *out_nt = t.rows();
+ *out_tets = (unsigned int*)MEM_callocN(sizeof(unsigned int)*4*(t.rows()), "ADMMPD_lattice_tets");
+
+ for (int i=0; i<vt.rows(); ++i) {
+ (*out_verts)[i*3+0] = vt(i,0);
+ (*out_verts)[i*3+1] = vt(i,1);
+ (*out_verts)[i*3+2] = vt(i,2);
+ }
+
+ for (int i=0; i<t.rows(); ++i) {
+ (*out_tets)[i*4+0] = t(i,0);
+ (*out_tets)[i*4+1] = t(i,1);
+ (*out_tets)[i*4+2] = t(i,2);
+ (*out_tets)[i*4+3] = t(i,3);
+ }
+}
+
int admmpd_mesh_needs_update(ADMMPDInterfaceData *iface, Object *ob)
{
if (!iface) { return 0; }
@@ -244,6 +278,7 @@ int admmpd_mesh_needs_update(ADMMPDInterfaceData *iface, Object *ob)
// Mode or topology change?
int mode = ob->soft->admmpd_mesh_mode;
int mesh_type = iface->idata->mesh->type();
+
if (mode != mesh_type) { return 1; }
if (!iface->idata->mesh->rest_facet_verts()) { return 1; }
int nx = iface->idata->mesh->rest_facet_verts()->rows();
@@ -258,9 +293,15 @@ int admmpd_update_mesh(ADMMPDInterfaceData *iface, Object *ob, float (*vertexCos
if (!ob) { return 0; }
if (!ob->soft) { return 0; }
- if (!iface->idata)
+ if (!iface->idata) {
iface->idata = (ADMMPDInternalData*)MEM_callocN(sizeof(ADMMPDInternalData), "ADMMPD_idata");
+ }
+ if (!iface->idata->options) {
+ iface->idata->options = std::make_shared<admmpd::Options>();
+ }
+
+ options_from_object(iface,NULL,ob,iface->idata->options.get(),false);
int mode = ob->soft->admmpd_mesh_mode;
iface->idata->mesh.reset();
@@ -362,9 +403,13 @@ int admmpd_update_solver(ADMMPDInterfaceData *iface, Scene *sc, Object *ob, flo
if (!iface->idata->mesh) { return 0; }
// Reset options and data
- iface->idata->options = std::make_shared<admmpd::Options>();
+ if (!iface->idata->options) {
+ iface->idata->options = std::make_shared<admmpd::Options>();
+ }
iface->idata->data = std::make_shared<admmpd::SolverData>();
- iface->idata->obs.reset();
+ iface->idata->obs_x0.clear();
+ iface->idata->obs_x1.clear();
+ iface->idata->obs_F.clear();
admmpd::Options *op = iface->idata->options.get();
options_from_object(iface,sc,ob,op,false);
@@ -417,11 +462,18 @@ void admmpd_copy_to_object(ADMMPDInterfaceData *iface, Object *ob, float (*verte
if (ob && ob->soft) {
SoftBody *sb = ob->soft;
+
if (!sb->bpoint) {
if (!ob->soft->bpoint) {
sb->bpoint = (BodyPoint*)MEM_callocN(nx*sizeof(BodyPoint), "ADMMPD_bpoint");
}
+ sb->totpoint = nx;
+ sb->totspring = 0;
+ }
+ if (sb->totpoint != nx && sb->totpoint>0) {
+ MEM_freeN(sb->bpoint);
+ sb->bpoint = (BodyPoint*)MEM_callocN(nx*sizeof(BodyPoint), "ADMMPD_bpoint");
sb->totpoint = nx;
sb->totspring = 0;
}
@@ -454,66 +506,6 @@ void admmpd_copy_to_object(ADMMPDInterfaceData *iface, Object *ob, float (*verte
}
}
-void admmpd_update_obstacles(
- ADMMPDInterfaceData *iface,
- float *in_verts_0,
- float *in_verts_1,
- int nv,
- unsigned int *in_faces,
- int nf)
-{
- if (iface==NULL || in_verts_0==NULL || in_verts_1==NULL || in_faces==NULL) {
- return;
- }
- if (!iface->idata) { return; }
-
- if (nf==0 || nv==0) { return; }
- int nv3 = nv*3;
- int nf3 = nf*3;
- iface->idata->obs.needs_sdf_recompute = false;
-
- if (iface->idata->obs.x0.size()!=nv3) {
- iface->idata->obs.x0.resize(nv3);
- iface->idata->obs.needs_sdf_recompute = true;
- }
-
- if (iface->idata->obs.x1.size()!=nv3) {
- iface->idata->obs.x1.resize(nv3);
- iface->idata->obs.needs_sdf_recompute = true;
- }
-
- if (iface->idata->obs.F.size()!=nf3) {
- iface->idata->obs.F.resize(nf3);
- iface->idata->obs.needs_sdf_recompute = true;
- }
-
- for (int i=0; i<nv3; ++i) {
-
- // Change in x?
- if (!iface->idata->obs.needs_sdf_recompute) {
- if (std::abs(iface->idata->obs.x0[i]-in_verts_0[i])>1e-8 ||
- std::abs(iface->idata->obs.x1[i]-in_verts_1[i])>1e-8 ) {
- iface->idata->obs.needs_sdf_recompute = true;
- }
- }
-
- iface->idata->obs.x0[i] = in_verts_0[i];
- iface->idata->obs.x1[i] = in_verts_1[i];
- }
- for (int i=0; i<nf3; ++i) {
-
- // Change in f?
- if (!iface->idata->obs.needs_sdf_recompute) {
- if (iface->idata->obs.F[i] != in_faces[i]) {
- iface->idata->obs.needs_sdf_recompute = true;
- }
- }
-
- iface->idata->obs.F[i] = in_faces[i];
- }
-
-}
-
static inline void admmpd_update_goals(ADMMPDInterfaceData *iface, Object *ob, float (*vertexCos)[3])
{
if (!iface) { return; }
@@ -603,11 +595,18 @@ int admmpd_solve(ADMMPDInterfaceData *iface, Object *ob, float (*vertexCos)[3])
return 0;
}
- if (!iface->idata || !iface->idata->options || !iface->idata->data) {
+ if (!iface->idata || !iface->idata->options ||
+ !iface->idata->data || !iface->idata->mesh) {
strcpy_error(iface, "NULL internal data");
return 0;
}
+ std::string meshname(ob->id.name);
+
+ // Set to true if certain conditions should
+ // throw a warning flag.
+ bool return_warning = false;
+
// Change only options that do not cause a reset of the solver.
bool skip_solver_reset = true;
options_from_object(
@@ -617,6 +616,19 @@ int admmpd_solve(ADMMPDInterfaceData *iface, Object *ob, float (*vertexCos)[3])
iface->idata->options.get(),
skip_solver_reset);
+ // Disable self collision flag if the mesh does not support it.
+ if (iface->idata->options->self_collision &&
+ !iface->idata->mesh->self_collision_allowed()) {
+ // Special message if embedded, in which the mesh is not closed.
+ std::string err = "Cannot do self collisions on object "+meshname+" for selected mesh type";
+ if (iface->idata->mesh->type() == MESHTYPE_EMBEDDED) {
+ err = "Cannot do self collisions on object "+meshname+", mesh is not closed.";
+ }
+ strcpy_error(iface, err.c_str());
+ iface->idata->options->self_collision = false;
+ return_warning = true;
+ }
+
// Goals and self collision group can change
// between time steps. If the goal indices/weights change,
// it will trigger a refactorization in the solver.
@@ -625,12 +637,11 @@ int admmpd_solve(ADMMPDInterfaceData *iface, Object *ob, float (*vertexCos)[3])
// Obstacle collisions not yet implemented
// for cloth or tet mesh.
- bool had_set_obstacle_error = false;
if ((ob->soft->admmpd_mesh_mode == MESHTYPE_TET ||
ob->soft->admmpd_mesh_mode == MESHTYPE_TRIANGLE) &&
- iface->idata->obs.x0.size()>0)
+ iface->idata->obs_x0.size()>0)
{
- had_set_obstacle_error = true;
+ return_warning = true;
strcpy_error(iface, "Obstacle collision not yet available for selected mesh mode.");
}
@@ -640,47 +651,40 @@ int admmpd_solve(ADMMPDInterfaceData *iface, Object *ob, float (*vertexCos)[3])
// b) the obstacle positions have changed from the last frame
bool has_obstacles =
iface->idata->collision &&
- iface->idata->obs.x0.size() > 0 &&
- iface->idata->obs.F.size() > 0 &&
- iface->idata->obs.x0.size()==iface->idata->obs.x1.size();
- bool lerp_obstacles =
- has_obstacles &&
- iface->idata->options->substeps>1 &&
- (iface->idata->obs.x0-iface->idata->obs.x1).lpNorm<Eigen::Infinity>()>1e-6;
-
- if (has_obstacles && iface->idata->obs.needs_sdf_recompute && !lerp_obstacles) {
+ iface->idata->obs_x0.size() > 0 &&
+ iface->idata->obs_x1.size() > 0 &&
+ iface->idata->obs_x0[0].size()==iface->idata->obs_x1[0].size();
+
+ int substeps = std::max(1,iface->idata->options->substeps);
+ int n_obs = iface->idata->obs_x0.size();
+ if (has_obstacles && substeps == 1) { // no lerp necessary
std::string set_obs_error = "";
if (!iface->idata->collision->set_obstacles(
- iface->idata->obs.x0.data(),
- iface->idata->obs.x1.data(),
- iface->idata->obs.x0.size()/3,
- iface->idata->obs.F.data(),
- iface->idata->obs.F.size()/3,
+ iface->idata->obs_x0, iface->idata->obs_x1, iface->idata->obs_F,
&set_obs_error)) {
strcpy_error(iface, set_obs_error.c_str());
- had_set_obstacle_error = true;
+ return_warning = true;
}
}
try
{
- Eigen::VectorXf obs_x1; // used if substeps > 1
- int substeps = std::max(1,iface->idata->options->substeps);
+ std::vector<Eigen::MatrixXd> obs_x1_t;
for (int i=0; i<substeps; ++i) {
- if (lerp_obstacles) {
+ // Interpolate obstacles
+ if (has_obstacles && substeps>1) {
float t = float(i)/float(substeps-1);
- obs_x1 = (1.f-t)*iface->idata->obs.x0 + t*iface->idata->obs.x1;
+ obs_x1_t.resize(n_obs);
+ for (int j=0; j<n_obs; ++j) {
+ obs_x1_t[j] = (1.f-t)*iface->idata->obs_x0[j] + t*iface->idata->obs_x1[j];
+ }
std::string set_obs_error = "";
- if (iface->idata->collision->set_obstacles(
- iface->idata->obs.x0.data(),
- obs_x1.data(),
- iface->idata->obs.x0.size()/3,
- iface->idata->obs.F.data(),
- iface->idata->obs.F.size()/3,
+ if (!iface->idata->collision->set_obstacles(
+ iface->idata->obs_x0, iface->idata->obs_x1, iface->idata->obs_F,
&set_obs_error)) {
strcpy_error(iface, set_obs_error.c_str());
- had_set_obstacle_error = true;
+ return_warning = true;
}
}
@@ -699,8 +703,7 @@ int admmpd_solve(ADMMPDInterfaceData *iface, Object *ob, float (*vertexCos)[3])
return 0;
}
- if (had_set_obstacle_error) {
- // Return warning (-1).
+ if (return_warning) {
// We've already copied the error message.
return -1;
}
@@ -708,6 +711,59 @@ int admmpd_solve(ADMMPDInterfaceData *iface, Object *ob, float (*vertexCos)[3])
return 1;
}
+void admmpd_update_obstacles(ADMMPDInterfaceData *iface, Object **obstacles, int numobjects)
+{
+ // Because substepping may occur, we'll buffer the start and end states
+ // of the obstacles. They will not be copied over to the collision pointer
+ // until solve(), depending on the number of substeps, in which case
+ // they are LERP'd
+ iface->idata->obs_x0.clear();
+ iface->idata->obs_x1.clear();
+ iface->idata->obs_F.clear();
+ if (!iface) { return; }
+ if (!iface->idata) { return; }
+ if (!obstacles || numobjects==0) { return; }
+
+ for (int i = 0; i < numobjects; ++i) {
+ Object *ob = obstacles[i];
+ if (!ob) {
+ continue; // uh?
+ }
+ if (ob->type != OB_MESH) {
+ continue; // is not a mesh type
+ }
+ if (!ob->pd || !ob->pd->deflect) {
+ continue; // is a non-collider
+ }
+ if (strcmp(ob->id.name,iface->name)==0) {
+ continue; // skip self
+ }
+
+ CollisionModifierData *cmd = (CollisionModifierData *)BKE_modifiers_findby_type(
+ ob, eModifierType_Collision);
+ if (!cmd) {
+ continue;
+ }
+
+ int idx = iface->idata->obs_x0.size();
+ iface->idata->obs_x0.emplace_back(Eigen::MatrixXd(cmd->mvert_num,3));
+ iface->idata->obs_x1.emplace_back(Eigen::MatrixXd(cmd->mvert_num,3));
+ iface->idata->obs_F.emplace_back(Eigen::MatrixXi(cmd->tri_num,3));
+
+ for (int j=0; j<cmd->mvert_num; ++j) {
+ for (int k=0; k<3; ++k) {
+ iface->idata->obs_x0[idx](j,k) = cmd->x[j].co[k];
+ iface->idata->obs_x1[idx](j,k) = cmd->xnew[j].co[k];
+ }
+ }
+
+ for (int j=0; j<cmd->tri_num; ++j) {
+ for (int k=0; k<3; ++k) {
+ iface->idata->obs_F[idx](j,k) = cmd->tri[j].tri[k];
+ }
+ }
+ }
+}
#ifdef WITH_TETGEN
@@ -864,6 +920,7 @@ static inline int admmpd_init_with_tetgen(ADMMPDInterfaceData *iface, Object *ob
iface->idata->mesh = std::make_shared<admmpd::TetMesh>();
bool success = iface->idata->mesh->create(
+ iface->idata->options.get(),
verts.data(),
nv,
faces.data(),
diff --git a/intern/softbody/admmpd_api.h b/intern/softbody/admmpd_api.h
index 18b0b8e3389..1055a5c9e7f 100644
--- a/intern/softbody/admmpd_api.h
+++ b/intern/softbody/admmpd_api.h
@@ -32,51 +32,72 @@ extern "C" {
#include "DNA_scene_types.h"
typedef struct ADMMPDInterfaceData {
- char last_error[256]; // last error message
- struct ADMMPDInternalData *idata; // internal data
+ /* So that ADMMPD data can be stored in a linked list. */
+ struct ADMMPDInterfaceData *next, *prev;
+ /* The name of the object that uses this data. */
+ char name[MAX_ID_NAME];
+ /* If API returns 0, error stored here. */
+ char last_error[256];
+ /* internal data is NULL until update_mesh or update_solver. */
+ struct ADMMPDInternalData *idata;
} ADMMPDInterfaceData;
-// Frees ADMMPDInternalData
+/* Frees ADMMPDInternalData */
void admmpd_dealloc(ADMMPDInterfaceData*);
-// Test if the mesh topology has changed in a way that requires re-initialization.
-// Returns 0 (no update needed) or 1 (needs update)
+/* Standalone function to compute embedding lattice
+* but without the embedding info (for visual debugging) */
+void admmpd_compute_lattice(
+ int subdiv,
+ float *in_verts, int in_nv,
+ unsigned int *in_faces, int in_nf,
+ float **out_verts, int *out_nv,
+ unsigned int **out_tets, int *out_nt);
+
+/* Test if the mesh topology has changed in a way that requires re-initialization.
+* Returns 0 (no update needed) or 1 (needs update) */
int admmpd_mesh_needs_update(ADMMPDInterfaceData*, Object*);
-// Initialize the mesh.
-// The SoftBody object's (ob->soft) bpoint array is also updated.
-// Returns 1 on success, 0 on failure, -1 on warning
+/* Initialize the mesh.
+* The SoftBody object's (ob->soft) bpoint array is also updated.
+* Returns 1 on success, 0 on failure, -1 on warning */
int admmpd_update_mesh(ADMMPDInterfaceData*, Object*, float (*vertexCos)[3]);
-// Test if certain parameter changes require re-initialization.
-// Returns 0 (no update needed) or 1 (needs update)
+/* Test if certain parameter changes require re-initialization.
+* Returns 0 (no update needed) or 1 (needs update) */
int admmpd_solver_needs_update(ADMMPDInterfaceData*, Scene*, Object*);
-// Initialize solver variables.
-// Returns 1 on success, 0 on failure, -1 on warning
+/* Initialize solver variables.
+* Returns 1 on success, 0 on failure, -1 on warning */
int admmpd_update_solver(ADMMPDInterfaceData*, Scene*, Object*, float (*vertexCos)[3]);
-// Copies BodyPoint data (from SoftBody)
-// to internal vertex position and velocity
+/* Copies BodyPoint data (from SoftBody)
+* to internal vertex position and velocity */
void admmpd_copy_from_object(ADMMPDInterfaceData*, Object*);
-// Copies ADMM-PD data to SoftBody::bpoint and vertexCos.
-// If vertexCos is NULL, it is ignored.
+/* Copies ADMM-PD data to SoftBody::bpoint and vertexCos.
+* If vertexCos is NULL, it is ignored. */
void admmpd_copy_to_object(ADMMPDInterfaceData*, Object*, float (*vertexCos)[3]);
-// Sets the obstacle data for collisions.
-// Update obstacles has a different interface because of the
-// complexity of grabbing obstacle mesh data. We'll leave that in softbody.c
+/* Sets the obstacle data for collisions. */
void admmpd_update_obstacles(
ADMMPDInterfaceData*,
- float *in_verts_0,
- float *in_verts_1,
- int nv,
- unsigned int *in_faces,
- int nf);
-
-// Performs a time step. Object and vertexCos are not changed.
-// Returns 1 on success, 0 on failure, -1 on warning
+ Object**,
+ int numobjects);
+
+/* Sets the obstacle data for collisions.
+* Update obstacles has a different interface because of the
+* complexity of grabbing obstacle mesh data. We'll leave that in softbody.c */
+//void admmpd_update_obstacles(
+// ADMMPDInterfaceData*,
+// float *in_verts_0,
+// float *in_verts_1,
+// int nv,
+// unsigned int *in_faces,
+// int nf);
+
+/* Performs a time step. Object and vertexCos are not changed.
+* Returns 1 on success, 0 on failure, -1 on warning */
int admmpd_solve(ADMMPDInterfaceData*, Object*, float (*vertexCos)[3]);
#ifdef __cplusplus
diff --git a/release/datafiles/locale b/release/datafiles/locale
-Subproject 260b439d0fb15e3cd1efe5c120cf24f91d13d85
+Subproject 2a85baf7318e2d8a26a25e6eb8211a2395d44a7
diff --git a/release/datafiles/userdef/userdef_default_theme.c b/release/datafiles/userdef/userdef_default_theme.c
index 4d48bb8eaac..7604a06f7df 100644
--- a/release/datafiles/userdef/userdef_default_theme.c
+++ b/release/datafiles/userdef/userdef_default_theme.c
@@ -238,11 +238,11 @@ const bTheme U_theme_default = {
.menu_shadow_fac = 0.3f,
.menu_shadow_width = 4,
.editor_outline = RGBA(0x1f1f1fff),
- .icon_alpha = 1.0f,
- .icon_saturation = 0.5f,
.transparent_checker_primary = RGBA(0x333333ff),
.transparent_checker_secondary = RGBA(0x262626ff),
.transparent_checker_size = 8,
+ .icon_alpha = 1.0f,
+ .icon_saturation = 0.5f,
.widget_text_cursor = RGBA(0x3399e6ff),
.xaxis = RGBA(0xff3352ff),
.yaxis = RGBA(0x8bdc00ff),
@@ -250,9 +250,9 @@ const bTheme U_theme_default = {
.gizmo_hi = RGBA(0xffffffff),
.gizmo_primary = RGBA(0xf5f14dff),
.gizmo_secondary = RGBA(0x63ffffff),
+ .gizmo_view_align = RGBA(0xffffffff),
.gizmo_a = RGBA(0x4da84dff),
.gizmo_b = RGBA(0xa33535ff),
- .gizmo_view_align = RGBA(0xffffffff),
.icon_scene = RGBA(0xe6e6e6ff),
.icon_collection = RGBA(0xf4f4f4ff),
.icon_object = RGBA(0xee9e5dff),
@@ -379,7 +379,7 @@ const bTheme U_theme_default = {
.paint_curve_handle = RGBA(0x7fff7f7f),
},
.space_file = {
- .back = RGBA(0x33333300),
+ .back = RGBA(0x28282800),
.title = RGBA(0xffffffff),
.text = RGBA(0xe6e6e6ff),
.text_hi = RGBA(0xffffffff),
@@ -404,6 +404,7 @@ const bTheme U_theme_default = {
.vertex_size = 3,
.outline_width = 1,
.facedot_size = 4,
+ .row_alternate = RGBA(0xffffff07),
},
.space_graph = {
.back = RGBA(0x42424200),
@@ -710,7 +711,6 @@ const bTheme U_theme_default = {
.preview_stitch_unstitchable = RGBA(0xff0000ff),
.preview_stitch_active = RGBA(0xe1d2c323),
.uv_shadow = RGBA(0x707070ff),
- .uv_others = RGBA(0x606060ff),
.paint_curve_pivot = RGBA(0xff7f7f7f),
.paint_curve_handle = RGBA(0x7fff7f7f),
.metadatatext = RGBA(0xffffffff),
diff --git a/release/scripts/addons b/release/scripts/addons
-Subproject 6ba9c816f3d8b342633cce3f874876f19b19118
+Subproject 88685da2c5d21cbde0412dba98f65b05f0d0bba
diff --git a/release/scripts/modules/rna_manual_reference.py b/release/scripts/modules/rna_manual_reference.py
index 673b33a1e93..f8562241ef9 100644
--- a/release/scripts/modules/rna_manual_reference.py
+++ b/release/scripts/modules/rna_manual_reference.py
@@ -42,6 +42,8 @@ url_manual_mapping = (
("bpy.types.fluiddomainsettings.sndparticle_potential_min_energy*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-min-energy"),
("bpy.types.fluiddomainsettings.sndparticle_sampling_trappedair*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-sampling-trappedair"),
("bpy.types.fluiddomainsettings.sndparticle_sampling_wavecrest*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-sampling-wavecrest"),
+ ("bpy.types.toolsettings.use_transform_correct_face_attributes*", "modeling/meshes/tools/tool_settings.html#bpy-types-toolsettings-use-transform-correct-face-attributes"),
+ ("bpy.types.toolsettings.use_transform_correct_keep_connected*", "modeling/meshes/tools/tool_settings.html#bpy-types-toolsettings-use-transform-correct-keep-connected"),
("bpy.types.fluiddomainsettings.sndparticle_potential_radius*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-radius"),
("bpy.types.fluiddomainsettings.openvdb_cache_compress_type*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-openvdb-cache-compress-type"),
("bpy.types.fluiddomainsettings.sndparticle_bubble_buoyancy*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-bubble-buoyancy"),
@@ -80,6 +82,7 @@ url_manual_mapping = (
("bpy.types.fluiddomainsettings.sndparticle_boundary*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-boundary"),
("bpy.types.fluiddomainsettings.sndparticle_life_max*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-life-max"),
("bpy.types.fluiddomainsettings.sndparticle_life_min*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-life-min"),
+ ("bpy.types.fluiddomainsettings.sys_particle_maximum*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-sys-particle-maximum"),
("bpy.types.fluiddomainsettings.use_bubble_particles*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-use-bubble-particles"),
("bpy.types.linestylegeometrymodifier_simplification*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/simplification.html#bpy-types-linestylegeometrymodifier-simplification"),
("bpy.types.materialgpencilstyle.use_overlap_strokes*", "grease_pencil/materials/grease_pencil_shader.html#bpy-types-materialgpencilstyle-use-overlap-strokes"),
@@ -97,6 +100,8 @@ url_manual_mapping = (
("bpy.types.linestylegeometrymodifier_perlinnoise1d*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/perlin_noise_1d.html#bpy-types-linestylegeometrymodifier-perlinnoise1d"),
("bpy.types.linestylegeometrymodifier_perlinnoise2d*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/perlin_noise_2d.html#bpy-types-linestylegeometrymodifier-perlinnoise2d"),
("bpy.types.rendersettings.use_high_quality_normals*", "render/eevee/render_settings/performance.html#bpy-types-rendersettings-use-high-quality-normals"),
+ ("bpy.ops.view3d.edit_mesh_extrude_individual_move*", "modeling/meshes/editing/face/extrude_faces.html#bpy-ops-view3d-edit-mesh-extrude-individual-move"),
+ ("bpy.ops.view3d.edit_mesh_extrude_manifold_normal*", "modeling/meshes/tools/extrude_manifold.html#bpy-ops-view3d-edit-mesh-extrude-manifold-normal"),
("bpy.types.cyclesrendersettings.use_distance_cull*", "render/cycles/render_settings/simplify.html#bpy-types-cyclesrendersettings-use-distance-cull"),
("bpy.types.fluiddomainsettings.cache_frame_offset*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-cache-frame-offset"),
("bpy.types.fluiddomainsettings.delete_in_obstacle*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-delete-in-obstacle"),
@@ -205,6 +210,7 @@ url_manual_mapping = (
("bpy.types.spaceview3d.use_local_collections*", "editors/3dview/properties/sidebar.html#bpy-types-spaceview3d-use-local-collections"),
("bpy.ops.object.constraint_add_with_targets*", "animation/constraints/interface/adding_removing.html#bpy-ops-object-constraint-add-with-targets"),
("bpy.ops.object.vertex_group_copy_to_linked*", "modeling/meshes/properties/vertex_groups/vertex_groups.html#bpy-ops-object-vertex-group-copy-to-linked"),
+ ("bpy.types.curve.bevel_factor_mapping_start*", "modeling/curves/properties/geometry.html#bpy-types-curve-bevel-factor-mapping-start"),
("bpy.types.cyclesobjectsettings.dicing_rate*", "render/cycles/object_settings/adaptive_subdiv.html#bpy-types-cyclesobjectsettings-dicing-rate"),
("bpy.types.fluiddomainsettings.adapt_margin*", "physics/fluid/type/domain/gas/adaptive_domain.html#bpy-types-fluiddomainsettings-adapt-margin"),
("bpy.types.fluiddomainsettings.burning_rate*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-burning-rate"),
@@ -226,6 +232,7 @@ url_manual_mapping = (
("bpy.ops.object.vertex_group_normalize_all*", "sculpt_paint/weight_paint/editing.html#bpy-ops-object-vertex-group-normalize-all"),
("bpy.ops.sculpt.face_set_change_visibility*", "sculpt_paint/sculpting/editing.html#bpy-ops-sculpt-face-set-change-visibility"),
("bpy.ops.sculpt.face_sets_randomize_colors*", "sculpt_paint/sculpting/editing.html#bpy-ops-sculpt-face-sets-randomize-colors"),
+ ("bpy.types.brush.disconnected_distance_max*", "sculpt_paint/sculpting/tools/pose.html#bpy-types-brush-disconnected-distance-max"),
("bpy.types.brush.surface_smooth_iterations*", "sculpt_paint/sculpting/tools/smooth.html#bpy-types-brush-surface-smooth-iterations"),
("bpy.types.brushgpencilsettings.pen_jitter*", "grease_pencil/modes/draw/tool_settings/brushes/draw_brush.html#bpy-types-brushgpencilsettings-pen-jitter"),
("bpy.types.fluiddomainsettings.domain_type*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-domain-type"),
@@ -257,6 +264,7 @@ url_manual_mapping = (
("bpy.types.brushgpencilsettings.uv_random*", "grease_pencil/modes/draw/tool_settings/brushes/draw_brush.html#bpy-types-brushgpencilsettings-uv-random"),
("bpy.types.clothsettings.internal_tension*", "physics/cloth/settings/physical_properties.html#bpy-types-clothsettings-internal-tension"),
("bpy.types.compositornodeplanetrackdeform*", "compositing/types/distort/plane_track_deform.html#bpy-types-compositornodeplanetrackdeform"),
+ ("bpy.types.curve.bevel_factor_mapping_end*", "modeling/curves/properties/geometry.html#bpy-types-curve-bevel-factor-mapping-end"),
("bpy.types.fluiddomainsettings.cache_type*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-cache-type"),
("bpy.types.fluiddomainsettings.coba_field*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-coba-field"),
("bpy.types.fluiddomainsettings.flip_ratio*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-flip-ratio"),
@@ -380,7 +388,6 @@ url_manual_mapping = (
("bpy.types.shadernodeambientocclusion*", "render/shader_nodes/input/ao.html#bpy-types-shadernodeambientocclusion"),
("bpy.types.shadernodevolumeabsorption*", "render/shader_nodes/shader/volume_absorption.html#bpy-types-shadernodevolumeabsorption"),
("bpy.types.shadernodevolumeprincipled*", "render/shader_nodes/shader/volume_principled.html#bpy-types-shadernodevolumeprincipled"),
- ("bpy.types.toolsettings.use_uv_sculpt*", "modeling/meshes/uv/uv_sculpt.html#bpy-types-toolsettings-use-uv-sculpt"),
("bpy.ops.gpencil.interpolate_reverse*", "grease_pencil/animation/interpolation.html#bpy-ops-gpencil-interpolate-reverse"),
("bpy.ops.gpencil.select_vertex_color*", "grease_pencil/selecting.html#bpy-ops-gpencil-select-vertex-color"),
("bpy.ops.gpencil.set_active_material*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-set-active-material"),
@@ -402,6 +409,7 @@ url_manual_mapping = (
("bpy.types.compositornodedilateerode*", "compositing/types/filter/dilate_erode.html#bpy-types-compositornodedilateerode"),
("bpy.types.compositornodeellipsemask*", "compositing/types/matte/ellipse_mask.html#bpy-types-compositornodeellipsemask"),
("bpy.types.compositornodesplitviewer*", "compositing/types/output/split_viewer.html#bpy-types-compositornodesplitviewer"),
+ ("bpy.types.curve.render_resolution_u*", "modeling/curves/properties/shape.html#bpy-types-curve-render-resolution-u"),
("bpy.types.dynamicpaintbrushsettings*", "physics/dynamic_paint/brush.html#bpy-types-dynamicpaintbrushsettings"),
("bpy.types.fluiddomainsettings.alpha*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-alpha"),
("bpy.types.fluidflowsettings.density*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-density"),
@@ -446,6 +454,7 @@ url_manual_mapping = (
("bpy.ops.wm.previews_batch_generate*", "files/blend/previews.html#bpy-ops-wm-previews-batch-generate"),
("bpy.types.brush.auto_smooth_factor*", "sculpt_paint/sculpting/tool_settings/brush_settings.html#bpy-types-brush-auto-smooth-factor"),
("bpy.types.brush.smooth_deform_type*", "sculpt_paint/sculpting/tools/smooth.html#bpy-types-brush-smooth-deform-type"),
+ ("bpy.types.brush.use_connected_only*", "sculpt_paint/sculpting/tools/pose.html#bpy-types-brush-use-connected-only"),
("bpy.types.brush.use_cursor_overlay*", "sculpt_paint/brush/cursor.html#bpy-types-brush-use-cursor-overlay"),
("bpy.types.camera.show_passepartout*", "render/cameras.html#bpy-types-camera-show-passepartout"),
("bpy.types.compositornodebokehimage*", "compositing/types/input/bokeh_image.html#bpy-types-compositornodebokehimage"),
@@ -456,6 +465,7 @@ url_manual_mapping = (
("bpy.types.compositornodeswitchview*", "compositing/types/converter/switch_view.html#bpy-types-compositornodeswitchview"),
("bpy.types.copytransformsconstraint*", "animation/constraints/transform/copy_transforms.html#bpy-types-copytransformsconstraint"),
("bpy.types.correctivesmoothmodifier*", "modeling/modifiers/deform/corrective_smooth.html#bpy-types-correctivesmoothmodifier"),
+ ("bpy.types.curve.bevel_factor_start*", "modeling/curves/properties/geometry.html#bpy-types-curve-bevel-factor-start"),
("bpy.types.cyclesvisibilitysettings*", "render/cycles/object_settings/object_data.html#bpy-types-cyclesvisibilitysettings"),
("bpy.types.fluiddomainsettings.beta*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-beta"),
("bpy.types.fluidmodifier.fluid_type*", "physics/fluid/type/index.html#bpy-types-fluidmodifier-fluid-type"),
@@ -470,6 +480,7 @@ url_manual_mapping = (
("bpy.types.shadernodebsdfrefraction*", "render/shader_nodes/shader/refraction.html#bpy-types-shadernodebsdfrefraction"),
("bpy.types.shadernodeoutputmaterial*", "render/shader_nodes/output/material.html#bpy-types-shadernodeoutputmaterial"),
("bpy.types.shadernodetexenvironment*", "render/shader_nodes/textures/environment.html#bpy-types-shadernodetexenvironment"),
+ ("bpy.types.spaceuveditor.uv_opacity*", "editors/uv/display_panel.html#bpy-types-spaceuveditor-uv-opacity"),
("bpy.types.subdividegpencilmodifier*", "grease_pencil/modifiers/generate/subdivide.html#bpy-types-subdividegpencilmodifier"),
("bpy.types.thicknessgpencilmodifier*", "grease_pencil/modifiers/deform/thickness.html#bpy-types-thicknessgpencilmodifier"),
("bpy.types.transformcacheconstraint*", "animation/constraints/transform/transform_cache.html#bpy-types-transformcacheconstraint"),
@@ -501,6 +512,7 @@ url_manual_mapping = (
("bpy.types.compositornodetransform*", "compositing/types/distort/transform.html#bpy-types-compositornodetransform"),
("bpy.types.compositornodetranslate*", "compositing/types/distort/translate.html#bpy-types-compositornodetranslate"),
("bpy.types.constraint.target_space*", "animation/constraints/interface/common.html#bpy-types-constraint-target-space"),
+ ("bpy.types.curve.use_deform_bounds*", "modeling/curves/properties/shape.html#bpy-types-curve-use-deform-bounds"),
("bpy.types.freestylemodulesettings*", "render/freestyle/python.html#bpy-types-freestylemodulesettings"),
("bpy.types.gpencillayer.blend_mode*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-blend-mode"),
("bpy.types.gpencillayer.mask_layer*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-mask-layer"),
@@ -519,6 +531,7 @@ url_manual_mapping = (
("bpy.types.shadernodevolumescatter*", "render/shader_nodes/shader/volume_scatter.html#bpy-types-shadernodevolumescatter"),
("bpy.types.simplifygpencilmodifier*", "grease_pencil/modifiers/generate/simplify.html#bpy-types-simplifygpencilmodifier"),
("bpy.types.spacegrapheditor.cursor*", "editors/graph_editor/introduction.html#bpy-types-spacegrapheditor-cursor"),
+ ("bpy.types.toolsettings.annotation*", "interface/annotate_tool.html#bpy-types-toolsettings-annotation"),
("bpy.types.vertexweightmixmodifier*", "modeling/modifiers/modify/weight_mix.html#bpy-types-vertexweightmixmodifier"),
("bpy.types.viewlayer.use_freestyle*", "render/freestyle/view_layer.html#bpy-types-viewlayer-use-freestyle"),
("bpy.ops.gpencil.frame_clean_fill*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-frame-clean-fill"),
@@ -542,6 +555,7 @@ url_manual_mapping = (
("bpy.ops.uv.average_islands_scale*", "modeling/meshes/uv/editing.html#bpy-ops-uv-average-islands-scale"),
("bpy.types.brightcontrastmodifier*", "video_editing/sequencer/properties/modifiers.html#bpy-types-brightcontrastmodifier"),
("bpy.types.brush.cursor_color_add*", "sculpt_paint/brush/cursor.html#bpy-types-brush-cursor-color-add"),
+ ("bpy.types.brush.pose_deform_type*", "sculpt_paint/sculpting/tools/pose.html#bpy-types-brush-pose-deform-type"),
("bpy.types.brush.pose_ik_segments*", "sculpt_paint/sculpting/tools/pose.html#bpy-types-brush-pose-ik-segments"),
("bpy.types.brush.pose_origin_type*", "sculpt_paint/sculpting/tools/pose.html#bpy-types-brush-pose-origin-type"),
("bpy.types.camerasolverconstraint*", "animation/constraints/motion_tracking/camera_solver.html#bpy-types-camerasolverconstraint"),
@@ -560,6 +574,8 @@ url_manual_mapping = (
("bpy.types.constraint.owner_space*", "animation/constraints/interface/common.html#bpy-types-constraint-owner-space"),
("bpy.types.copylocationconstraint*", "animation/constraints/transform/copy_location.html#bpy-types-copylocationconstraint"),
("bpy.types.copyrotationconstraint*", "animation/constraints/transform/copy_rotation.html#bpy-types-copyrotationconstraint"),
+ ("bpy.types.curve.bevel_factor_end*", "modeling/curves/properties/geometry.html#bpy-types-curve-bevel-factor-end"),
+ ("bpy.types.curve.bevel_resolution*", "modeling/curves/properties/geometry.html#bpy-types-curve-bevel-resolution"),
("bpy.types.cyclesmaterialsettings*", "render/cycles/material_settings.html#bpy-types-cyclesmaterialsettings"),
("bpy.types.dopesheet.show_summary*", "editors/dope_sheet/introduction.html#bpy-types-dopesheet-show-summary"),
("bpy.types.imagepaint.use_occlude*", "sculpt_paint/texture_paint/tool_settings/options.html#bpy-types-imagepaint-use-occlude"),
@@ -576,6 +592,7 @@ url_manual_mapping = (
("bpy.types.shadernodeparticleinfo*", "render/shader_nodes/input/particle_info.html#bpy-types-shadernodeparticleinfo"),
("bpy.types.shadernodevectorrotate*", "render/shader_nodes/vector/vector_rotate.html#bpy-types-shadernodevectorrotate"),
("bpy.types.spaceview3d.show_gizmo*", "editors/3dview/display/gizmo.html#bpy-types-spaceview3d-show-gizmo"),
+ ("bpy.types.texturegpencilmodifier*", "grease_pencil/modifiers/color/texture_mapping.html#bpy-types-texturegpencilmodifier"),
("bpy.types.volumerender.step_size*", "modeling/volumes/properties.html#bpy-types-volumerender-step-size"),
("bpy.types.weightednormalmodifier*", "modeling/modifiers/modify/weighted_normal.html#bpy-types-weightednormalmodifier"),
("bpy.ops.armature.autoside_names*", "animation/armatures/bones/editing/naming.html#bpy-ops-armature-autoside-names"),
@@ -618,6 +635,8 @@ url_manual_mapping = (
("bpy.types.compositornodetexture*", "compositing/types/input/texture.html#bpy-types-compositornodetexture"),
("bpy.types.compositornodetonemap*", "compositing/types/color/tone_map.html#bpy-types-compositornodetonemap"),
("bpy.types.compositornodevecblur*", "compositing/types/filter/vector_blur.html#bpy-types-compositornodevecblur"),
+ ("bpy.types.curve.use_fill_deform*", "modeling/curves/properties/shape.html#bpy-types-curve-use-fill-deform"),
+ ("bpy.types.curve.use_path_follow*", "modeling/curves/properties/path_animation.html#bpy-types-curve-use-path-follow"),
("bpy.types.dampedtrackconstraint*", "animation/constraints/tracking/damped_track.html#bpy-types-dampedtrackconstraint"),
("bpy.types.distortednoisetexture*", "render/materials/legacy_textures/types/distorted_noise.html#bpy-types-distortednoisetexture"),
("bpy.types.fluideffectorsettings*", "physics/fluid/type/effector.html#bpy-types-fluideffectorsettings"),
@@ -725,6 +744,7 @@ url_manual_mapping = (
("bpy.ops.object.select_by_type*", "scene_layout/object/selecting.html#bpy-ops-object-select-by-type"),
("bpy.ops.object.select_grouped*", "scene_layout/object/selecting.html#bpy-ops-object-select-grouped"),
("bpy.ops.object.select_pattern*", "scene_layout/object/selecting.html#bpy-ops-object-select-pattern"),
+ ("bpy.ops.outliner.id_operation*", "editors/outliner.html#bpy-ops-outliner-id-operation"),
("bpy.ops.paint.mask_flood_fill*", "sculpt_paint/sculpting/hide_mask.html#bpy-ops-paint-mask-flood-fill"),
("bpy.ops.pose.quaternions_flip*", "animation/armatures/posing/editing/flip_quats.html#bpy-ops-pose-quaternions-flip"),
("bpy.ops.pose.transforms_clear*", "animation/armatures/posing/editing/clear.html#bpy-ops-pose-transforms-clear"),
@@ -751,6 +771,9 @@ url_manual_mapping = (
("bpy.types.compositornodescale*", "compositing/types/distort/scale.html#bpy-types-compositornodescale"),
("bpy.types.compositornodevalue*", "compositing/types/input/value.html#bpy-types-compositornodevalue"),
("bpy.types.copyscaleconstraint*", "animation/constraints/transform/copy_scale.html#bpy-types-copyscaleconstraint"),
+ ("bpy.types.curve.path_duration*", "modeling/curves/properties/path_animation.html#bpy-types-curve-path-duration"),
+ ("bpy.types.curve.use_fill_caps*", "modeling/curves/properties/geometry.html#bpy-types-curve-use-fill-caps"),
+ ("bpy.types.curve.use_map_taper*", "modeling/curves/properties/geometry.html#bpy-types-curve-use-map-taper"),
("bpy.types.cyclesworldsettings*", "render/cycles/world_settings.html#bpy-types-cyclesworldsettings"),
("bpy.types.fluiddomainsettings*", "physics/fluid/type/domain/index.html#bpy-types-fluiddomainsettings"),
("bpy.types.hookgpencilmodifier*", "grease_pencil/modifiers/deform/hook.html#bpy-types-hookgpencilmodifier"),
@@ -828,8 +851,11 @@ url_manual_mapping = (
("bpy.types.compositornodemask*", "compositing/types/input/mask.html#bpy-types-compositornodemask"),
("bpy.types.compositornodemath*", "compositing/types/converter/math.html#bpy-types-compositornodemath"),
("bpy.types.compositornodetime*", "compositing/types/input/time.html#bpy-types-compositornodetime"),
+ ("bpy.types.curve.bevel_object*", "modeling/curves/properties/geometry.html#bpy-types-curve-bevel-object"),
("bpy.types.curve.resolution_u*", "modeling/curves/properties/shape.html#bpy-types-curve-resolution-u"),
("bpy.types.curve.resolution_v*", "modeling/surfaces/properties/shape.html#bpy-types-curve-resolution-v"),
+ ("bpy.types.curve.taper_object*", "modeling/curves/properties/geometry.html#bpy-types-curve-taper-object"),
+ ("bpy.types.curve.twist_smooth*", "modeling/curves/properties/shape.html#bpy-types-curve-twist-smooth"),
("bpy.types.curvepaintsettings*", "modeling/curves/tools/draw.html#bpy-types-curvepaintsettings"),
("bpy.types.fmodifiergenerator*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifiergenerator"),
("bpy.types.freestylelinestyle*", "render/freestyle/parameter_editor/line_style/index.html#bpy-types-freestylelinestyle"),
@@ -912,6 +938,8 @@ url_manual_mapping = (
("bpy.types.collisionsettings*", "physics/collision.html#bpy-types-collisionsettings"),
("bpy.types.compositornodergb*", "compositing/types/input/rgb.html#bpy-types-compositornodergb"),
("bpy.types.compositornodesep*", "editors/texture_node/types/color/combine_separate.html#bpy-types-compositornodesep"),
+ ("bpy.types.curve.bevel_depth*", "modeling/curves/properties/geometry.html#bpy-types-curve-bevel-depth"),
+ ("bpy.types.curve.use_stretch*", "modeling/curves/properties/shape.html#bpy-types-curve-use-stretch"),
("bpy.types.edgesplitmodifier*", "modeling/modifiers/generate/edge_split.html#bpy-types-edgesplitmodifier"),
("bpy.types.fluidflowsettings*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings"),
("bpy.types.fmodifierenvelope*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifierenvelope"),
@@ -957,6 +985,7 @@ url_manual_mapping = (
("bpy.ops.mesh.colors_rotate*", "modeling/meshes/editing/face/face_data.html#bpy-ops-mesh-colors-rotate"),
("bpy.ops.mesh.edge_collapse*", "modeling/meshes/editing/mesh/delete.html#bpy-ops-mesh-edge-collapse"),
("bpy.ops.mesh.edge_face_add*", "modeling/meshes/editing/vertex/make_face_edge.html#bpy-ops-mesh-edge-face-add"),
+ ("bpy.ops.mesh.extrude_indiv*", "modeling/meshes/editing/face/extrude_faces.html#bpy-ops-mesh-extrude-indiv"),
("bpy.ops.mesh.knife_project*", "modeling/meshes/editing/mesh/knife_project.html#bpy-ops-mesh-knife-project"),
("bpy.ops.mesh.loopcut_slide*", "modeling/meshes/editing/edge/loopcut_slide.html#bpy-ops-mesh-loopcut-slide"),
("bpy.ops.mesh.merge_normals*", "modeling/meshes/editing/mesh/normals.html#bpy-ops-mesh-merge-normals"),
@@ -979,10 +1008,14 @@ url_manual_mapping = (
("bpy.ops.transform.tosphere*", "modeling/meshes/editing/mesh/transform/to_sphere.html#bpy-ops-transform-tosphere"),
("bpy.ops.view3d.clip_border*", "editors/3dview/navigate/regions.html#bpy-ops-view3d-clip-border"),
("bpy.ops.wm.previews_ensure*", "files/blend/previews.html#bpy-ops-wm-previews-ensure"),
+ ("bpy.ops.wm.search_operator*", "interface/controls/templates/operator_search.html#bpy-ops-wm-search-operator"),
("bpy.types.actionconstraint*", "animation/constraints/relationship/action.html#bpy-types-actionconstraint"),
("bpy.types.addonpreferences*", "editors/preferences/addons.html#bpy-types-addonpreferences"),
("bpy.types.armaturemodifier*", "modeling/modifiers/deform/armature.html#bpy-types-armaturemodifier"),
("bpy.types.colormixsequence*", "video_editing/sequencer/strips/effects/color_mix.html#bpy-types-colormixsequence"),
+ ("bpy.types.curve.dimensions*", "modeling/curves/properties/shape.html#bpy-types-curve-dimensions"),
+ ("bpy.types.curve.twist_mode*", "modeling/curves/properties/shape.html#bpy-types-curve-twist-mode"),
+ ("bpy.types.curve.use_radius*", "modeling/curves/properties/shape.html#bpy-types-curve-use-radius"),
("bpy.types.decimatemodifier*", "modeling/modifiers/generate/decimate.html#bpy-types-decimatemodifier"),
("bpy.types.displacemodifier*", "modeling/modifiers/deform/displace.html#bpy-types-displacemodifier"),
("bpy.types.displaysafeareas*", "render/cameras.html#bpy-types-displaysafeareas"),
@@ -1026,10 +1059,13 @@ url_manual_mapping = (
("bpy.ops.fluid.bake_guides*", "physics/fluid/type/domain/guides.html#bpy-ops-fluid-bake-guides"),
("bpy.ops.fluid.free_guides*", "physics/fluid/type/domain/guides.html#bpy-ops-fluid-free-guides"),
("bpy.ops.font.style_toggle*", "modeling/texts/editing.html#bpy-ops-font-style-toggle"),
+ ("bpy.ops.gpencil.mesh_bake*", "grease_pencil/animation/tools.html#bpy-ops-gpencil-mesh-bake"),
("bpy.ops.gpencil.reproject*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-reproject"),
("bpy.ops.graph.easing_type*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-easing-type"),
("bpy.ops.graph.handle_type*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-handle-type"),
+ ("bpy.ops.mesh.bevel.vertex*", "modeling/meshes/editing/vertex/bevel_vertices.html#bpy-ops-mesh-bevel-vertex"),
("bpy.ops.mesh.delete_loose*", "modeling/meshes/editing/mesh/cleanup.html#bpy-ops-mesh-delete-loose"),
+ ("bpy.ops.mesh.face_shading*", "modeling/meshes/editing/face/shading.html#bpy-ops-mesh-face-shading"),
("bpy.ops.mesh.flip_normals*", "modeling/meshes/editing/mesh/normals.html#bpy-ops-mesh-flip-normals"),
("bpy.ops.mesh.select_loose*", "modeling/meshes/selecting/all_by_trait.html#bpy-ops-mesh-select-loose"),
("bpy.ops.mesh.vert_connect*", "modeling/meshes/editing/vertex/connect_vertex_pairs.html#bpy-ops-mesh-vert-connect"),
@@ -1053,6 +1089,8 @@ url_manual_mapping = (
("bpy.types.booleanmodifier*", "modeling/modifiers/generate/booleans.html#bpy-types-booleanmodifier"),
("bpy.types.brush.mask_tool*", "sculpt_paint/sculpting/tools/mask.html#bpy-types-brush-mask-tool"),
("bpy.types.constraint.mute*", "animation/constraints/interface/header.html#bpy-types-constraint-mute"),
+ ("bpy.types.curve.eval_time*", "modeling/curves/properties/path_animation.html#bpy-types-curve-eval-time"),
+ ("bpy.types.curve.fill_mode*", "modeling/curves/properties/shape.html#bpy-types-curve-fill-mode"),
("bpy.types.explodemodifier*", "modeling/modifiers/physics/explode.html#bpy-types-explodemodifier"),
("bpy.types.fcurvemodifiers*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fcurvemodifiers"),
("bpy.types.floorconstraint*", "animation/constraints/relationship/floor.html#bpy-types-floorconstraint"),
@@ -1148,6 +1186,7 @@ url_manual_mapping = (
("bpy.ops.fluid.free_data*", "physics/fluid/type/domain/settings.html#bpy-ops-fluid-free-data"),
("bpy.ops.fluid.free_mesh*", "physics/fluid/type/domain/liquid/mesh.html#bpy-ops-fluid-free-mesh"),
("bpy.ops.font.select_all*", "modeling/texts/selecting.html#bpy-ops-font-select-all"),
+ ("bpy.ops.gpencil.convert*", "grease_pencil/modes/object/convert_to_geometry.html#bpy-ops-gpencil-convert"),
("bpy.ops.mesh.customdata*", "modeling/meshes/properties/custom_data.html#bpy-ops-mesh-customdata"),
("bpy.ops.mesh.edge_split*", "modeling/meshes/editing/mesh/split.html#bpy-ops-mesh-edge-split"),
("bpy.ops.mesh.fill_holes*", "modeling/meshes/editing/mesh/cleanup.html#bpy-ops-mesh-fill-holes"),
@@ -1182,6 +1221,7 @@ url_manual_mapping = (
("bpy.types.cloudstexture*", "render/materials/legacy_textures/types/clouds.html#bpy-types-cloudstexture"),
("bpy.types.colorsequence*", "video_editing/sequencer/strips/color.html#bpy-types-colorsequence"),
("bpy.types.crosssequence*", "video_editing/sequencer/strips/transitions/cross.html#bpy-types-crosssequence"),
+ ("bpy.types.curve.extrude*", "modeling/curves/properties/geometry.html#bpy-types-curve-extrude"),
("bpy.types.curvemodifier*", "modeling/modifiers/deform/curve.html#bpy-types-curvemodifier"),
("bpy.types.fieldsettings*", "physics/forces/force_fields/index.html#bpy-types-fieldsettings"),
("bpy.types.imagesequence*", "video_editing/sequencer/strips/movie_image.html#bpy-types-imagesequence"),
@@ -1204,6 +1244,7 @@ url_manual_mapping = (
("bpy.types.soundsequence*", "video_editing/sequencer/strips/sound.html#bpy-types-soundsequence"),
("bpy.types.spaceoutliner*", "editors/outliner.html#bpy-types-spaceoutliner"),
("bpy.types.spacetimeline*", "editors/timeline.html#bpy-types-spacetimeline"),
+ ("bpy.types.spaceuveditor*", "editors/uv/index.html#bpy-types-spaceuveditor"),
("bpy.types.stuccitexture*", "render/materials/legacy_textures/types/stucci.html#bpy-types-stuccitexture"),
("bpy.types.view3doverlay*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay"),
("bpy.types.volumedisplay*", "modeling/volumes/properties.html#bpy-types-volumedisplay"),
@@ -1244,6 +1285,7 @@ url_manual_mapping = (
("bpy.types.brush.height*", "sculpt_paint/sculpting/tools/layer.html#bpy-types-brush-height"),
("bpy.types.castmodifier*", "modeling/modifiers/deform/cast.html#bpy-types-castmodifier"),
("bpy.types.colormanaged*", "render/color_management.html#bpy-types-colormanaged"),
+ ("bpy.types.curve.offset*", "modeling/curves/properties/geometry.html#bpy-types-curve-offset"),
("bpy.types.glowsequence*", "video_editing/sequencer/strips/effects/glow.html#bpy-types-glowsequence"),
("bpy.types.gpencillayer*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer"),
("bpy.types.hookmodifier*", "modeling/modifiers/deform/hooks.html#bpy-types-hookmodifier"),
@@ -1292,6 +1334,7 @@ url_manual_mapping = (
("bpy.types.addsequence*", "video_editing/sequencer/strips/effects/add.html#bpy-types-addsequence"),
("bpy.types.camera.show*", "render/cameras.html#bpy-types-camera-show"),
("bpy.types.consoleline*", "editors/python_console.html#bpy-types-consoleline"),
+ ("bpy.types.curve.bevel*", "modeling/curves/properties/geometry.html#bpy-types-curve-bevel"),
("bpy.types.mesh.remesh*", "modeling/meshes/retopology.html#bpy-types-mesh-remesh"),
("bpy.types.meshstatvis*", "modeling/meshes/mesh_analysis.html#bpy-types-meshstatvis"),
("bpy.types.nodesetting*", "interface/controls/nodes/parts.html#bpy-types-nodesetting"),
@@ -1346,6 +1389,7 @@ url_manual_mapping = (
("bpy.ops.object.hook*", "modeling/meshes/editing/vertex/hooks.html#bpy-ops-object-hook"),
("bpy.ops.object.join*", "scene_layout/object/editing/join.html#bpy-ops-object-join"),
("bpy.ops.object.text*", "modeling/texts/index.html#bpy-ops-object-text"),
+ ("bpy.ops.uv.rip_move*", "modeling/meshes/uv/tools/rip.html#bpy-ops-uv-rip-move"),
("bpy.ops.view3d.snap*", "scene_layout/object/editing/snap.html#bpy-ops-view3d-snap"),
("bpy.types.*texspace*", "modeling/meshes/uv/uv_texture_spaces.html#bpy-types-texspace"),
("bpy.types.arealight*", "render/lights/light_object.html#bpy-types-arealight"),
@@ -1414,6 +1458,7 @@ url_manual_mapping = (
("bpy.ops.uv.select*", "editors/uv/selecting.html#bpy-ops-uv-select"),
("bpy.ops.uv.stitch*", "modeling/meshes/uv/editing.html#bpy-ops-uv-stitch"),
("bpy.ops.uv.unwrap*", "modeling/meshes/editing/uv.html#bpy-ops-uv-unwrap"),
+ ("bpy.ops.wm.search*", "interface/controls/templates/operator_search.html#bpy-ops-wm-search"),
("bpy.types.animviz*", "animation/motion_paths.html#bpy-types-animviz"),
("bpy.types.lattice*", "animation/lattice.html#bpy-types-lattice"),
("bpy.types.library*", "files/linked_libraries/index.html#bpy-types-library"),
@@ -1479,6 +1524,7 @@ url_manual_mapping = (
("bpy.ops.script*", "advanced/scripting/index.html#bpy-ops-script"),
("bpy.ops.sculpt*", "sculpt_paint/sculpting/index.html#bpy-ops-sculpt"),
("bpy.ops.uv.pin*", "modeling/meshes/uv/editing.html#bpy-ops-uv-pin"),
+ ("bpy.ops.uv.rip*", "modeling/meshes/uv/tools/rip.html#bpy-ops-uv-rip"),
("bpy.ops.view3d*", "editors/3dview/index.html#bpy-ops-view3d"),
("bpy.types.area*", "interface/window_system/areas.html#bpy-types-area"),
("bpy.types.boid*", "physics/particles/emitter/physics/boids.html#bpy-types-boid"),
diff --git a/release/scripts/presets/interface_theme/blender_light.xml b/release/scripts/presets/interface_theme/blender_light.xml
index 48ad0a8367e..1b48377f62c 100644
--- a/release/scripts/presets/interface_theme/blender_light.xml
+++ b/release/scripts/presets/interface_theme/blender_light.xml
@@ -534,13 +534,14 @@
</graph_editor>
<file_browser>
<ThemeFileBrowser
+ row_alternate="#ffffff0f"
selected_file="#5680c2"
>
<space>
<ThemeSpaceGeneric
- back="#404040"
+ back="#999999"
title="#000000"
- text="#eeeeee"
+ text="#000000"
text_hi="#ffffff"
header="#adadadff"
header_text="#000000"
@@ -735,7 +736,6 @@
preview_stitch_unstitchable="#ff0000ff"
preview_stitch_active="#e1d2c323"
uv_shadow="#707070ff"
- uv_others="#606060ff"
frame_current="#5680c2"
metadatabg="#000000"
metadatatext="#ffffff"
diff --git a/release/scripts/startup/bl_operators/sequencer.py b/release/scripts/startup/bl_operators/sequencer.py
index a332f938afd..9b15ccf167b 100644
--- a/release/scripts/startup/bl_operators/sequencer.py
+++ b/release/scripts/startup/bl_operators/sequencer.py
@@ -151,7 +151,13 @@ class SequencerFadesClear(Operator):
return context.scene and context.scene.sequence_editor and context.scene.sequence_editor.active_strip
def execute(self, context):
- fcurves = context.scene.animation_data.action.fcurves
+ animation_data = context.scene.animation_data
+ if animation_data is None:
+ return {'CANCELLED'}
+ action = animation_data.action
+ if action is None:
+ return {'CANCELLED'}
+ fcurves = action.fcurves
fcurve_map = {
curve.data_path: curve
for curve in fcurves
diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py
index 83039a5c333..59abff12834 100644
--- a/release/scripts/startup/bl_operators/wm.py
+++ b/release/scripts/startup/bl_operators/wm.py
@@ -1284,18 +1284,11 @@ class WM_OT_properties_edit(Operator):
prop_type_old = type(item[prop_old])
rna_idprop_ui_prop_clear(item, prop_old)
- exec_str = "del item[%r]" % prop_old
- # print(exec_str)
- exec(exec_str)
+ del item[prop_old]
# Reassign
- exec_str = "item[%r] = %s" % (prop, repr(value_eval))
- # print(exec_str)
- exec(exec_str)
-
- exec_str = "item.property_overridable_library_set('[\"%s\"]', %s)" % (prop, self.is_overridable_library)
- exec(exec_str)
-
+ item[prop] = value_eval
+ item.property_overridable_library_set('["%s"]' % prop, self.is_overridable_library)
rna_idprop_ui_prop_update(item, prop)
self._last_prop[:] = [prop]
@@ -1385,8 +1378,9 @@ class WM_OT_properties_edit(Operator):
return {'CANCELLED'}
# retrieve overridable static
- exec_str = "item.is_property_overridable_library('[\"%s\"]')" % (self.property)
- self.is_overridable_library = bool(eval(exec_str))
+ is_overridable = item.is_property_overridable_library('["%s"]' % self.property)
+ self.is_overridable_library = bool(is_overridable)
+
# default default value
prop_type, is_array = rna_idprop_value_item_type(self.get_value_eval())
diff --git a/release/scripts/startup/bl_ui/properties_data_camera.py b/release/scripts/startup/bl_ui/properties_data_camera.py
index 3de144c5a15..80bd8347421 100644
--- a/release/scripts/startup/bl_ui/properties_data_camera.py
+++ b/release/scripts/startup/bl_ui/properties_data_camera.py
@@ -385,6 +385,15 @@ class DATA_PT_camera_display(CameraButtonsPanel, Panel):
col.prop(cam, "show_sensor", text="Sensor")
col.prop(cam, "show_name", text="Name")
+ col = layout.column(align=False, heading="Passepartout")
+ col.use_property_decorate = False
+ row = col.row(align=True)
+ sub = row.row(align=True)
+ sub.prop(cam, "show_passepartout", text="")
+ sub = sub.row(align=True)
+ sub.active = cam.show_passepartout
+ sub.prop(cam, "passepartout_alpha", text="")
+ row.prop_decorator(cam, "passepartout_alpha")
class DATA_PT_camera_display_composition_guides(CameraButtonsPanel, Panel):
bl_label = "Composition Guides"
@@ -414,27 +423,6 @@ class DATA_PT_camera_display_composition_guides(CameraButtonsPanel, Panel):
col.prop(cam, "show_composition_harmony_tri_b", text="Triangle B")
-class DATA_PT_camera_display_passepartout(CameraButtonsPanel, Panel):
- bl_label = "Passepartout"
- bl_parent_id = "DATA_PT_camera_display"
- bl_options = {'DEFAULT_CLOSED'}
- COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
-
- def draw_header(self, context):
- cam = context.camera
-
- self.layout.prop(cam, "show_passepartout", text="")
-
- def draw(self, context):
- layout = self.layout
- layout.use_property_split = True
-
- cam = context.camera
-
- layout.active = cam.show_passepartout
- layout.prop(cam, "passepartout_alpha", text="Opacity", slider=True)
-
-
class DATA_PT_camera_safe_areas(CameraButtonsPanel, Panel):
bl_label = "Safe Areas"
bl_options = {'DEFAULT_CLOSED'}
@@ -534,7 +522,6 @@ classes = (
DATA_PT_camera_background_image,
DATA_PT_camera_display,
DATA_PT_camera_display_composition_guides,
- DATA_PT_camera_display_passepartout,
DATA_PT_custom_props_camera,
)
diff --git a/release/scripts/startup/bl_ui/properties_data_mesh.py b/release/scripts/startup/bl_ui/properties_data_mesh.py
index 9cb774e1d1b..9673fe32a9c 100644
--- a/release/scripts/startup/bl_ui/properties_data_mesh.py
+++ b/release/scripts/startup/bl_ui/properties_data_mesh.py
@@ -502,7 +502,7 @@ class DATA_PT_remesh(MeshButtonsPanel, Panel):
row = layout.row()
mesh = context.mesh
- row.prop(mesh, "remesh_mode", text="Mode", expand=True)
+ row.prop(mesh, "remesh_mode", text="Mode", expand=False)
col = layout.column()
if mesh.remesh_mode == 'VOXEL':
col.prop(mesh, "remesh_voxel_size")
@@ -520,8 +520,10 @@ class DATA_PT_remesh(MeshButtonsPanel, Panel):
col.operator("object.voxel_remesh", text="Voxel Remesh")
elif mesh.remesh_mode == 'QUAD':
col.operator("object.quadriflow_remesh", text="QuadriFlow Remesh")
- else:
+ elif mesh.remesh_mode == 'TET':
col.operator("object.tetgen_remesh", text="Tetrahedralize")
+ elif mesh.remesh_mode == 'TETLATTICE':
+ col.operator("object.tetlattice_remesh", text="Generate Lattice")
diff --git a/release/scripts/startup/bl_ui/properties_physics_softbody.py b/release/scripts/startup/bl_ui/properties_physics_softbody.py
index 65475b97666..4d6e93b5bf6 100644
--- a/release/scripts/startup/bl_ui/properties_physics_softbody.py
+++ b/release/scripts/startup/bl_ui/properties_physics_softbody.py
@@ -57,6 +57,7 @@ class PHYSICS_PT_softbody(PhysicButtonsPanel, Panel):
layout.prop(softbody, "solver_mode")
if softbody.solver_mode=='LEGACY':
+ # Moved to collision dropdown in ADMMPD
layout.prop(softbody, "collision_collection")
elif softbody.solver_mode=='ADMMPD':
layout.prop(softbody, "admmpd_mesh_mode")
@@ -350,6 +351,8 @@ class PHYSICS_PT_softbody_admmpdcollision(PhysicButtonsPanel, Panel):
layout.enabled = softbody_panel_enabled(md)
ob = context.object
+ layout.prop(softbody, "collision_collection")
+
layout.prop(softbody, "admmpd_ck_exp")
layout.prop(softbody, "admmpd_floor_z")
layout.prop(softbody, "admmpd_self_collision")
diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
index 1bc6d5cf71f..e336635a4ee 100644
--- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
@@ -1289,6 +1289,7 @@ class _defs_sculpt:
layout.prop(props, "strength")
row = layout.row(align=True)
row.prop(props, "force_axis")
+ layout.prop(props, "orientation", expand=False)
layout.prop(props, "cloth_mass")
layout.prop(props, "cloth_damping")
layout.prop(props, "use_face_sets")
diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index c251b4bfcac..deac4e4c507 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -5942,7 +5942,7 @@ class VIEW3D_PT_gizmo_display(Panel):
layout.separator()
col = layout.column()
- col.active = view.show_gizmo_context
+ col.active = view.show_gizmo and view.show_gizmo_context
col.label(text="Object Gizmos")
col.prop(scene.transform_orientation_slots[1], "type", text="")
col.prop(view, "show_gizmo_object_translate", text="Move")
@@ -5953,6 +5953,7 @@ class VIEW3D_PT_gizmo_display(Panel):
# Match order of object type visibility
col = layout.column()
+ col.active = view.show_gizmo
col.label(text="Empty")
col.prop(view, "show_gizmo_empty_image", text="Image")
col.prop(view, "show_gizmo_empty_force_field", text="Force Field")
@@ -6111,8 +6112,12 @@ class VIEW3D_PT_overlay_motion_tracking(Panel):
bl_label = "Motion Tracking"
def draw_header(self, context):
+ layout = self.layout
view = context.space_data
- self.layout.prop(view, "show_reconstruction", text=self.bl_label)
+ overlay = view.overlay
+ display_all = overlay.show_overlays
+ layout.active = display_all
+ layout.prop(view, "show_reconstruction", text=self.bl_label)
def draw(self, context):
layout = self.layout
@@ -6134,6 +6139,7 @@ class VIEW3D_PT_overlay_motion_tracking(Panel):
sub.prop(view, "show_bundle_names", text="Marker Names")
col = layout.column()
+ col.active = display_all
col.label(text="Tracks:")
row = col.row(align=True)
row.prop(view, "tracks_display_type", text="")
@@ -6569,6 +6575,8 @@ class VIEW3D_PT_proportional_edit(Panel):
layout = self.layout
tool_settings = context.tool_settings
col = layout.column()
+ col.active = (tool_settings.use_proportional_edit_objects if context.mode == 'OBJECT'
+ else tool_settings.use_proportional_edit)
if context.mode != 'OBJECT':
col.prop(tool_settings, "use_proportional_connected")
diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py
index f62c2ead144..0cf0a99c326 100644
--- a/release/scripts/startup/nodeitems_builtins.py
+++ b/release/scripts/startup/nodeitems_builtins.py
@@ -478,7 +478,7 @@ texture_node_categories = [
def not_implemented_node(idname):
NodeType = getattr(bpy.types, idname)
name = NodeType.bl_rna.name
- label = f"{name} (mockup)"
+ label = "%s (mockup)" % name
return NodeItem(idname, label=label)
simulation_node_categories = [
diff --git a/source/blender/blenkernel/BKE_anim_data.h b/source/blender/blenkernel/BKE_anim_data.h
index 189e45f4fe5..8507793b1dc 100644
--- a/source/blender/blenkernel/BKE_anim_data.h
+++ b/source/blender/blenkernel/BKE_anim_data.h
@@ -35,6 +35,10 @@ struct LibraryForeachIDData;
struct Main;
struct ReportList;
struct bAction;
+struct BlendWriter;
+struct BlendDataReader;
+struct BlendLibReader;
+struct BlendExpander;
/* ************************************* */
/* AnimData API */
@@ -94,6 +98,13 @@ void BKE_animdata_merge_copy(struct Main *bmain,
eAnimData_MergeCopy_Modes action_mode,
bool fix_drivers);
+void BKE_animdata_blend_write(struct BlendWriter *writer, struct AnimData *adt);
+void BKE_animdata_blend_read_data(struct BlendDataReader *reader, struct AnimData *adt);
+void BKE_animdata_blend_read_lib(struct BlendLibReader *reader,
+ struct ID *id,
+ struct AnimData *adt);
+void BKE_animdata_blend_read_expand(struct BlendExpander *expander, struct AnimData *adt);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h
index 231cd0e53c5..5ad903a0119 100644
--- a/source/blender/blenkernel/BKE_blender_version.h
+++ b/source/blender/blenkernel/BKE_blender_version.h
@@ -39,7 +39,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
-#define BLENDER_FILE_SUBVERSION 0
+#define BLENDER_FILE_SUBVERSION 1
/* 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_customdata.h b/source/blender/blenkernel/BKE_customdata.h
index 92bfa3a9490..25360d4b3fa 100644
--- a/source/blender/blenkernel/BKE_customdata.h
+++ b/source/blender/blenkernel/BKE_customdata.h
@@ -37,6 +37,8 @@ struct BMesh;
struct CustomData;
struct CustomData_MeshMasks;
struct ID;
+struct BlendWriter;
+struct BlendDataReader;
typedef uint64_t CustomDataMask;
/*a data type large enough to hold 1 element from any customdata layer type*/
@@ -571,6 +573,14 @@ typedef struct CustomDataTransferLayerMap {
void CustomData_data_transfer(const struct MeshPairRemap *me_remap,
const CustomDataTransferLayerMap *laymap);
+/* .blend file I/O */
+void CustomData_blend_write(struct BlendWriter *writer,
+ struct CustomData *data,
+ int count,
+ CustomDataMask cddata_mask,
+ struct ID *id);
+void CustomData_blend_read(struct BlendDataReader *reader, struct CustomData *data, int count);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/BKE_deform.h b/source/blender/blenkernel/BKE_deform.h
index 35111d5240e..a9f81676a7a 100644
--- a/source/blender/blenkernel/BKE_deform.h
+++ b/source/blender/blenkernel/BKE_deform.h
@@ -35,6 +35,8 @@ struct MLoop;
struct MPoly;
struct Object;
struct bDeformGroup;
+struct BlendWriter;
+struct BlendDataReader;
struct bDeformGroup *BKE_object_defgroup_new(struct Object *ob, const char *name);
void BKE_defgroup_copy_list(struct ListBase *lb1, const struct ListBase *lb2);
@@ -162,6 +164,11 @@ void BKE_defvert_extract_vgroup_to_polyweights(struct MDeformVert *dvert,
void BKE_defvert_weight_to_rgb(float r_rgb[3], const float weight);
+void BKE_defvert_blend_write(struct BlendWriter *writer, int count, struct MDeformVert *dvlist);
+void BKE_defvert_blend_read(struct BlendDataReader *reader,
+ int count,
+ struct MDeformVert *mdverts);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/BKE_fcurve.h b/source/blender/blenkernel/BKE_fcurve.h
index 3717eb0f282..b316fc28726 100644
--- a/source/blender/blenkernel/BKE_fcurve.h
+++ b/source/blender/blenkernel/BKE_fcurve.h
@@ -44,6 +44,10 @@ struct PropertyRNA;
struct StructRNA;
struct bAction;
struct bContext;
+struct BlendWriter;
+struct BlendDataReader;
+struct BlendLibReader;
+struct BlendExpander;
/* ************** Keyframe Tools ***************** */
@@ -311,6 +315,24 @@ float fcurve_samplingcb_evalcurve(struct FCurve *fcu, void *data, float evaltime
void fcurve_store_samples(
struct FCurve *fcu, void *data, int start, int end, FcuSampleFunc sample_cb);
+/* ************* F-Curve .blend file API ******************** */
+
+void BKE_fmodifiers_blend_write(struct BlendWriter *writer, struct ListBase *fmodifiers);
+void BKE_fmodifiers_blend_read_data(struct BlendDataReader *reader,
+ ListBase *fmodifiers,
+ struct FCurve *curve);
+void BKE_fmodifiers_blend_read_lib(struct BlendLibReader *reader,
+ struct ID *id,
+ struct ListBase *fmodifiers);
+void BKE_fmodifiers_blend_read_expand(struct BlendExpander *expander, struct ListBase *fmodifiers);
+
+void BKE_fcurve_blend_write(struct BlendWriter *writer, struct ListBase *fcurves);
+void BKE_fcurve_blend_read_data(struct BlendDataReader *reader, struct ListBase *fcurves);
+void BKE_fcurve_blend_read_lib(struct BlendLibReader *reader,
+ struct ID *id,
+ struct ListBase *fcurves);
+void BKE_fcurve_blend_read_expand(struct BlendExpander *expander, struct ListBase *fcurves);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/BKE_global.h b/source/blender/blenkernel/BKE_global.h
index 9ffd496616d..5e16c9c979c 100644
--- a/source/blender/blenkernel/BKE_global.h
+++ b/source/blender/blenkernel/BKE_global.h
@@ -71,7 +71,6 @@ typedef struct Global {
* * -1: Disable faster motion paths computation (since 08/2018).
* * 1 - 30: EEVEE debug/stats values (01/2018).
* * 101: Enable UI debug drawing of fullscreen area's corner widget (10/2014).
- * * 527: Old mysterious switch in behavior of MeshDeform modifier (before 04/2010).
* * 666: Use quicker batch delete for outliners' delete hierarchy (01/2019).
* * 777: Enable UI node panel's sockets polling (11/2011).
* * 799: Enable some mysterious new depsgraph behavior (05/2015).
diff --git a/source/blender/blenkernel/BKE_idprop.h b/source/blender/blenkernel/BKE_idprop.h
index 58629cde6ec..e7f9ad207a1 100644
--- a/source/blender/blenkernel/BKE_idprop.h
+++ b/source/blender/blenkernel/BKE_idprop.h
@@ -30,6 +30,10 @@ extern "C" {
struct ID;
struct IDProperty;
+struct BlendWriter;
+struct BlendDataReader;
+struct BlendLibReader;
+struct BlendExpander;
typedef union IDPropertyTemplate {
int i;
@@ -196,6 +200,14 @@ void IDP_repr_fn(const IDProperty *prop,
void *user_data);
void IDP_print(const struct IDProperty *prop);
+void IDP_BlendWrite(struct BlendWriter *writer, const struct IDProperty *prop);
+void IDP_BlendReadData_impl(struct BlendDataReader *reader,
+ IDProperty **prop,
+ const char *caller_func_id);
+#define IDP_BlendDataRead(reader, prop) IDP_BlendReadData_impl(reader, prop, __func__)
+void IDP_BlendReadLib(struct BlendLibReader *reader, IDProperty *prop);
+void IDP_BlendReadExpand(struct BlendExpander *expander, IDProperty *prop);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/BKE_idtype.h b/source/blender/blenkernel/BKE_idtype.h
index aefba9ef02a..57b005d1a1e 100644
--- a/source/blender/blenkernel/BKE_idtype.h
+++ b/source/blender/blenkernel/BKE_idtype.h
@@ -34,6 +34,10 @@ extern "C" {
struct ID;
struct LibraryForeachIDData;
struct Main;
+struct BlendWriter;
+struct BlendDataReader;
+struct BlendLibReader;
+struct BlendExpander;
/** IDTypeInfo.flags. */
enum {
@@ -89,6 +93,13 @@ typedef void (*IDTypeForeachCacheFunction)(struct ID *id,
IDTypeForeachCacheFunctionCallback function_callback,
void *user_data);
+typedef void (*IDTypeBlendWriteFunction)(struct BlendWriter *writer,
+ struct ID *id,
+ const void *id_address);
+typedef void (*IDTypeBlendReadDataFunction)(struct BlendDataReader *reader, struct ID *id);
+typedef void (*IDTypeBlendReadLibFunction)(struct BlendLibReader *reader, struct ID *id);
+typedef void (*IDTypeBlendReadExpandFunction)(struct BlendExpander *expander, struct ID *id);
+
typedef struct IDTypeInfo {
/* ********** General IDType data. ********** */
@@ -161,6 +172,26 @@ typedef struct IDTypeInfo {
* Iterator over all cache pointers of given ID.
*/
IDTypeForeachCacheFunction foreach_cache;
+
+ /**
+ * Write all structs that should be saved in a .blend file.
+ */
+ IDTypeBlendWriteFunction blend_write;
+
+ /**
+ * Update pointers for all structs directly owned by this data block.
+ */
+ IDTypeBlendReadDataFunction blend_read_data;
+
+ /**
+ * Update pointers to other id data blocks.
+ */
+ IDTypeBlendReadLibFunction blend_read_lib;
+
+ /**
+ * Specify which other id data blocks should be loaded when the current one is loaded.
+ */
+ IDTypeBlendReadExpandFunction blend_read_expand;
} IDTypeInfo;
/* ********** Declaration of each IDTypeInfo. ********** */
diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h
index a2cbe537349..7768e903ac3 100644
--- a/source/blender/blenkernel/BKE_lib_id.h
+++ b/source/blender/blenkernel/BKE_lib_id.h
@@ -59,6 +59,8 @@ struct Main;
struct PointerRNA;
struct PropertyRNA;
struct bContext;
+struct BlendWriter;
+struct BlendDataReader;
size_t BKE_libblock_get_alloc_info(short type, const char **name);
void *BKE_libblock_alloc_notest(short type) ATTR_WARN_UNUSED_RESULT;
@@ -285,6 +287,8 @@ bool BKE_id_is_in_global_main(struct ID *id);
void BKE_id_ordered_list(struct ListBase *ordered_lb, const struct ListBase *lb);
void BKE_id_reorder(const struct ListBase *lb, struct ID *id, struct ID *relative, bool after);
+void BKE_id_blend_write(struct BlendWriter *writer, struct ID *id);
+
#define IS_TAGGED(_id) ((_id) && (((ID *)_id)->tag & LIB_TAG_DOIT))
#ifdef __cplusplus
diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h
index 5843992b25c..9a5700d2fbd 100644
--- a/source/blender/blenkernel/BKE_lib_override.h
+++ b/source/blender/blenkernel/BKE_lib_override.h
@@ -68,11 +68,20 @@ void BKE_lib_override_library_dependencies_tag(struct Main *bmain,
struct ID *id_root,
const uint tag,
const bool do_create_main_relashionships);
+void BKE_lib_override_library_override_group_tag(struct Main *bmain,
+ struct ID *id_root,
+ const uint tag,
+ const bool do_create_main_relashionships);
bool BKE_lib_override_library_create(struct Main *bmain,
struct Scene *scene,
struct ViewLayer *view_layer,
struct ID *id_root,
struct ID *id_reference);
+bool BKE_lib_override_library_resync(struct Main *bmain,
+ struct Scene *scene,
+ struct ViewLayer *view_layer,
+ struct ID *id_root);
+void BKE_lib_override_library_delete(struct Main *bmain, struct ID *id_root);
struct IDOverrideLibraryProperty *BKE_lib_override_library_property_find(
struct IDOverrideLibrary *override, const char *rna_path);
diff --git a/source/blender/blenkernel/BKE_mesh_mapping.h b/source/blender/blenkernel/BKE_mesh_mapping.h
index 140a60dbdb7..cbd992bbbec 100644
--- a/source/blender/blenkernel/BKE_mesh_mapping.h
+++ b/source/blender/blenkernel/BKE_mesh_mapping.h
@@ -48,9 +48,6 @@ typedef struct UvMapVert {
unsigned int poly_index;
unsigned short loop_of_poly_index;
bool separate;
- /* Zero-ed by map creation, left for use by specific areas. Is not
- * initialized to anything. */
- unsigned char flag;
} UvMapVert;
/* UvElement stores per uv information so that we can quickly access information for a uv.
diff --git a/source/blender/blenkernel/BKE_mesh_remesh_voxel.h b/source/blender/blenkernel/BKE_mesh_remesh_voxel.h
index 277747e3e27..4db933fdc09 100644
--- a/source/blender/blenkernel/BKE_mesh_remesh_voxel.h
+++ b/source/blender/blenkernel/BKE_mesh_remesh_voxel.h
@@ -59,7 +59,10 @@ struct Mesh *BKE_mesh_remesh_quadriflow_to_mesh_nomain(struct Mesh *mesh,
struct Mesh *BKE_mesh_remesh_tetgen_to_mesh_nomain(struct Mesh *mesh,
unsigned int **tets,
int *numtets);
-
+struct Mesh *BKE_mesh_remesh_tetlattice_to_mesh_nomain(struct Mesh *mesh,
+ int subdivisions,
+ unsigned int **tets,
+ int *numtets);
/* Data reprojection functions */
void BKE_mesh_remesh_reproject_paint_mask(struct Mesh *target, struct Mesh *source);
void BKE_remesh_reproject_vertex_paint(struct Mesh *target, struct Mesh *source);
diff --git a/source/blender/blenkernel/BKE_mesh_runtime.h b/source/blender/blenkernel/BKE_mesh_runtime.h
index 267be4f44fd..87b55c581a2 100644
--- a/source/blender/blenkernel/BKE_mesh_runtime.h
+++ b/source/blender/blenkernel/BKE_mesh_runtime.h
@@ -72,9 +72,9 @@ struct Mesh *mesh_get_eval_deform(struct Depsgraph *depsgraph,
const struct CustomData_MeshMasks *dataMask);
struct Mesh *mesh_create_eval_final(struct Depsgraph *depsgraph,
- struct Scene *scene,
- struct Object *ob,
- const struct CustomData_MeshMasks *dataMask);
+ struct Scene *scene,
+ struct Object *ob,
+ const struct CustomData_MeshMasks *dataMask);
struct Mesh *mesh_create_eval_final_index_render(struct Depsgraph *depsgraph,
struct Scene *scene,
diff --git a/source/blender/blenkernel/BKE_nla.h b/source/blender/blenkernel/BKE_nla.h
index 8b3231e5302..4cded6b9d6a 100644
--- a/source/blender/blenkernel/BKE_nla.h
+++ b/source/blender/blenkernel/BKE_nla.h
@@ -38,6 +38,10 @@ struct bAction;
struct PointerRNA;
struct PropertyRNA;
+struct BlendWriter;
+struct BlendDataReader;
+struct BlendLibReader;
+struct BlendExpander;
/* ----------------------------- */
/* Data Management */
@@ -102,6 +106,7 @@ void BKE_nlastrip_set_active(struct AnimData *adt, struct NlaStrip *strip);
bool BKE_nlastrip_within_bounds(struct NlaStrip *strip, float min, float max);
void BKE_nlastrip_recalculate_bounds(struct NlaStrip *strip);
+void BKE_nlastrip_recalculate_bounds_sync_action(struct NlaStrip *strip);
void BKE_nlastrip_validate_name(struct AnimData *adt, struct NlaStrip *strip);
@@ -145,6 +150,14 @@ enum eNlaTime_ConvertModes {
float BKE_nla_tweakedit_remap(struct AnimData *adt, float cframe, short mode);
+/* ----------------------------- */
+/* .blend file API */
+
+void BKE_nla_blend_write(struct BlendWriter *writer, struct ListBase *tracks);
+void BKE_nla_blend_read_data(struct BlendDataReader *reader, struct ListBase *tracks);
+void BKE_nla_blend_read_lib(struct BlendLibReader *reader, struct ID *id, struct ListBase *tracks);
+void BKE_nla_blend_read_expand(struct BlendExpander *expander, struct ListBase *tracks);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h
index b2794e9d9d6..dfc75e2fd54 100644
--- a/source/blender/blenkernel/BKE_paint.h
+++ b/source/blender/blenkernel/BKE_paint.h
@@ -259,6 +259,16 @@ typedef struct SculptPoseIKChain {
/* Cloth Brush */
+typedef enum eSculptClothConstraintType {
+ /* Constraint that creates the structure of the cloth. */
+ SCULPT_CLOTH_CONSTRAINT_STRUCTURAL = 0,
+ /* Constraint that references the position of a vertex and a position in deformation_pos which
+ can be deformed by the tools. */
+ SCULPT_CLOTH_CONSTRAINT_DEFORMATION = 1,
+ /* Constarint that references the vertex position and its initial position. */
+ SCULPT_CLOTH_CONSTRAINT_SOFTBODY = 2,
+} eSculptClothConstraintType;
+
typedef struct SculptClothLengthConstraint {
/* Elements that are affected by the constraint. */
/* Element a should always be a mesh vertex with the index stored in elem_index_a as it is always
@@ -274,6 +284,8 @@ typedef struct SculptClothLengthConstraint {
float length;
float strength;
+
+ eSculptClothConstraintType type;
} SculptClothLengthConstraint;
typedef struct SculptClothSimulation {
@@ -287,6 +299,7 @@ typedef struct SculptClothSimulation {
* final positions of the simulated vertices are updated with constraints that use these points
* as targets. */
float (*deformation_pos)[3];
+ float *deformation_strength;
float mass;
float damping;
diff --git a/source/blender/blenkernel/BKE_rigidbody.h b/source/blender/blenkernel/BKE_rigidbody.h
index c2059144388..ae1e437cd60 100644
--- a/source/blender/blenkernel/BKE_rigidbody.h
+++ b/source/blender/blenkernel/BKE_rigidbody.h
@@ -143,6 +143,7 @@ void BKE_rigidbody_aftertrans_update(struct Object *ob,
float rotAngle);
void BKE_rigidbody_sync_transforms(struct RigidBodyWorld *rbw, struct Object *ob, float ctime);
bool BKE_rigidbody_check_sim_running(struct RigidBodyWorld *rbw, float ctime);
+bool BKE_rigidbody_is_affected_by_simulation(struct Object *ob);
void BKE_rigidbody_cache_reset(struct RigidBodyWorld *rbw);
void BKE_rigidbody_rebuild_world(struct Depsgraph *depsgraph, struct Scene *scene, float ctime);
void BKE_rigidbody_do_simulation(struct Depsgraph *depsgraph, struct Scene *scene, float ctime);
diff --git a/source/blender/blenkernel/BKE_scene.h b/source/blender/blenkernel/BKE_scene.h
index 3ab923f05f6..05966ec3989 100644
--- a/source/blender/blenkernel/BKE_scene.h
+++ b/source/blender/blenkernel/BKE_scene.h
@@ -110,8 +110,7 @@ void BKE_toolsettings_free(struct ToolSettings *toolsettings);
struct Scene *BKE_scene_duplicate(struct Main *bmain, struct Scene *sce, eSceneCopyMethod type);
void BKE_scene_groups_relink(struct Scene *sce);
-struct Scene *BKE_scene_find_from_view_layer(const struct Main *bmain,
- const struct ViewLayer *layer);
+bool BKE_scene_has_view_layer(const struct Scene *scene, const struct ViewLayer *layer);
struct Scene *BKE_scene_find_from_collection(const struct Main *bmain,
const struct Collection *collection);
@@ -220,10 +219,12 @@ void BKE_scene_ensure_depsgraph_hash(struct Scene *scene);
void BKE_scene_free_depsgraph_hash(struct Scene *scene);
void BKE_scene_free_view_layer_depsgraph(struct Scene *scene, struct ViewLayer *view_layer);
-struct Depsgraph *BKE_scene_get_depsgraph(struct Main *bmain,
- struct Scene *scene,
- struct ViewLayer *view_layer,
- bool allocate);
+/* Do not allocate new depsgraph. */
+struct Depsgraph *BKE_scene_get_depsgraph(struct Scene *scene, struct ViewLayer *view_layer);
+/* Allocate new depsgraph if necessary. */
+struct Depsgraph *BKE_scene_ensure_depsgraph(struct Main *bmain,
+ struct Scene *scene,
+ struct ViewLayer *view_layer);
struct GHash *BKE_scene_undo_depsgraphs_extract(struct Main *bmain);
void BKE_scene_undo_depsgraphs_restore(struct Main *bmain, struct GHash *depsgraph_extract);
diff --git a/source/blender/blenkernel/BKE_sequencer.h b/source/blender/blenkernel/BKE_sequencer.h
index b6d901c8ef2..fd881175872 100644
--- a/source/blender/blenkernel/BKE_sequencer.h
+++ b/source/blender/blenkernel/BKE_sequencer.h
@@ -62,30 +62,34 @@ typedef struct SeqIterator {
int valid;
} SeqIterator;
-void BKE_sequence_iterator_begin(struct Editing *ed, SeqIterator *iter, bool use_pointer);
+void BKE_sequence_iterator_begin(struct Editing *ed,
+ SeqIterator *iter,
+ const bool use_current_sequences);
void BKE_sequence_iterator_next(SeqIterator *iter);
void BKE_sequence_iterator_end(SeqIterator *iter);
-#define SEQP_BEGIN(_ed, _seq) \
- { \
- SeqIterator iter_macro; \
- for (BKE_sequence_iterator_begin(_ed, &iter_macro, true); iter_macro.valid; \
- BKE_sequence_iterator_next(&iter_macro)) { \
- _seq = iter_macro.seq;
-
-#define SEQ_BEGIN(ed, _seq) \
+#define SEQ_ALL_BEGIN(ed, _seq) \
{ \
SeqIterator iter_macro; \
for (BKE_sequence_iterator_begin(ed, &iter_macro, false); iter_macro.valid; \
BKE_sequence_iterator_next(&iter_macro)) { \
_seq = iter_macro.seq;
-#define SEQ_END \
+#define SEQ_ALL_END \
} \
BKE_sequence_iterator_end(&iter_macro); \
} \
((void)0)
+#define SEQ_CURRENT_BEGIN(_ed, _seq) \
+ { \
+ SeqIterator iter_macro; \
+ for (BKE_sequence_iterator_begin(_ed, &iter_macro, true); iter_macro.valid; \
+ BKE_sequence_iterator_next(&iter_macro)) { \
+ _seq = iter_macro.seq;
+
+#define SEQ_CURRENT_END SEQ_ALL_END
+
typedef enum eSeqTaskId {
SEQ_TASK_MAIN_RENDER,
SEQ_TASK_PREFETCH_RENDER,
diff --git a/source/blender/blenkernel/BKE_softbody.h b/source/blender/blenkernel/BKE_softbody.h
index fb377302c7a..f0bc521664f 100644
--- a/source/blender/blenkernel/BKE_softbody.h
+++ b/source/blender/blenkernel/BKE_softbody.h
@@ -50,8 +50,11 @@ typedef struct BodyPoint {
/* allocates and initializes general main data */
extern struct SoftBody *sbNew(struct Scene *scene);
-/* reads custom structs for file i/o */
-extern void sbCustomRead(struct Object *ob);
+/* copies external solver data from src to dest */
+extern void sbExternalCopy(struct Object *dest, struct Object *src);
+
+/* initializes settings for external solvers */
+extern void sbExternalSetDefault(struct SoftBody *sb);
/* frees internal data and softbody itself */
extern void sbFree(struct Object *ob);
diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c
index 4f587abd9f0..263f63cb6da 100644
--- a/source/blender/blenkernel/intern/DerivedMesh.c
+++ b/source/blender/blenkernel/intern/DerivedMesh.c
@@ -1991,9 +1991,9 @@ Mesh *mesh_get_eval_deform(struct Depsgraph *depsgraph,
}
Mesh *mesh_create_eval_final(Depsgraph *depsgraph,
- Scene *scene,
- Object *ob,
- const CustomData_MeshMasks *dataMask)
+ Scene *scene,
+ Object *ob,
+ const CustomData_MeshMasks *dataMask)
{
Mesh *final;
diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c
index 85ac2c693cb..089d8bef09e 100644
--- a/source/blender/blenkernel/intern/action.c
+++ b/source/blender/blenkernel/intern/action.c
@@ -184,6 +184,12 @@ IDTypeInfo IDType_ID_AC = {
.free_data = action_free_data,
.make_local = NULL,
.foreach_id = action_foreach_id,
+ .foreach_cache = NULL,
+
+ .blend_write = NULL,
+ .blend_read_data = NULL,
+ .blend_read_lib = NULL,
+ .blend_read_expand = NULL,
};
/* ***************** Library data level operations on action ************** */
diff --git a/source/blender/blenkernel/intern/anim_data.c b/source/blender/blenkernel/intern/anim_data.c
index 038a0d14ddb..5ce449c5000 100644
--- a/source/blender/blenkernel/intern/anim_data.c
+++ b/source/blender/blenkernel/intern/anim_data.c
@@ -54,6 +54,8 @@
#include "DEG_depsgraph.h"
+#include "BLO_read_write.h"
+
#include "RNA_access.h"
#include "CLG_log.h"
@@ -1497,3 +1499,85 @@ void BKE_animdata_fix_paths_rename_all(ID *ref_id,
/* scenes */
RENAMEFIX_ANIM_NODETREE_IDS(bmain->scenes.first, Scene);
}
+
+/* .blend file API -------------------------------------------- */
+
+void BKE_animdata_blend_write(BlendWriter *writer, struct AnimData *adt)
+{
+ /* firstly, just write the AnimData block */
+ BLO_write_struct(writer, AnimData, adt);
+
+ /* write drivers */
+ BKE_fcurve_blend_write(writer, &adt->drivers);
+
+ /* write overrides */
+ // FIXME: are these needed?
+ LISTBASE_FOREACH (AnimOverride *, aor, &adt->overrides) {
+ /* overrides consist of base data + rna_path */
+ BLO_write_struct(writer, AnimOverride, aor);
+ BLO_write_string(writer, aor->rna_path);
+ }
+
+ // TODO write the remaps (if they are needed)
+
+ /* write NLA data */
+ BKE_nla_blend_write(writer, &adt->nla_tracks);
+}
+
+void BKE_animdata_blend_read_data(BlendDataReader *reader, AnimData *adt)
+{
+ /* NOTE: must have called BLO_read_data_address already before doing this... */
+ if (adt == NULL) {
+ return;
+ }
+
+ /* link drivers */
+ BLO_read_list(reader, &adt->drivers);
+ BKE_fcurve_blend_read_data(reader, &adt->drivers);
+ adt->driver_array = NULL;
+
+ /* link overrides */
+ // TODO...
+
+ /* link NLA-data */
+ BLO_read_list(reader, &adt->nla_tracks);
+ BKE_nla_blend_read_data(reader, &adt->nla_tracks);
+
+ /* relink active track/strip - even though strictly speaking this should only be used
+ * if we're in 'tweaking mode', we need to be able to have this loaded back for
+ * undo, but also since users may not exit tweakmode before saving (#24535)
+ */
+ // TODO: it's not really nice that anyone should be able to save the file in this
+ // state, but it's going to be too hard to enforce this single case...
+ BLO_read_data_address(reader, &adt->act_track);
+ BLO_read_data_address(reader, &adt->actstrip);
+}
+
+void BKE_animdata_blend_read_lib(BlendLibReader *reader, ID *id, AnimData *adt)
+{
+ if (adt == NULL) {
+ return;
+ }
+
+ /* link action data */
+ BLO_read_id_address(reader, id->lib, &adt->action);
+ BLO_read_id_address(reader, id->lib, &adt->tmpact);
+
+ /* link drivers */
+ BKE_fcurve_blend_read_lib(reader, id, &adt->drivers);
+
+ /* overrides don't have lib-link for now, so no need to do anything */
+
+ /* link NLA-data */
+ BKE_nla_blend_read_lib(reader, id, &adt->nla_tracks);
+}
+
+void BKE_animdata_blend_read_expand(struct BlendExpander *expander, AnimData *adt)
+{
+ /* own action */
+ BLO_expand(expander, adt->action);
+ BLO_expand(expander, adt->tmpact);
+
+ /* drivers - assume that these F-Curves have driver data to be in this list... */
+ BKE_fcurve_blend_read_expand(expander, &adt->drivers);
+}
diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c
index 5b5e32f1d81..69e70cffdb2 100644
--- a/source/blender/blenkernel/intern/anim_sys.c
+++ b/source/blender/blenkernel/intern/anim_sys.c
@@ -2192,7 +2192,15 @@ static bool animsys_evaluate_nla(NlaEvalData *echannels,
if (is_inplace_tweak) {
/* edit active action in-place according to its active strip, so copy the data */
memcpy(dummy_strip, adt->actstrip, sizeof(NlaStrip));
+ /* Prevents nla eval from considering active strip's adj strips.
+ * For user, this means entering tweak mode on a strip ignores evaluating adjacent strips
+ * in the same track. */
dummy_strip->next = dummy_strip->prev = NULL;
+
+ /* If tweaked strip is syncing action length, then evaluate using action length. */
+ if (dummy_strip->flag & NLASTRIP_FLAG_SYNC_LENGTH) {
+ BKE_nlastrip_recalculate_bounds_sync_action(dummy_strip);
+ }
}
else {
/* set settings of dummy NLA strip from AnimData settings */
@@ -2237,9 +2245,11 @@ static bool animsys_evaluate_nla(NlaEvalData *echannels,
/* If computing the context for keyframing, store data there instead of the list. */
else {
/* The extend mode here effectively controls
- * whether it is possible to key-frame beyond the ends. */
- dummy_strip->extendmode = is_inplace_tweak ? NLASTRIP_EXTEND_NOTHING :
- NLASTRIP_EXTEND_HOLD;
+ * whether it is possible to key-frame beyond the ends.*/
+ dummy_strip->extendmode = (is_inplace_tweak &&
+ !(dummy_strip->flag & NLASTRIP_FLAG_SYNC_LENGTH)) ?
+ NLASTRIP_EXTEND_NOTHING :
+ NLASTRIP_EXTEND_HOLD;
r_context->eval_strip = nes = nlastrips_ctime_get_strip(
NULL, &dummy_trackslist, -1, anim_eval_context, flush_to_original);
@@ -2860,7 +2870,7 @@ void BKE_animsys_eval_driver(Depsgraph *depsgraph, ID *id, int driver_index, FCu
/* set error-flag if evaluation failed */
if (ok == 0) {
- CLOG_ERROR(&LOG, "invalid driver - %s[%d]", fcu->rna_path, fcu->array_index);
+ CLOG_WARN(&LOG, "invalid driver - %s[%d]", fcu->rna_path, fcu->array_index);
driver_orig->flag |= DRIVER_FLAG_INVALID;
}
}
diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c
index 631ce4edd20..3b73702cf0f 100644
--- a/source/blender/blenkernel/intern/armature.c
+++ b/source/blender/blenkernel/intern/armature.c
@@ -180,6 +180,12 @@ IDTypeInfo IDType_ID_AR = {
.free_data = armature_free_data,
.make_local = NULL,
.foreach_id = armature_foreach_id,
+ .foreach_cache = NULL,
+
+ .blend_write = NULL,
+ .blend_read_data = NULL,
+ .blend_read_lib = NULL,
+ .blend_read_expand = NULL,
};
/** \} */
diff --git a/source/blender/blenkernel/intern/bpath.c b/source/blender/blenkernel/intern/bpath.c
index 1833ad5a748..4ab8ea5a647 100644
--- a/source/blender/blenkernel/intern/bpath.c
+++ b/source/blender/blenkernel/intern/bpath.c
@@ -703,7 +703,7 @@ void BKE_bpath_traverse_id(
if (scene->ed) {
Sequence *seq;
- SEQ_BEGIN (scene->ed, seq) {
+ SEQ_ALL_BEGIN (scene->ed, seq) {
if (SEQ_HAS_PATH(seq)) {
StripElem *se = seq->strip->stripdata;
@@ -732,7 +732,7 @@ void BKE_bpath_traverse_id(
}
}
}
- SEQ_END;
+ SEQ_ALL_END;
}
break;
}
diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c
index 8cd30c2241f..0749f29879c 100644
--- a/source/blender/blenkernel/intern/brush.c
+++ b/source/blender/blenkernel/intern/brush.c
@@ -210,6 +210,12 @@ IDTypeInfo IDType_ID_BR = {
.free_data = brush_free_data,
.make_local = brush_make_local,
.foreach_id = brush_foreach_id,
+ .foreach_cache = NULL,
+
+ .blend_write = NULL,
+ .blend_read_data = NULL,
+ .blend_read_lib = NULL,
+ .blend_read_expand = NULL,
};
static RNG *brush_rng;
@@ -1539,7 +1545,6 @@ void BKE_brush_sculpt_reset(Brush *br)
break;
case SCULPT_TOOL_SMOOTH:
br->flag &= ~BRUSH_SPACE_ATTEN;
- br->automasking_flags |= BRUSH_AUTOMASKING_BOUNDARY_EDGES;
br->spacing = 5;
br->alpha = 0.7f;
br->surface_smooth_shape_preservation = 0.5f;
diff --git a/source/blender/blenkernel/intern/cachefile.c b/source/blender/blenkernel/intern/cachefile.c
index 9ad6ae84c5c..f3386df03c8 100644
--- a/source/blender/blenkernel/intern/cachefile.c
+++ b/source/blender/blenkernel/intern/cachefile.c
@@ -100,6 +100,12 @@ IDTypeInfo IDType_ID_CF = {
.free_data = cache_file_free_data,
.make_local = NULL,
.foreach_id = NULL,
+ .foreach_cache = NULL,
+
+ .blend_write = NULL,
+ .blend_read_data = NULL,
+ .blend_read_lib = NULL,
+ .blend_read_expand = NULL,
};
/* TODO: make this per cache file to avoid global locks. */
diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c
index d8b4150b2b1..0c0ad7a57ab 100644
--- a/source/blender/blenkernel/intern/camera.c
+++ b/source/blender/blenkernel/intern/camera.c
@@ -128,6 +128,12 @@ IDTypeInfo IDType_ID_CA = {
.free_data = camera_free_data,
.make_local = camera_make_local,
.foreach_id = camera_foreach_id,
+ .foreach_cache = NULL,
+
+ .blend_write = NULL,
+ .blend_read_data = NULL,
+ .blend_read_lib = NULL,
+ .blend_read_expand = NULL,
};
/** \} */
diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c
index 0d65ee5faa3..8975be2b618 100644
--- a/source/blender/blenkernel/intern/collection.c
+++ b/source/blender/blenkernel/intern/collection.c
@@ -173,6 +173,12 @@ IDTypeInfo IDType_ID_GR = {
.free_data = collection_free_data,
.make_local = NULL,
.foreach_id = collection_foreach_id,
+ .foreach_cache = NULL,
+
+ .blend_write = NULL,
+ .blend_read_data = NULL,
+ .blend_read_lib = NULL,
+ .blend_read_expand = NULL,
};
/** \} */
@@ -408,18 +414,28 @@ static Collection *collection_duplicate_recursive(Main *bmain,
}
if (do_objects) {
+ /* We need to first duplicate the objects in a separate loop, to support the master collection
+ * case, where both old and new collections are the same.
+ * Otherwise, depending on naming scheme and sorting, we may end up duplicating the new objects
+ * we just added, in some infinite loop. */
+ LISTBASE_FOREACH (CollectionObject *, cob, &collection_old->gobject) {
+ Object *ob_old = cob->ob;
+
+ if (ob_old->id.newid == NULL) {
+ BKE_object_duplicate(
+ bmain, ob_old, duplicate_flags, duplicate_options | LIB_ID_DUPLICATE_IS_SUBPROCESS);
+ }
+ }
+
/* We can loop on collection_old's objects, but have to consider it mutable because with master
* collections collection_old and collection_new are the same data here. */
LISTBASE_FOREACH_MUTABLE (CollectionObject *, cob, &collection_old->gobject) {
Object *ob_old = cob->ob;
Object *ob_new = (Object *)ob_old->id.newid;
- if (ob_new == NULL) {
- ob_new = BKE_object_duplicate(
- bmain, ob_old, duplicate_flags, duplicate_options | LIB_ID_DUPLICATE_IS_SUBPROCESS);
- }
-
- if (ob_new == ob_old) {
+ /* New object can be NULL in master collection case, since new and old objects are in same
+ * collection. */
+ if (ELEM(ob_new, ob_old, NULL)) {
continue;
}
diff --git a/source/blender/blenkernel/intern/collision.c b/source/blender/blenkernel/intern/collision.c
index 05c521e3b94..115980d577e 100644
--- a/source/blender/blenkernel/intern/collision.c
+++ b/source/blender/blenkernel/intern/collision.c
@@ -647,9 +647,9 @@ DO_INLINE void collision_interpolateOnTriangle(float to[3],
VECADDMUL(to, v3, w3);
}
-static void cloth_selfcollision_impulse_vert(const float clamp_sq,
- const float impulse[3],
- struct ClothVertex *vert)
+static void cloth_collision_impulse_vert(const float clamp_sq,
+ const float impulse[3],
+ struct ClothVertex *vert)
{
float impulse_len_sq = len_squared_v3(impulse);
@@ -681,7 +681,7 @@ static int cloth_collision_response_static(ClothModifierData *clmd,
{
int result = 0;
Cloth *cloth = clmd->clothObject;
- const float clamp_sq = square_f(clmd->coll_parms->self_clamp * dt);
+ const float clamp_sq = square_f(clmd->coll_parms->clamp * dt);
const float time_multiplier = 1.0f / (clmd->sim_parms->dt * clmd->sim_parms->timescale);
const float epsilon2 = BLI_bvhtree_get_epsilon(collmd->bvhtree);
const float min_distance = (clmd->coll_parms->epsilon + epsilon2) * (8.0f / 9.0f);
@@ -828,10 +828,10 @@ static int cloth_collision_response_static(ClothModifierData *clmd,
}
if (result) {
- cloth_selfcollision_impulse_vert(clamp_sq, i1, &cloth->verts[collpair->ap1]);
- cloth_selfcollision_impulse_vert(clamp_sq, i2, &cloth->verts[collpair->ap2]);
+ cloth_collision_impulse_vert(clamp_sq, i1, &cloth->verts[collpair->ap1]);
+ cloth_collision_impulse_vert(clamp_sq, i2, &cloth->verts[collpair->ap2]);
if (!is_hair) {
- cloth_selfcollision_impulse_vert(clamp_sq, i3, &cloth->verts[collpair->ap3]);
+ cloth_collision_impulse_vert(clamp_sq, i3, &cloth->verts[collpair->ap3]);
}
}
}
@@ -987,13 +987,13 @@ static int cloth_selfcollision_response_static(ClothModifierData *clmd,
}
if (result) {
- cloth_selfcollision_impulse_vert(clamp_sq, ia[0], &cloth->verts[collpair->ap1]);
- cloth_selfcollision_impulse_vert(clamp_sq, ia[1], &cloth->verts[collpair->ap2]);
- cloth_selfcollision_impulse_vert(clamp_sq, ia[2], &cloth->verts[collpair->ap3]);
+ cloth_collision_impulse_vert(clamp_sq, ia[0], &cloth->verts[collpair->ap1]);
+ cloth_collision_impulse_vert(clamp_sq, ia[1], &cloth->verts[collpair->ap2]);
+ cloth_collision_impulse_vert(clamp_sq, ia[2], &cloth->verts[collpair->ap3]);
- cloth_selfcollision_impulse_vert(clamp_sq, ib[0], &cloth->verts[collpair->bp1]);
- cloth_selfcollision_impulse_vert(clamp_sq, ib[1], &cloth->verts[collpair->bp2]);
- cloth_selfcollision_impulse_vert(clamp_sq, ib[2], &cloth->verts[collpair->bp3]);
+ cloth_collision_impulse_vert(clamp_sq, ib[0], &cloth->verts[collpair->bp1]);
+ cloth_collision_impulse_vert(clamp_sq, ib[1], &cloth->verts[collpair->bp2]);
+ cloth_collision_impulse_vert(clamp_sq, ib[2], &cloth->verts[collpair->bp3]);
}
}
diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c
index e9ba3a5f873..dce14c4c082 100644
--- a/source/blender/blenkernel/intern/context.c
+++ b/source/blender/blenkernel/intern/context.c
@@ -1349,7 +1349,7 @@ Depsgraph *CTX_data_depsgraph_pointer(const bContext *C)
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, true);
+ Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer);
/* Dependency graph might have been just allocated, and hence it will not be marked.
* This confuses redo system due to the lack of flushing changes back to the original data.
* In the future we would need to check whether the CTX_wm_window(C) is in editing mode (as an
@@ -1377,8 +1377,7 @@ Depsgraph *CTX_data_ensure_evaluated_depsgraph(const bContext *C)
Depsgraph *CTX_data_depsgraph_on_load(const bContext *C)
{
- Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- return BKE_scene_get_depsgraph(bmain, scene, view_layer, false);
+ return BKE_scene_get_depsgraph(scene, view_layer);
}
diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c
index 45ca89ac47e..44d5bbfd710 100644
--- a/source/blender/blenkernel/intern/curve.c
+++ b/source/blender/blenkernel/intern/curve.c
@@ -146,6 +146,12 @@ IDTypeInfo IDType_ID_CU = {
.free_data = curve_free_data,
.make_local = NULL,
.foreach_id = curve_foreach_id,
+ .foreach_cache = NULL,
+
+ .blend_write = NULL,
+ .blend_read_data = NULL,
+ .blend_read_lib = NULL,
+ .blend_read_expand = NULL,
};
static int cu_isectLL(const float v1[3],
diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c
index f728436a759..b75efb6cdf1 100644
--- a/source/blender/blenkernel/intern/customdata.c
+++ b/source/blender/blenkernel/intern/customdata.c
@@ -35,6 +35,8 @@
#include "DNA_meshdata_types.h"
#include "DNA_pointcloud_types.h"
+#include "BLI_bitmap.h"
+#include "BLI_endian_switch.h"
#include "BLI_math.h"
#include "BLI_math_color_blend.h"
#include "BLI_mempool.h"
@@ -47,10 +49,14 @@
#include "BKE_customdata.h"
#include "BKE_customdata_file.h"
+#include "BKE_deform.h"
#include "BKE_main.h"
#include "BKE_mesh_mapping.h"
#include "BKE_mesh_remap.h"
#include "BKE_multires.h"
+#include "BKE_subsurf.h"
+
+#include "BLO_read_write.h"
#include "bmesh.h"
@@ -5155,3 +5161,181 @@ void CustomData_data_transfer(const MeshPairRemap *me_remap,
MEM_SAFE_FREE(tmp_data_src);
}
+
+static void write_mdisps(BlendWriter *writer, int count, MDisps *mdlist, int external)
+{
+ if (mdlist) {
+ BLO_write_struct_array(writer, MDisps, count, mdlist);
+ for (int i = 0; i < count; i++) {
+ MDisps *md = &mdlist[i];
+ if (md->disps) {
+ if (!external) {
+ BLO_write_float3_array(writer, md->totdisp, &md->disps[0][0]);
+ }
+ }
+
+ if (md->hidden) {
+ BLO_write_raw(writer, BLI_BITMAP_SIZE(md->totdisp), md->hidden);
+ }
+ }
+ }
+}
+
+static void write_grid_paint_mask(BlendWriter *writer, int count, GridPaintMask *grid_paint_mask)
+{
+ if (grid_paint_mask) {
+ BLO_write_struct_array(writer, GridPaintMask, count, grid_paint_mask);
+ for (int i = 0; i < count; i++) {
+ GridPaintMask *gpm = &grid_paint_mask[i];
+ if (gpm->data) {
+ const int gridsize = BKE_ccg_gridsize(gpm->level);
+ BLO_write_raw(writer, sizeof(*gpm->data) * gridsize * gridsize, gpm->data);
+ }
+ }
+ }
+}
+
+void CustomData_blend_write(
+ BlendWriter *writer, CustomData *data, int count, CustomDataMask cddata_mask, ID *id)
+{
+ CustomDataLayer *layers = NULL;
+ CustomDataLayer layers_buff[CD_TEMP_CHUNK_SIZE];
+ CustomData_file_write_prepare(data, &layers, layers_buff, ARRAY_SIZE(layers_buff));
+
+ /* write external customdata (not for undo) */
+ if (data->external && !BLO_write_is_undo(writer)) {
+ CustomData_external_write(data, id, cddata_mask, count, 0);
+ }
+
+ BLO_write_struct_array_at_address(writer, CustomDataLayer, data->totlayer, data->layers, layers);
+
+ for (int i = 0; i < data->totlayer; i++) {
+ CustomDataLayer *layer = &layers[i];
+
+ if (layer->type == CD_MDEFORMVERT) {
+ /* layer types that allocate own memory need special handling */
+ BKE_defvert_blend_write(writer, count, layer->data);
+ }
+ else if (layer->type == CD_MDISPS) {
+ write_mdisps(writer, count, layer->data, layer->flag & CD_FLAG_EXTERNAL);
+ }
+ else if (layer->type == CD_PAINT_MASK) {
+ const float *layer_data = layer->data;
+ BLO_write_raw(writer, sizeof(*layer_data) * count, layer_data);
+ }
+ else if (layer->type == CD_SCULPT_FACE_SETS) {
+ const float *layer_data = layer->data;
+ BLO_write_raw(writer, sizeof(*layer_data) * count, layer_data);
+ }
+ else if (layer->type == CD_GRID_PAINT_MASK) {
+ write_grid_paint_mask(writer, count, layer->data);
+ }
+ else if (layer->type == CD_FACEMAP) {
+ const int *layer_data = layer->data;
+ BLO_write_raw(writer, sizeof(*layer_data) * count, layer_data);
+ }
+ else {
+ const char *structname;
+ int structnum;
+ CustomData_file_write_info(layer->type, &structname, &structnum);
+ if (structnum) {
+ int datasize = structnum * count;
+ BLO_write_struct_array_by_name(writer, structname, datasize, layer->data);
+ }
+ else if (!BLO_write_is_undo(writer)) { /* Do not warn on undo. */
+ printf("%s error: layer '%s':%d - can't be written to file\n",
+ __func__,
+ structname,
+ layer->type);
+ }
+ }
+ }
+
+ if (data->external) {
+ BLO_write_struct(writer, CustomDataExternal, data->external);
+ }
+
+ if (!ELEM(layers, NULL, layers_buff)) {
+ MEM_freeN(layers);
+ }
+}
+
+static void blend_read_mdisps(BlendDataReader *reader, int count, MDisps *mdisps, int external)
+{
+ if (mdisps) {
+ for (int i = 0; i < count; i++) {
+ BLO_read_data_address(reader, &mdisps[i].disps);
+ BLO_read_data_address(reader, &mdisps[i].hidden);
+
+ if (mdisps[i].totdisp && !mdisps[i].level) {
+ /* this calculation is only correct for loop mdisps;
+ * if loading pre-BMesh face mdisps this will be
+ * overwritten with the correct value in
+ * bm_corners_to_loops() */
+ float gridsize = sqrtf(mdisps[i].totdisp);
+ mdisps[i].level = (int)(logf(gridsize - 1.0f) / (float)M_LN2) + 1;
+ }
+
+ if (BLO_read_requires_endian_switch(reader) && (mdisps[i].disps)) {
+ /* DNA_struct_switch_endian doesn't do endian swap for (*disps)[] */
+ /* this does swap for data written at write_mdisps() - readfile.c */
+ BLI_endian_switch_float_array(*mdisps[i].disps, mdisps[i].totdisp * 3);
+ }
+ if (!external && !mdisps[i].disps) {
+ mdisps[i].totdisp = 0;
+ }
+ }
+ }
+}
+
+static void blend_read_paint_mask(BlendDataReader *reader,
+ int count,
+ GridPaintMask *grid_paint_mask)
+{
+ if (grid_paint_mask) {
+ for (int i = 0; i < count; i++) {
+ GridPaintMask *gpm = &grid_paint_mask[i];
+ if (gpm->data) {
+ BLO_read_data_address(reader, &gpm->data);
+ }
+ }
+ }
+}
+
+void CustomData_blend_read(BlendDataReader *reader, CustomData *data, int count)
+{
+ BLO_read_data_address(reader, &data->layers);
+
+ /* annoying workaround for bug [#31079] loading legacy files with
+ * no polygons _but_ have stale customdata */
+ if (UNLIKELY(count == 0 && data->layers == NULL && data->totlayer != 0)) {
+ CustomData_reset(data);
+ return;
+ }
+
+ BLO_read_data_address(reader, &data->external);
+
+ int i = 0;
+ while (i < data->totlayer) {
+ CustomDataLayer *layer = &data->layers[i];
+
+ if (layer->flag & CD_FLAG_EXTERNAL) {
+ layer->flag &= ~CD_FLAG_IN_MEMORY;
+ }
+
+ layer->flag &= ~CD_FLAG_NOFREE;
+
+ if (CustomData_verify_versions(data, i)) {
+ BLO_read_data_address(reader, &layer->data);
+ if (layer->type == CD_MDISPS) {
+ blend_read_mdisps(reader, count, layer->data, layer->flag & CD_FLAG_EXTERNAL);
+ }
+ else if (layer->type == CD_GRID_PAINT_MASK) {
+ blend_read_paint_mask(reader, count, layer->data);
+ }
+ i++;
+ }
+ }
+
+ CustomData_update_typemap(data);
+}
diff --git a/source/blender/blenkernel/intern/deform.c b/source/blender/blenkernel/intern/deform.c
index 1a32deac776..ea5e4ec6532 100644
--- a/source/blender/blenkernel/intern/deform.c
+++ b/source/blender/blenkernel/intern/deform.c
@@ -50,6 +50,8 @@
#include "BKE_object.h"
#include "BKE_object_deform.h"
+#include "BLO_read_write.h"
+
#include "data_transfer_intern.h"
bDeformGroup *BKE_object_defgroup_new(Object *ob, const char *name)
@@ -1523,3 +1525,49 @@ void BKE_defvert_weight_to_rgb(float r_rgb[3], const float weight)
}
/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name .blend file I/O
+ * \{ */
+
+void BKE_defvert_blend_write(BlendWriter *writer, int count, MDeformVert *dvlist)
+{
+ if (dvlist == NULL) {
+ return;
+ }
+
+ /* Write the dvert list */
+ BLO_write_struct_array(writer, MDeformVert, count, dvlist);
+
+ /* Write deformation data for each dvert */
+ for (int i = 0; i < count; i++) {
+ if (dvlist[i].dw) {
+ BLO_write_struct_array(writer, MDeformWeight, dvlist[i].totweight, dvlist[i].dw);
+ }
+ }
+}
+
+void BKE_defvert_blend_read(BlendDataReader *reader, int count, MDeformVert *mdverts)
+{
+ if (mdverts == NULL) {
+ return;
+ }
+
+ for (int i = count; i > 0; i--, mdverts++) {
+ /*convert to vgroup allocation system*/
+ MDeformWeight *dw;
+ if (mdverts->dw && (dw = BLO_read_get_new_data_address(reader, mdverts->dw))) {
+ const size_t dw_len = MAX2(mdverts->totweight, 0) * sizeof(MDeformWeight);
+ void *dw_tmp = MEM_mallocN(dw_len, __func__);
+ memcpy(dw_tmp, dw, dw_len);
+ mdverts->dw = dw_tmp;
+ MEM_freeN(dw);
+ }
+ else {
+ mdverts->dw = NULL;
+ mdverts->totweight = 0;
+ }
+ }
+}
+
+/** \} */
diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c
index abed90e7192..294e6f8c8a0 100644
--- a/source/blender/blenkernel/intern/fcurve.c
+++ b/source/blender/blenkernel/intern/fcurve.c
@@ -48,6 +48,8 @@
#include "BKE_lib_query.h"
#include "BKE_nla.h"
+#include "BLO_read_write.h"
+
#include "RNA_access.h"
#include "CLG_log.h"
@@ -57,13 +59,21 @@
static CLG_LogRef LOG = {"bke.fcurve"};
-/* ************************** Data-Level Functions ************************* */
+/* -------------------------------------------------------------------- */
+/** \name F-Curve Data Create
+ * \{ */
+
FCurve *BKE_fcurve_create(void)
{
FCurve *fcu = MEM_callocN(sizeof(FCurve), __func__);
return fcu;
}
-/* ---------------------- Freeing --------------------------- */
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name F-Curve Data Free
+ * \{ */
/* Frees the F-Curve itself too, so make sure BLI_remlink is called before calling this... */
void BKE_fcurve_free(FCurve *fcu)
@@ -110,7 +120,11 @@ void BKE_fcurves_free(ListBase *list)
BLI_listbase_clear(list);
}
-/* ---------------------- Copy --------------------------- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name F-Curve Data Copy
+ * \{ */
/* duplicate an F-Curve */
FCurve *BKE_fcurve_copy(const FCurve *fcu)
@@ -478,7 +492,11 @@ FCurve *BKE_fcurve_find_by_rna_context_ui(bContext *C,
return fcu;
}
-/* ----------------- Finding Keyframes/Extents -------------------------- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Finding Keyframes/Extents
+ * \{ */
/* Binary search algorithm for finding where to insert BezTriple,
* with optional argument for precision required.
@@ -809,7 +827,11 @@ bool BKE_fcurve_calc_range(
return foundvert;
}
-/* ----------------- Status Checks -------------------------- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Status Checks
+ * \{ */
/* Are keyframes on F-Curve of any use?
* Usability of keyframes refers to whether they should be displayed,
@@ -899,7 +921,11 @@ bool BKE_fcurve_is_keyframable(FCurve *fcu)
return true;
}
-/* ***************************** Keyframe Column Tools ********************************* */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Keyframe Column Tools
+ * \{ */
/* add a BezTriple to a column */
void bezt_add_to_cfra_elem(ListBase *lb, BezTriple *bezt)
@@ -933,7 +959,12 @@ void bezt_add_to_cfra_elem(ListBase *lb, BezTriple *bezt)
cen->sel = bezt->f2;
}
-/* ***************************** Samples Utilities ******************************* */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Samples Utilities
+ * \{ */
+
/* Some utilities for working with FPoints (i.e. 'sampled' animation curve data, such as
* data imported from BVH/Mocap files), which are specialized for use with high density datasets,
* which BezTriples/Keyframe data are ill equipped to do.
@@ -1271,7 +1302,11 @@ short test_time_fcurve(FCurve *fcu)
return 0;
}
-/* ***************************** Curve Calculations ********************************* */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name F-Curve Calculations
+ * \{ */
/* The total length of the handles is not allowed to be more
* than the horizontal distance between (v1-v4).
@@ -1449,7 +1484,11 @@ static void berekeny(float f1, float f2, float f3, float f4, float *o, int b)
}
}
-/* -------------------------- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name F-Curve Evaluation
+ * \{ */
static float fcurve_eval_keyframes_extrapolate(
FCurve *fcu, BezTriple *bezts, float evaltime, int endpoint_offset, int direction_to_neighbor)
@@ -1812,7 +1851,11 @@ static float fcurve_eval_samples(FCurve *fcu, FPoint *fpts, float evaltime)
return cvalue;
}
-/* ***************************** F-Curve - Evaluation ********************************* */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name F-Curve - Evaluation
+ * \{ */
/* Evaluate and return the value of the given F-Curve at the specified frame ("evaltime")
* Note: this is also used for drivers
@@ -1947,3 +1990,268 @@ float calculate_fcurve(PathResolvedRNA *anim_rna,
fcu->curval = curval; /* debug display only, not thread safe! */
return curval;
}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name F-Curve - .blend file API
+ * \{ */
+
+void BKE_fmodifiers_blend_write(BlendWriter *writer, ListBase *fmodifiers)
+{
+ /* Write all modifiers first (for faster reloading) */
+ BLO_write_struct_list(writer, FModifier, fmodifiers);
+
+ /* Modifiers */
+ LISTBASE_FOREACH (FModifier *, fcm, fmodifiers) {
+ const FModifierTypeInfo *fmi = fmodifier_get_typeinfo(fcm);
+
+ /* Write the specific data */
+ if (fmi && fcm->data) {
+ /* firstly, just write the plain fmi->data struct */
+ BLO_write_struct_by_name(writer, fmi->structName, fcm->data);
+
+ /* do any modifier specific stuff */
+ switch (fcm->type) {
+ case FMODIFIER_TYPE_GENERATOR: {
+ FMod_Generator *data = fcm->data;
+
+ /* write coefficients array */
+ if (data->coefficients) {
+ BLO_write_float_array(writer, data->arraysize, data->coefficients);
+ }
+
+ break;
+ }
+ case FMODIFIER_TYPE_ENVELOPE: {
+ FMod_Envelope *data = fcm->data;
+
+ /* write envelope data */
+ if (data->data) {
+ BLO_write_struct_array(writer, FCM_EnvelopeData, data->totvert, data->data);
+ }
+
+ break;
+ }
+ case FMODIFIER_TYPE_PYTHON: {
+ FMod_Python *data = fcm->data;
+
+ /* Write ID Properties -- and copy this comment EXACTLY for easy finding
+ * of library blocks that implement this.*/
+ IDP_BlendWrite(writer, data->prop);
+
+ break;
+ }
+ }
+ }
+ }
+}
+
+void BKE_fmodifiers_blend_read_data(BlendDataReader *reader, ListBase *fmodifiers, FCurve *curve)
+{
+ LISTBASE_FOREACH (FModifier *, fcm, fmodifiers) {
+ /* relink general data */
+ BLO_read_data_address(reader, &fcm->data);
+ fcm->curve = curve;
+
+ /* do relinking of data for specific types */
+ switch (fcm->type) {
+ case FMODIFIER_TYPE_GENERATOR: {
+ FMod_Generator *data = (FMod_Generator *)fcm->data;
+ BLO_read_float_array(reader, data->arraysize, &data->coefficients);
+ break;
+ }
+ case FMODIFIER_TYPE_ENVELOPE: {
+ FMod_Envelope *data = (FMod_Envelope *)fcm->data;
+
+ BLO_read_data_address(reader, &data->data);
+
+ break;
+ }
+ case FMODIFIER_TYPE_PYTHON: {
+ FMod_Python *data = (FMod_Python *)fcm->data;
+
+ BLO_read_data_address(reader, &data->prop);
+ IDP_BlendDataRead(reader, &data->prop);
+
+ break;
+ }
+ }
+ }
+}
+
+void BKE_fmodifiers_blend_read_lib(BlendLibReader *reader, ID *id, ListBase *fmodifiers)
+{
+ LISTBASE_FOREACH (FModifier *, fcm, fmodifiers) {
+ /* data for specific modifiers */
+ switch (fcm->type) {
+ case FMODIFIER_TYPE_PYTHON: {
+ FMod_Python *data = (FMod_Python *)fcm->data;
+ BLO_read_id_address(reader, id->lib, &data->script);
+ break;
+ }
+ }
+ }
+}
+
+void BKE_fmodifiers_blend_read_expand(BlendExpander *expander, ListBase *fmodifiers)
+{
+ LISTBASE_FOREACH (FModifier *, fcm, fmodifiers) {
+ /* library data for specific F-Modifier types */
+ switch (fcm->type) {
+ case FMODIFIER_TYPE_PYTHON: {
+ FMod_Python *data = (FMod_Python *)fcm->data;
+ BLO_expand(expander, data->script);
+ break;
+ }
+ }
+ }
+}
+
+void BKE_fcurve_blend_write(BlendWriter *writer, ListBase *fcurves)
+{
+ BLO_write_struct_list(writer, FCurve, fcurves);
+ LISTBASE_FOREACH (FCurve *, fcu, fcurves) {
+ /* curve data */
+ if (fcu->bezt) {
+ BLO_write_struct_array(writer, BezTriple, fcu->totvert, fcu->bezt);
+ }
+ if (fcu->fpt) {
+ BLO_write_struct_array(writer, FPoint, fcu->totvert, fcu->fpt);
+ }
+
+ if (fcu->rna_path) {
+ BLO_write_string(writer, fcu->rna_path);
+ }
+
+ /* driver data */
+ if (fcu->driver) {
+ ChannelDriver *driver = fcu->driver;
+
+ BLO_write_struct(writer, ChannelDriver, driver);
+
+ /* variables */
+ BLO_write_struct_list(writer, DriverVar, &driver->variables);
+ LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) {
+ DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) {
+ if (dtar->rna_path) {
+ BLO_write_string(writer, dtar->rna_path);
+ }
+ }
+ DRIVER_TARGETS_LOOPER_END;
+ }
+ }
+
+ /* write F-Modifiers */
+ BKE_fmodifiers_blend_write(writer, &fcu->modifiers);
+ }
+}
+
+void BKE_fcurve_blend_read_data(BlendDataReader *reader, ListBase *fcurves)
+{
+ /* link F-Curve data to F-Curve again (non ID-libs) */
+ LISTBASE_FOREACH (FCurve *, fcu, fcurves) {
+ /* curve data */
+ BLO_read_data_address(reader, &fcu->bezt);
+ BLO_read_data_address(reader, &fcu->fpt);
+
+ /* rna path */
+ BLO_read_data_address(reader, &fcu->rna_path);
+
+ /* group */
+ BLO_read_data_address(reader, &fcu->grp);
+
+ /* clear disabled flag - allows disabled drivers to be tried again ([#32155]),
+ * but also means that another method for "reviving disabled F-Curves" exists
+ */
+ fcu->flag &= ~FCURVE_DISABLED;
+
+ /* driver */
+ BLO_read_data_address(reader, &fcu->driver);
+ if (fcu->driver) {
+ ChannelDriver *driver = fcu->driver;
+
+ /* Compiled expression data will need to be regenerated
+ * (old pointer may still be set here). */
+ driver->expr_comp = NULL;
+ driver->expr_simple = NULL;
+
+ /* give the driver a fresh chance - the operating environment may be different now
+ * (addons, etc. may be different) so the driver namespace may be sane now [#32155]
+ */
+ driver->flag &= ~DRIVER_FLAG_INVALID;
+
+ /* relink variables, targets and their paths */
+ BLO_read_list(reader, &driver->variables);
+ LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) {
+ DRIVER_TARGETS_LOOPER_BEGIN (dvar) {
+ /* only relink the targets being used */
+ if (tarIndex < dvar->num_targets) {
+ BLO_read_data_address(reader, &dtar->rna_path);
+ }
+ else {
+ dtar->rna_path = NULL;
+ }
+ }
+ DRIVER_TARGETS_LOOPER_END;
+ }
+ }
+
+ /* modifiers */
+ BLO_read_list(reader, &fcu->modifiers);
+ BKE_fmodifiers_blend_read_data(reader, &fcu->modifiers, fcu);
+ }
+}
+
+void BKE_fcurve_blend_read_lib(BlendLibReader *reader, ID *id, ListBase *fcurves)
+{
+ if (fcurves == NULL) {
+ return;
+ }
+
+ /* relink ID-block references... */
+ LISTBASE_FOREACH (FCurve *, fcu, fcurves) {
+ /* driver data */
+ if (fcu->driver) {
+ ChannelDriver *driver = fcu->driver;
+ LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) {
+ DRIVER_TARGETS_LOOPER_BEGIN (dvar) {
+ /* only relink if still used */
+ if (tarIndex < dvar->num_targets) {
+ BLO_read_id_address(reader, id->lib, &dtar->id);
+ }
+ else {
+ dtar->id = NULL;
+ }
+ }
+ DRIVER_TARGETS_LOOPER_END;
+ }
+ }
+
+ /* modifiers */
+ BKE_fmodifiers_blend_read_lib(reader, id, &fcu->modifiers);
+ }
+}
+
+void BKE_fcurve_blend_read_expand(BlendExpander *expander, ListBase *fcurves)
+{
+ LISTBASE_FOREACH (FCurve *, fcu, fcurves) {
+ /* Driver targets if there is a driver */
+ if (fcu->driver) {
+ ChannelDriver *driver = fcu->driver;
+
+ LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) {
+ DRIVER_TARGETS_LOOPER_BEGIN (dvar) {
+ // TODO: only expand those that are going to get used?
+ BLO_expand(expander, dtar->id);
+ }
+ DRIVER_TARGETS_LOOPER_END;
+ }
+ }
+
+ /* F-Curve Modifiers */
+ BKE_fmodifiers_blend_read_expand(expander, &fcu->modifiers);
+ }
+}
+
+/** \} */
diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c
index 4bedba8f76b..dfbb8202093 100644
--- a/source/blender/blenkernel/intern/font.c
+++ b/source/blender/blenkernel/intern/font.c
@@ -135,6 +135,12 @@ IDTypeInfo IDType_ID_VF = {
.free_data = vfont_free_data,
.make_local = NULL,
.foreach_id = NULL,
+ .foreach_cache = NULL,
+
+ .blend_write = NULL,
+ .blend_read_data = NULL,
+ .blend_read_lib = NULL,
+ .blend_read_expand = NULL,
};
/***************************** VFont *******************************/
diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c
index 4f65f8a57ab..10b6328ead4 100644
--- a/source/blender/blenkernel/intern/gpencil.c
+++ b/source/blender/blenkernel/intern/gpencil.c
@@ -126,6 +126,12 @@ IDTypeInfo IDType_ID_GD = {
.free_data = greasepencil_free_data,
.make_local = NULL,
.foreach_id = greasepencil_foreach_id,
+ .foreach_cache = NULL,
+
+ .blend_write = NULL,
+ .blend_read_data = NULL,
+ .blend_read_lib = NULL,
+ .blend_read_expand = NULL,
};
/* ************************************************** */
diff --git a/source/blender/blenkernel/intern/gpencil_geom.c b/source/blender/blenkernel/intern/gpencil_geom.c
index 65ce117431b..4226e65d8fa 100644
--- a/source/blender/blenkernel/intern/gpencil_geom.c
+++ b/source/blender/blenkernel/intern/gpencil_geom.c
@@ -2238,12 +2238,12 @@ static Material *gpencil_add_material(Main *bmain,
gp_style->flag |= GP_MATERIAL_STROKE_SHOW;
}
else {
- linearrgb_to_srgb_v4(gp_style->stroke_rgba, color);
+ copy_v4_v4(gp_style->stroke_rgba, color);
gp_style->flag &= ~GP_MATERIAL_STROKE_SHOW;
}
/* Fill color. */
- linearrgb_to_srgb_v4(gp_style->fill_rgba, color);
+ copy_v4_v4(gp_style->fill_rgba, color);
if (use_fill) {
gp_style->flag |= GP_MATERIAL_FILL_SHOW;
}
diff --git a/source/blender/blenkernel/intern/hair.c b/source/blender/blenkernel/intern/hair.c
index 2905bfc978a..8831d09698b 100644
--- a/source/blender/blenkernel/intern/hair.c
+++ b/source/blender/blenkernel/intern/hair.c
@@ -119,6 +119,12 @@ IDTypeInfo IDType_ID_HA = {
.free_data = hair_free_data,
.make_local = NULL,
.foreach_id = hair_foreach_id,
+ .foreach_cache = NULL,
+
+ .blend_write = NULL,
+ .blend_read_data = NULL,
+ .blend_read_lib = NULL,
+ .blend_read_expand = NULL,
};
static void hair_random(Hair *hair)
diff --git a/source/blender/blenkernel/intern/idprop.c b/source/blender/blenkernel/intern/idprop.c
index 529ae227df9..661d27f4765 100644
--- a/source/blender/blenkernel/intern/idprop.c
+++ b/source/blender/blenkernel/intern/idprop.c
@@ -26,11 +26,13 @@
#include <stdlib.h>
#include <string.h>
+#include "BLI_endian_switch.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_string.h"
#include "BLI_utildefines.h"
+#include "BKE_global.h"
#include "BKE_idprop.h"
#include "BKE_lib_id.h"
@@ -38,6 +40,8 @@
#include "MEM_guardedalloc.h"
+#include "BLO_read_write.h"
+
#include "BLI_strict_flags.h"
/* IDPropertyTemplate is a union in DNA_ID.h */
@@ -1170,4 +1174,270 @@ void IDP_foreach_property(IDProperty *id_property_root,
}
}
+void IDP_WriteProperty_OnlyData(const IDProperty *prop, BlendWriter *writer);
+
+static void IDP_WriteArray(const IDProperty *prop, BlendWriter *writer)
+{
+ /*REMEMBER to set totalen to len in the linking code!!*/
+ if (prop->data.pointer) {
+ BLO_write_raw(writer, (int)MEM_allocN_len(prop->data.pointer), prop->data.pointer);
+
+ if (prop->subtype == IDP_GROUP) {
+ IDProperty **array = prop->data.pointer;
+ int a;
+
+ for (a = 0; a < prop->len; a++) {
+ IDP_BlendWrite(writer, array[a]);
+ }
+ }
+ }
+}
+
+static void IDP_WriteIDPArray(const IDProperty *prop, BlendWriter *writer)
+{
+ /*REMEMBER to set totalen to len in the linking code!!*/
+ if (prop->data.pointer) {
+ const IDProperty *array = prop->data.pointer;
+ int a;
+
+ BLO_write_struct_array(writer, IDProperty, prop->len, array);
+
+ for (a = 0; a < prop->len; a++) {
+ IDP_WriteProperty_OnlyData(&array[a], writer);
+ }
+ }
+}
+
+static void IDP_WriteString(const IDProperty *prop, BlendWriter *writer)
+{
+ /*REMEMBER to set totalen to len in the linking code!!*/
+ BLO_write_raw(writer, prop->len, prop->data.pointer);
+}
+
+static void IDP_WriteGroup(const IDProperty *prop, BlendWriter *writer)
+{
+ IDProperty *loop;
+
+ for (loop = prop->data.group.first; loop; loop = loop->next) {
+ IDP_BlendWrite(writer, loop);
+ }
+}
+
+/* Functions to read/write ID Properties */
+void IDP_WriteProperty_OnlyData(const IDProperty *prop, BlendWriter *writer)
+{
+ switch (prop->type) {
+ case IDP_GROUP:
+ IDP_WriteGroup(prop, writer);
+ break;
+ case IDP_STRING:
+ IDP_WriteString(prop, writer);
+ break;
+ case IDP_ARRAY:
+ IDP_WriteArray(prop, writer);
+ break;
+ case IDP_IDPARRAY:
+ IDP_WriteIDPArray(prop, writer);
+ break;
+ }
+}
+
+void IDP_BlendWrite(BlendWriter *writer, const IDProperty *prop)
+{
+ BLO_write_struct(writer, IDProperty, prop);
+ IDP_WriteProperty_OnlyData(prop, writer);
+}
+
+static void IDP_DirectLinkProperty(IDProperty *prop, BlendDataReader *reader);
+
+static void IDP_DirectLinkIDPArray(IDProperty *prop, BlendDataReader *reader)
+{
+ IDProperty *array;
+ int i;
+
+ /* since we didn't save the extra buffer, set totallen to len */
+ prop->totallen = prop->len;
+ BLO_read_data_address(reader, &prop->data.pointer);
+
+ array = (IDProperty *)prop->data.pointer;
+
+ /* note!, idp-arrays didn't exist in 2.4x, so the pointer will be cleared
+ * there's not really anything we can do to correct this, at least don't crash */
+ if (array == NULL) {
+ prop->len = 0;
+ prop->totallen = 0;
+ }
+
+ for (i = 0; i < prop->len; i++) {
+ IDP_DirectLinkProperty(&array[i], reader);
+ }
+}
+
+static void IDP_DirectLinkArray(IDProperty *prop, BlendDataReader *reader)
+{
+ IDProperty **array;
+ int i;
+
+ /* since we didn't save the extra buffer, set totallen to len */
+ prop->totallen = prop->len;
+
+ if (prop->subtype == IDP_GROUP) {
+ BLO_read_pointer_array(reader, &prop->data.pointer);
+ array = prop->data.pointer;
+
+ for (i = 0; i < prop->len; i++) {
+ IDP_DirectLinkProperty(array[i], reader);
+ }
+ }
+ else if (prop->subtype == IDP_DOUBLE) {
+ BLO_read_double_array(reader, prop->len, (double **)&prop->data.pointer);
+ }
+ else {
+ /* also used for floats */
+ BLO_read_int32_array(reader, prop->len, (int **)&prop->data.pointer);
+ }
+}
+
+static void IDP_DirectLinkString(IDProperty *prop, BlendDataReader *reader)
+{
+ /*since we didn't save the extra string buffer, set totallen to len.*/
+ prop->totallen = prop->len;
+ BLO_read_data_address(reader, &prop->data.pointer);
+}
+
+static void IDP_DirectLinkGroup(IDProperty *prop, BlendDataReader *reader)
+{
+ ListBase *lb = &prop->data.group;
+ IDProperty *loop;
+
+ BLO_read_list(reader, lb);
+
+ /*Link child id properties now*/
+ for (loop = prop->data.group.first; loop; loop = loop->next) {
+ IDP_DirectLinkProperty(loop, reader);
+ }
+}
+
+static void IDP_DirectLinkProperty(IDProperty *prop, BlendDataReader *reader)
+{
+ switch (prop->type) {
+ case IDP_GROUP:
+ IDP_DirectLinkGroup(prop, reader);
+ break;
+ case IDP_STRING:
+ IDP_DirectLinkString(prop, reader);
+ break;
+ case IDP_ARRAY:
+ IDP_DirectLinkArray(prop, reader);
+ break;
+ case IDP_IDPARRAY:
+ IDP_DirectLinkIDPArray(prop, reader);
+ break;
+ case IDP_DOUBLE:
+ /* Workaround for doubles.
+ * They are stored in the same field as `int val, val2` in the #IDPropertyData struct,
+ * they have to deal with endianness specifically.
+ *
+ * In theory, val and val2 would've already been swapped
+ * if switch_endian is true, so we have to first un-swap
+ * them then re-swap them as a single 64-bit entity. */
+ if (BLO_read_requires_endian_switch(reader)) {
+ BLI_endian_switch_int32(&prop->data.val);
+ BLI_endian_switch_int32(&prop->data.val2);
+ BLI_endian_switch_int64((int64_t *)&prop->data.val);
+ }
+ break;
+ case IDP_INT:
+ case IDP_FLOAT:
+ case IDP_ID:
+ break; /* Nothing special to do here. */
+ default:
+ /* Unknown IDP type, nuke it (we cannot handle unknown types everywhere in code,
+ * IDP are way too polymorphic to do it safely. */
+ printf(
+ "%s: found unknown IDProperty type %d, reset to Integer one !\n", __func__, prop->type);
+ /* Note: we do not attempt to free unknown prop, we have no way to know how to do that! */
+ prop->type = IDP_INT;
+ prop->subtype = 0;
+ IDP_Int(prop) = 0;
+ }
+}
+
+void IDP_BlendReadData_impl(BlendDataReader *reader, IDProperty **prop, const char *caller_func_id)
+{
+ if (*prop) {
+ if ((*prop)->type == IDP_GROUP) {
+ IDP_DirectLinkGroup(*prop, reader);
+ }
+ else {
+ /* corrupt file! */
+ printf("%s: found non group data, freeing type %d!\n", caller_func_id, (*prop)->type);
+ /* don't risk id, data's likely corrupt. */
+ // IDP_FreePropertyContent(*prop);
+ *prop = NULL;
+ }
+ }
+}
+
+void IDP_BlendReadLib(BlendLibReader *reader, IDProperty *prop)
+{
+ if (!prop) {
+ return;
+ }
+
+ switch (prop->type) {
+ case IDP_ID: /* PointerProperty */
+ {
+ void *newaddr = BLO_read_get_new_id_address(reader, NULL, IDP_Id(prop));
+ if (IDP_Id(prop) && !newaddr && G.debug) {
+ printf("Error while loading \"%s\". Data not found in file!\n", prop->name);
+ }
+ prop->data.pointer = newaddr;
+ break;
+ }
+ case IDP_IDPARRAY: /* CollectionProperty */
+ {
+ IDProperty *idp_array = IDP_IDPArray(prop);
+ for (int i = 0; i < prop->len; i++) {
+ IDP_BlendReadLib(reader, &(idp_array[i]));
+ }
+ break;
+ }
+ case IDP_GROUP: /* PointerProperty */
+ {
+ LISTBASE_FOREACH (IDProperty *, loop, &prop->data.group) {
+ IDP_BlendReadLib(reader, loop);
+ }
+ break;
+ }
+ default:
+ break; /* Nothing to do for other IDProps. */
+ }
+}
+
+void IDP_BlendReadExpand(struct BlendExpander *expander, IDProperty *prop)
+{
+ if (!prop) {
+ return;
+ }
+
+ switch (prop->type) {
+ case IDP_ID:
+ BLO_expand(expander, IDP_Id(prop));
+ break;
+ case IDP_IDPARRAY: {
+ IDProperty *idp_array = IDP_IDPArray(prop);
+ for (int i = 0; i < prop->len; i++) {
+ IDP_BlendReadExpand(expander, &idp_array[i]);
+ }
+ break;
+ }
+ case IDP_GROUP:
+ LISTBASE_FOREACH (IDProperty *, loop, &prop->data.group) {
+ IDP_BlendReadExpand(expander, loop);
+ }
+ break;
+ }
+}
+
/** \} */
diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c
index 7ff34acca7a..0f694a26b45 100644
--- a/source/blender/blenkernel/intern/image.c
+++ b/source/blender/blenkernel/intern/image.c
@@ -232,6 +232,11 @@ IDTypeInfo IDType_ID_IM = {
.make_local = NULL,
.foreach_id = NULL,
.foreach_cache = image_foreach_cache,
+
+ .blend_write = NULL,
+ .blend_read_data = NULL,
+ .blend_read_lib = NULL,
+ .blend_read_expand = NULL,
};
/* prototypes */
diff --git a/source/blender/blenkernel/intern/ipo.c b/source/blender/blenkernel/intern/ipo.c
index 94a142600b6..3e92fd13370 100644
--- a/source/blender/blenkernel/intern/ipo.c
+++ b/source/blender/blenkernel/intern/ipo.c
@@ -124,6 +124,12 @@ IDTypeInfo IDType_ID_IP = {
.free_data = ipo_free_data,
.make_local = NULL,
.foreach_id = NULL,
+ .foreach_cache = NULL,
+
+ .blend_write = NULL,
+ .blend_read_data = NULL,
+ .blend_read_lib = NULL,
+ .blend_read_expand = NULL,
};
/* *************************************************** */
@@ -2197,7 +2203,7 @@ void do_versions_ipos_to_animato(Main *bmain)
AnimData *adt = BKE_animdata_add_id(id);
- SEQ_BEGIN (ed, seq) {
+ SEQ_ALL_BEGIN (ed, seq) {
IpoCurve *icu = (seq->ipo) ? seq->ipo->curve.first : NULL;
short adrcode = SEQ_FAC1;
@@ -2238,7 +2244,7 @@ void do_versions_ipos_to_animato(Main *bmain)
id_us_min(&seq->ipo->id);
seq->ipo = NULL;
}
- SEQ_END;
+ SEQ_ALL_END;
}
}
diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c
index 0108befa710..456325851a6 100644
--- a/source/blender/blenkernel/intern/key.c
+++ b/source/blender/blenkernel/intern/key.c
@@ -113,6 +113,12 @@ IDTypeInfo IDType_ID_KE = {
.free_data = shapekey_free_data,
.make_local = NULL,
.foreach_id = shapekey_foreach_id,
+ .foreach_cache = NULL,
+
+ .blend_write = NULL,
+ .blend_read_data = NULL,
+ .blend_read_lib = NULL,
+ .blend_read_expand = NULL,
};
#define KEY_MODE_DUMMY 0 /* use where mode isn't checked for */
diff --git a/source/blender/blenkernel/intern/lattice.c b/source/blender/blenkernel/intern/lattice.c
index 4d073593da7..7304dd91eea 100644
--- a/source/blender/blenkernel/intern/lattice.c
+++ b/source/blender/blenkernel/intern/lattice.c
@@ -35,6 +35,9 @@
#include "BLT_translation.h"
+/* Allow using deprecated functionality for .blend file I/O. */
+#define DNA_DEPRECATED_ALLOW
+
#include "DNA_curve_types.h"
#include "DNA_defaults.h"
#include "DNA_key_types.h"
@@ -43,7 +46,9 @@
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "BKE_anim_data.h"
#include "BKE_curve.h"
+#include "BKE_deform.h"
#include "BKE_displist.h"
#include "BKE_idtype.h"
#include "BKE_lattice.h"
@@ -53,10 +58,10 @@
#include "BKE_modifier.h"
#include "BKE_object.h"
-#include "BKE_deform.h"
-
#include "DEG_depsgraph_query.h"
+#include "BLO_read_write.h"
+
static void lattice_init_data(ID *id)
{
Lattice *lattice = (Lattice *)id;
@@ -124,6 +129,59 @@ static void lattice_foreach_id(ID *id, LibraryForeachIDData *data)
BKE_LIB_FOREACHID_PROCESS(data, lattice->key, IDWALK_CB_USER);
}
+static void lattice_blend_write(BlendWriter *writer, ID *id, const void *id_address)
+{
+ Lattice *lt = (Lattice *)id;
+ if (lt->id.us > 0 || BLO_write_is_undo(writer)) {
+ /* Clean up, important in undo case to reduce false detection of changed datablocks. */
+ lt->editlatt = NULL;
+ lt->batch_cache = NULL;
+
+ /* write LibData */
+ BLO_write_id_struct(writer, Lattice, id_address, &lt->id);
+ BKE_id_blend_write(writer, &lt->id);
+
+ /* write animdata */
+ if (lt->adt) {
+ BKE_animdata_blend_write(writer, lt->adt);
+ }
+
+ /* direct data */
+ BLO_write_struct_array(writer, BPoint, lt->pntsu * lt->pntsv * lt->pntsw, lt->def);
+
+ BKE_defvert_blend_write(writer, lt->pntsu * lt->pntsv * lt->pntsw, lt->dvert);
+ }
+}
+
+static void lattice_blend_read_data(BlendDataReader *reader, ID *id)
+{
+ Lattice *lt = (Lattice *)id;
+ BLO_read_data_address(reader, &lt->def);
+
+ BLO_read_data_address(reader, &lt->dvert);
+ BKE_defvert_blend_read(reader, lt->pntsu * lt->pntsv * lt->pntsw, lt->dvert);
+
+ lt->editlatt = NULL;
+ lt->batch_cache = NULL;
+
+ BLO_read_data_address(reader, &lt->adt);
+ BKE_animdata_blend_read_data(reader, lt->adt);
+}
+
+static void lattice_blend_read_lib(BlendLibReader *reader, ID *id)
+{
+ Lattice *lt = (Lattice *)id;
+ BLO_read_id_address(reader, lt->id.lib, &lt->ipo); // XXX deprecated - old animation system
+ BLO_read_id_address(reader, lt->id.lib, &lt->key);
+}
+
+static void lattice_blend_read_expand(BlendExpander *expander, ID *id)
+{
+ Lattice *lt = (Lattice *)id;
+ BLO_expand(expander, lt->ipo); // XXX deprecated - old animation system
+ BLO_expand(expander, lt->key);
+}
+
IDTypeInfo IDType_ID_LT = {
.id_code = ID_LT,
.id_filter = FILTER_ID_LT,
@@ -139,6 +197,12 @@ IDTypeInfo IDType_ID_LT = {
.free_data = lattice_free_data,
.make_local = NULL,
.foreach_id = lattice_foreach_id,
+ .foreach_cache = NULL,
+
+ .blend_write = lattice_blend_write,
+ .blend_read_data = lattice_blend_read_data,
+ .blend_read_lib = lattice_blend_read_lib,
+ .blend_read_expand = lattice_blend_read_expand,
};
int BKE_lattice_index_from_uvw(Lattice *lt, const int u, const int v, const int w)
diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c
index 3b7aae98327..dfe939aa878 100644
--- a/source/blender/blenkernel/intern/lib_id.c
+++ b/source/blender/blenkernel/intern/lib_id.c
@@ -75,6 +75,8 @@
#include "RNA_access.h"
+#include "BLO_read_write.h"
+
#include "atomic_ops.h"
//#define DEBUG_TIME
@@ -342,8 +344,11 @@ static int lib_id_expand_local_cb(LibraryIDLinkCallbackData *cb_data)
}
if (cb_flag & IDWALK_CB_EMBEDDED) {
- /* Embedded data-blocks need to be made fully local as well. */
- if (*id_pointer != NULL) {
+ /* Embedded data-blocks need to be made fully local as well.
+ * Note however that in some cases (when owner ID had to be duplicated instead of being made
+ * local directly), its embedded IDs should also have already been duplicated, and hence be
+ * fully local here already. */
+ if (*id_pointer != NULL && ID_IS_LINKED(*id_pointer)) {
BLI_assert(*id_pointer != id_self);
lib_id_clear_library_data_ex(bmain, *id_pointer);
@@ -640,7 +645,7 @@ ID *BKE_id_copy_for_duplicate(Main *bmain, ID *id, const eDupli_ID_Flags duplica
BKE_animdata_duplicate_id_action(bmain, id_new, duplicate_flags);
if (key_new != NULL) {
- BKE_animdata_duplicate_id_action(bmain, id_new, duplicate_flags);
+ BKE_animdata_duplicate_id_action(bmain, key_new, duplicate_flags);
}
/* Note that actions of embedded data (root nodetrees and master collections) are handled
* by `BKE_animdata_duplicate_id_action` as well. */
@@ -2326,3 +2331,30 @@ void BKE_id_reorder(const ListBase *lb, ID *id, ID *relative, bool after)
*id_order = relative_order - 1;
}
}
+
+void BKE_id_blend_write(BlendWriter *writer, ID *id)
+{
+ /* ID_WM's id->properties are considered runtime only, and never written in .blend file. */
+ if (id->properties && !ELEM(GS(id->name), ID_WM)) {
+ IDP_BlendWrite(writer, id->properties);
+ }
+
+ if (id->override_library) {
+ BLO_write_struct(writer, IDOverrideLibrary, id->override_library);
+
+ BLO_write_struct_list(writer, IDOverrideLibraryProperty, &id->override_library->properties);
+ LISTBASE_FOREACH (IDOverrideLibraryProperty *, op, &id->override_library->properties) {
+ BLO_write_string(writer, op->rna_path);
+
+ BLO_write_struct_list(writer, IDOverrideLibraryPropertyOperation, &op->operations);
+ LISTBASE_FOREACH (IDOverrideLibraryPropertyOperation *, opop, &op->operations) {
+ if (opop->subitem_reference_name) {
+ BLO_write_string(writer, opop->subitem_reference_name);
+ }
+ if (opop->subitem_local_name) {
+ BLO_write_string(writer, opop->subitem_local_name);
+ }
+ }
+ }
+ }
+}
diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c
index 5b45148ed63..e9244c5af73 100644
--- a/source/blender/blenkernel/intern/lib_override.c
+++ b/source/blender/blenkernel/intern/lib_override.c
@@ -361,7 +361,10 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain)
return success;
}
-static bool lib_override_hierarchy_recursive_tag(Main *bmain, ID *id, const uint tag)
+static bool lib_override_hierarchy_recursive_tag(Main *bmain,
+ ID *id,
+ const uint tag,
+ Library *override_group_lib_reference)
{
void **entry_vp = BLI_ghash_lookup_p(bmain->relations->id_user_to_used, id);
if (entry_vp == NULL) {
@@ -369,6 +372,11 @@ static bool lib_override_hierarchy_recursive_tag(Main *bmain, ID *id, const uint
return (id->tag & tag) != 0;
}
+ if (override_group_lib_reference != NULL && ID_IS_OVERRIDE_LIBRARY_REAL(id) &&
+ id->override_library->reference->lib == override_group_lib_reference) {
+ id->tag |= tag;
+ }
+
/* This way we won't process again that ID should we encounter it again through another
* relationship hierarchy.
* Note that this does not free any memory from relations, so we can still use the entries.
@@ -383,7 +391,9 @@ static bool lib_override_hierarchy_recursive_tag(Main *bmain, ID *id, const uint
}
/* We only consider IDs from the same library. */
if (entry->id_pointer != NULL && (*entry->id_pointer)->lib == id->lib) {
- if (lib_override_hierarchy_recursive_tag(bmain, *entry->id_pointer, tag)) {
+ if (lib_override_hierarchy_recursive_tag(
+ bmain, *entry->id_pointer, tag, override_group_lib_reference) &&
+ override_group_lib_reference == NULL) {
id->tag |= tag;
}
}
@@ -395,6 +405,7 @@ static bool lib_override_hierarchy_recursive_tag(Main *bmain, ID *id, const uint
/**
* Tag all IDs in given \a bmain that are being used by given \a id_root ID or its dependencies,
* recursively.
+ * It detects and tag only chains of dependencies marked at both ends by given tag.
*
* This will include all local IDs, and all IDs from the same library as the \a id_root.
*
@@ -402,8 +413,8 @@ static bool lib_override_hierarchy_recursive_tag(Main *bmain, ID *id, const uint
* \param do_create_main_relashionships Whether main relations needs to be created or already exist
* (in any case, they will be freed by this function).
*/
-void BKE_lib_override_library_dependencies_tag(struct Main *bmain,
- struct ID *id_root,
+void BKE_lib_override_library_dependencies_tag(Main *bmain,
+ ID *id_root,
const uint tag,
const bool do_create_main_relashionships)
{
@@ -411,10 +422,36 @@ void BKE_lib_override_library_dependencies_tag(struct Main *bmain,
BKE_main_relations_create(bmain, 0);
}
- /* Then we tag all intermediary data-blocks in-between two overridden ones (e.g. if a shapekey
+ /* We tag all intermediary data-blocks in-between two overridden ones (e.g. if a shapekey
* has a driver using an armature object's bone, we need to override the shapekey/obdata, the
* objects using them, etc.) */
- lib_override_hierarchy_recursive_tag(bmain, id_root, tag);
+ lib_override_hierarchy_recursive_tag(bmain, id_root, tag, NULL);
+
+ BKE_main_relations_free(bmain);
+}
+
+/**
+ * Tag all IDs in given \a bmain that are part of the same \a id_root liboverride ID group.
+ * That is, all other liboverrides IDs (in)directly used by \a is_root one, sharing the same
+ * library for their reference IDs.
+ *
+ * \param id_root The root of the hierarchy of liboverride dependencies to be tagged.
+ * \param do_create_main_relashionships Whether main relations needs to be created or already exist
+ * (in any case, they will be freed by this function).
+ */
+void BKE_lib_override_library_override_group_tag(Main *bmain,
+ ID *id_root,
+ const uint tag,
+ const bool do_create_main_relashionships)
+{
+ if (do_create_main_relashionships) {
+ BKE_main_relations_create(bmain, 0);
+ }
+
+ /* We tag all liboverride data-blocks from the same library as reference one,
+ * being used by the root ID. */
+ lib_override_hierarchy_recursive_tag(
+ bmain, id_root, tag, id_root->override_library->reference->lib);
BKE_main_relations_free(bmain);
}
@@ -459,26 +496,7 @@ static int lib_override_library_make_tag_ids_cb(LibraryIDLinkCallbackData *cb_da
return IDWALK_RET_NOP;
}
-/**
- * Advanced 'smart' function to create fully functional overrides.
- *
- * \note Currently it only does special things if given \a id_root is an object of collection, more
- * specific behaviors may be added in the future for other ID types.
- *
- * \note It will overrides all IDs tagged with \a LIB_TAG_DOIT, and it does not clear that tag at
- * its beginning, so caller code can add extra data-blocks to be overridden as well.
- *
- * \note In the future that same function may be extended to support 'refresh' of overrides
- * (rebuilding overrides from linked data, trying to preserve local overrides already defined).
- *
- * \param id_root The root ID to create an override from.
- * \param id_reference some reference ID used to do some post-processing after overrides have been
- * created, may be NULL. Typically, the Empty object instantiating the linked
- * collection we override, currently.
- * \return true if override was successfully created.
- */
-bool BKE_lib_override_library_create(
- Main *bmain, Scene *scene, ViewLayer *view_layer, ID *id_root, ID *id_reference)
+static bool lib_override_library_create_do(Main *bmain, ID *id_root)
{
/* Tag all collections and objects, as well as other IDs using them. */
id_root->tag |= LIB_TAG_DOIT;
@@ -508,115 +526,308 @@ bool BKE_lib_override_library_create(
/* Note that this call will also free the main relations data we created above. */
BKE_lib_override_library_dependencies_tag(bmain, id_root, LIB_TAG_DOIT, false);
- const bool success = BKE_lib_override_library_create_from_tag(bmain);
-
- if (success) {
- BKE_main_collection_sync(bmain);
-
- switch (GS(id_root->name)) {
- case ID_GR: {
- Object *ob_reference = id_reference != NULL && GS(id_reference->name) == ID_OB ?
- (Object *)id_reference :
- NULL;
- Collection *collection_new = ((Collection *)id_root->newid);
- if (ob_reference != NULL) {
- BKE_collection_add_from_object(bmain, scene, ob_reference, collection_new);
- }
- else {
- BKE_collection_add_from_collection(
- bmain, scene, ((Collection *)id_root), collection_new);
- }
+ return BKE_lib_override_library_create_from_tag(bmain);
+}
- FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection_new, ob_new) {
- if (ob_new != NULL && ob_new->id.override_library != NULL) {
- if (ob_reference != NULL) {
- Base *base;
- if ((base = BKE_view_layer_base_find(view_layer, ob_new)) == NULL) {
- BKE_collection_object_add_from(bmain, scene, ob_reference, ob_new);
- base = BKE_view_layer_base_find(view_layer, ob_new);
- DEG_id_tag_update_ex(
- bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS);
- }
+static void lib_override_library_create_post_process(
+ Main *bmain, Scene *scene, ViewLayer *view_layer, ID *id_root, ID *id_reference)
+{
+ BKE_main_collection_sync(bmain);
+
+ switch (GS(id_root->name)) {
+ case ID_GR: {
+ Object *ob_reference = id_reference != NULL && GS(id_reference->name) == ID_OB ?
+ (Object *)id_reference :
+ NULL;
+ Collection *collection_new = ((Collection *)id_root->newid);
+ if (ob_reference != NULL) {
+ BKE_collection_add_from_object(bmain, scene, ob_reference, collection_new);
+ }
+ else {
+ BKE_collection_add_from_collection(bmain, scene, ((Collection *)id_root), collection_new);
+ }
- if (ob_new == (Object *)ob_reference->id.newid) {
- /* TODO: is setting active needed? */
- BKE_view_layer_base_select_and_set_active(view_layer, base);
- }
- }
- else if (BKE_view_layer_base_find(view_layer, ob_new) == NULL) {
- BKE_collection_object_add(bmain, collection_new, ob_new);
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection_new, ob_new) {
+ if (ob_new != NULL && ob_new->id.override_library != NULL) {
+ if (ob_reference != NULL) {
+ Base *base;
+ if ((base = BKE_view_layer_base_find(view_layer, ob_new)) == NULL) {
+ BKE_collection_object_add_from(bmain, scene, ob_reference, ob_new);
+ base = BKE_view_layer_base_find(view_layer, ob_new);
DEG_id_tag_update_ex(bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS);
}
+
+ if (ob_new == (Object *)ob_reference->id.newid) {
+ /* TODO: is setting active needed? */
+ BKE_view_layer_base_select_and_set_active(view_layer, base);
+ }
+ }
+ else if (BKE_view_layer_base_find(view_layer, ob_new) == NULL) {
+ BKE_collection_object_add(bmain, collection_new, ob_new);
+ DEG_id_tag_update_ex(bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS);
}
}
- FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
- break;
- }
- case ID_OB: {
- BKE_collection_object_add_from(
- bmain, scene, (Object *)id_root, ((Object *)id_root->newid));
- break;
}
- default:
- break;
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
+ break;
+ }
+ case ID_OB: {
+ BKE_collection_object_add_from(bmain, scene, (Object *)id_root, ((Object *)id_root->newid));
+ break;
}
+ default:
+ break;
+ }
- /* We need to ensure all new overrides of objects are properly instantiated. */
- LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
- Object *ob_new = (Object *)ob->id.newid;
- if (ob_new != NULL) {
- BLI_assert(ob_new->id.override_library != NULL &&
- ob_new->id.override_library->reference == &ob->id);
-
- Collection *default_instantiating_collection = NULL;
- if (BKE_view_layer_base_find(view_layer, ob_new) == NULL) {
- if (default_instantiating_collection == NULL) {
- switch (GS(id_root->name)) {
- case ID_GR: {
- default_instantiating_collection = BKE_collection_add(
- bmain, (Collection *)id_root, "OVERRIDE_HIDDEN");
- break;
- }
- case ID_OB: {
- /* Add the new container collection to one of the collections instantiating the
- * root object, or scene's master collection if none found. */
- Object *ob_root = (Object *)id_root;
- LISTBASE_FOREACH (Collection *, collection, &bmain->collections) {
- if (BKE_collection_has_object(collection, ob_root) &&
- BKE_view_layer_has_collection(view_layer, collection) &&
- !ID_IS_LINKED(collection) && !ID_IS_OVERRIDE_LIBRARY(collection)) {
- default_instantiating_collection = BKE_collection_add(
- bmain, collection, "OVERRIDE_HIDDEN");
- }
- }
- if (default_instantiating_collection == NULL) {
+ /* We need to ensure all new overrides of objects are properly instantiated. */
+ LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
+ Object *ob_new = (Object *)ob->id.newid;
+ if (ob_new != NULL) {
+ BLI_assert(ob_new->id.override_library != NULL &&
+ ob_new->id.override_library->reference == &ob->id);
+
+ Collection *default_instantiating_collection = NULL;
+ if (BKE_view_layer_base_find(view_layer, ob_new) == NULL) {
+ if (default_instantiating_collection == NULL) {
+ switch (GS(id_root->name)) {
+ case ID_GR: {
+ default_instantiating_collection = BKE_collection_add(
+ bmain, (Collection *)id_root, "OVERRIDE_HIDDEN");
+ break;
+ }
+ case ID_OB: {
+ /* Add the new container collection to one of the collections instantiating the
+ * root object, or scene's master collection if none found. */
+ Object *ob_root = (Object *)id_root;
+ LISTBASE_FOREACH (Collection *, collection, &bmain->collections) {
+ if (BKE_collection_has_object(collection, ob_root) &&
+ BKE_view_layer_has_collection(view_layer, collection) &&
+ !ID_IS_LINKED(collection) && !ID_IS_OVERRIDE_LIBRARY(collection)) {
default_instantiating_collection = BKE_collection_add(
- bmain, scene->master_collection, "OVERRIDE_HIDDEN");
+ bmain, collection, "OVERRIDE_HIDDEN");
}
- break;
}
- default:
- BLI_assert(0);
+ if (default_instantiating_collection == NULL) {
+ default_instantiating_collection = BKE_collection_add(
+ bmain, scene->master_collection, "OVERRIDE_HIDDEN");
+ }
+ break;
}
- /* Hide the collection from viewport and render. */
- default_instantiating_collection->flag |= COLLECTION_RESTRICT_VIEWPORT |
- COLLECTION_RESTRICT_RENDER;
+ default:
+ BLI_assert(0);
}
+ /* Hide the collection from viewport and render. */
+ default_instantiating_collection->flag |= COLLECTION_RESTRICT_VIEWPORT |
+ COLLECTION_RESTRICT_RENDER;
+ }
+
+ BKE_collection_object_add(bmain, default_instantiating_collection, ob_new);
+ DEG_id_tag_update_ex(bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS);
+ }
+ }
+ }
+}
- BKE_collection_object_add(bmain, default_instantiating_collection, ob_new);
- DEG_id_tag_update_ex(bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS);
+/**
+ * Advanced 'smart' function to create fully functional overrides.
+ *
+ * \note Currently it only does special things if given \a id_root is an object of collection, more
+ * specific behaviors may be added in the future for other ID types.
+ *
+ * \note It will overrides all IDs tagged with \a LIB_TAG_DOIT, and it does not clear that tag at
+ * its beginning, so caller code can add extra data-blocks to be overridden as well.
+ *
+ * \note In the future that same function may be extended to support 'refresh' of overrides
+ * (rebuilding overrides from linked data, trying to preserve local overrides already defined).
+ *
+ * \param id_root The root ID to create an override from.
+ * \param id_reference some reference ID used to do some post-processing after overrides have been
+ * created, may be NULL. Typically, the Empty object instantiating the linked
+ * collection we override, currently.
+ * \return true if override was successfully created.
+ */
+bool BKE_lib_override_library_create(
+ Main *bmain, Scene *scene, ViewLayer *view_layer, ID *id_root, ID *id_reference)
+{
+ const bool success = lib_override_library_create_do(bmain, id_root);
+
+ if (!success) {
+ return success;
+ }
+
+ lib_override_library_create_post_process(bmain, scene, view_layer, id_root, id_reference);
+
+ /* Cleanup. */
+ BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
+
+ return success;
+}
+
+/**
+ * Advanced 'smart' function to resync, re-create fully functional overrides up-to-date with linked
+ * data, from an existing override hierarchy.
+ *
+ * \param id_root The root liboverride ID to resync from.
+ * \return true if override was successfully resynced.
+ */
+bool BKE_lib_override_library_resync(Main *bmain, Scene *scene, ViewLayer *view_layer, ID *id_root)
+{
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root));
+
+ /* Tag all collections and objects, as well as other IDs using them. */
+ id_root->tag |= LIB_TAG_DOIT;
+ ID *id_root_reference = id_root->override_library->reference;
+
+ /* Make a mapping 'linked reference IDs' -> 'Local override IDs' of existing overrides, and tag
+ * linked reference ones to be overridden again. */
+ BKE_lib_override_library_override_group_tag(bmain, id_root, LIB_TAG_DOIT, true);
+
+ GHash *linkedref_to_old_override = BLI_ghash_new(
+ BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__);
+ ID *id;
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ if (id->tag & LIB_TAG_DOIT && ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
+ /* While this should not happen in typical cases (and won't be properly supported here), user
+ * is free to do all kind of very bad things, including having different local overrides of a
+ * same linked ID in a same hierarchy... */
+ if (!BLI_ghash_haskey(linkedref_to_old_override, id->override_library->reference)) {
+ BLI_ghash_insert(linkedref_to_old_override, id->override_library->reference, id);
+ id->override_library->reference->tag |= LIB_TAG_DOIT;
+ }
+ }
+ }
+ FOREACH_MAIN_ID_END;
+
+ /* Make new override from linked data. */
+ /* Note that this call also remap all pointers of tagged IDs from old override IDs to new
+ * override IDs (including within the old overrides themselves, since those are tagged too
+ * above). */
+ const bool success = lib_override_library_create_do(bmain, id_root_reference);
+
+ if (!success) {
+ return success;
+ }
+
+ ListBase *lb;
+ FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) {
+ FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id) {
+ if (id->tag & LIB_TAG_DOIT && id->newid != NULL && ID_IS_LINKED(id)) {
+ ID *id_override_new = id->newid;
+ ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id);
+
+ if (id_override_old != NULL) {
+ /* Swap the names between old override ID and new one. */
+ char id_name_buf[MAX_ID_NAME];
+ memcpy(id_name_buf, id_override_old->name, sizeof(id_name_buf));
+ memcpy(id_override_old->name, id_override_new->name, sizeof(id_override_old->name));
+ memcpy(id_override_new->name, id_name_buf, sizeof(id_override_new->name));
+ /* Note that this is very efficient way to keep BMain IDs ordered as expected after
+ * swapping their names.
+ * However, one has to be very careful with this when iterating over the listbase at the
+ * same time. Here it works because we only execute this code when we are in the linked
+ * IDs, which are always *after* all local ones, and we only affect local IDs. */
+ BLI_listbase_swaplinks(lb, id_override_old, id_override_new);
+
+ /* Remap the whole local IDs to use the new override. */
+ BKE_libblock_remap(
+ bmain, id_override_old, id_override_new, ID_REMAP_SKIP_INDIRECT_USAGE);
+
+ /* Copy over overrides rules from old override ID to new one. */
+ BLI_duplicatelist(&id_override_new->override_library->properties,
+ &id_override_old->override_library->properties);
+ for (IDOverrideLibraryProperty *
+ op_new = id_override_new->override_library->properties.first,
+ *op_old = id_override_old->override_library->properties.first;
+ op_new;
+ op_new = op_new->next, op_old = op_old->next) {
+ lib_override_library_property_copy(op_new, op_old);
+ }
+
+ /* 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);
+
+ RNA_struct_override_apply(
+ bmain, &rnaptr_dst, &rnaptr_src, NULL, id_override_new->override_library);
}
}
}
+ FOREACH_MAIN_LISTBASE_ID_END;
}
+ FOREACH_MAIN_LISTBASE_END;
+
+ /* Delete old override IDs. */
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ if (id->tag & LIB_TAG_DOIT && id->newid != NULL && ID_IS_LINKED(id)) {
+ ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id);
+
+ if (id_override_old != NULL) {
+ BKE_id_delete(bmain, id_override_old);
+ }
+ }
+ }
+ FOREACH_MAIN_ID_END;
+
+ /* Essentially ensures that potentially new overrides of new objects will be instantiated. */
+ lib_override_library_create_post_process(bmain, scene, view_layer, id_root_reference, id_root);
/* Cleanup. */
+ BLI_ghash_free(linkedref_to_old_override, NULL, NULL);
+
BKE_main_id_clear_newpoins(bmain);
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
return success;
}
+/**
+ * Advanced 'smart' function to delete library overrides (including their existing override
+ * hierarchy) and remap their usages to their linked reference IDs.
+ *
+ * \note All IDs tagged with `LIB_TAG_DOIT` will be deleted.
+ *
+ * \param id_root The root liboverride ID to resync from.
+ */
+void BKE_lib_override_library_delete(Main *bmain, ID *id_root)
+{
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root));
+
+ /* Tag all collections and objects, as well as other IDs using them. */
+ id_root->tag |= LIB_TAG_DOIT;
+
+ /* Make a mapping 'linked reference IDs' -> 'Local override IDs' of existing overrides, and tag
+ * linked reference ones to be overridden again. */
+ BKE_lib_override_library_override_group_tag(bmain, id_root, LIB_TAG_DOIT, true);
+
+ ID *id;
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ if (id->tag & LIB_TAG_DOIT) {
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
+ ID *id_override_reference = id->override_library->reference;
+
+ /* Remap the whole local IDs to use the linked data. */
+ BKE_libblock_remap(bmain, id, id_override_reference, ID_REMAP_SKIP_INDIRECT_USAGE);
+ }
+ }
+ }
+ FOREACH_MAIN_ID_END;
+
+ /* Delete the override IDs. */
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ if (id->tag & LIB_TAG_DOIT) {
+ BKE_id_delete(bmain, id);
+ }
+ }
+ FOREACH_MAIN_ID_END;
+
+ /* Should not actually be needed here... */
+ BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
+}
+
BLI_INLINE IDOverrideLibraryRuntime *override_library_rna_path_runtime_ensure(
IDOverrideLibrary *override)
{
@@ -1474,6 +1685,20 @@ void BKE_lib_override_library_update(Main *bmain, ID *local)
/* XXX And crashing in complex cases (e.g. because depsgraph uses same data...). */
BKE_id_free_ex(bmain, tmp_id, LIB_ID_FREE_NO_UI_USER, true);
+ if (GS(local->name) == ID_AR) {
+ /* Funtime again, thanks to bone pointers in pose data of objects. We keep same ID addresses,
+ * but internal data has changed for sure, so we need to invalidate posebones caches. */
+ LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
+ if (ob->pose != NULL && ob->data == local) {
+ BLI_assert(ob->type == OB_ARMATURE);
+ ob->pose->flag |= POSE_RECALC;
+ /* We need to clear pose bone pointers immediately, some code may access those before pose
+ * is actually recomputed, which can lead to segfault. */
+ BKE_pose_clear_pointers(ob->pose);
+ }
+ }
+ }
+
if (local->override_library->storage) {
/* We know this datablock is not used anywhere besides local->override->storage. */
/* XXX For until we get fully shadow copies, we still need to ensure storage releases
diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c
index d4246056efe..c88513ec2af 100644
--- a/source/blender/blenkernel/intern/lib_remap.c
+++ b/source/blender/blenkernel/intern/lib_remap.c
@@ -244,17 +244,17 @@ static void libblock_remap_data_preprocess(IDRemap *r_id_remap_data)
ID *old_id = r_id_remap_data->old_id;
if (!old_id || GS(old_id->name) == ID_AR) {
Object *ob = (Object *)r_id_remap_data->id_owner;
- /* Object's pose holds reference to armature bones... sic */
- /* Note that in theory, we should have to bother about
- * linked/non-linked/never-null/etc. flags/states.
+ /* Object's pose holds reference to armature bones. sic */
+ /* Note that in theory, we should have to bother about linked/non-linked/never-null/etc.
+ * flags/states.
* Fortunately, this is just a tag, so we can accept to 'over-tag' a bit for pose recalc,
* and avoid another complex and risky condition nightmare like the one we have in
- * foreach_libblock_remap_callback()... */
+ * foreach_libblock_remap_callback(). */
if (ob->pose && (!old_id || ob->data == old_id)) {
BLI_assert(ob->type == OB_ARMATURE);
ob->pose->flag |= POSE_RECALC;
- /* We need to clear pose bone pointers immediately, things like undo writefile may be
- * called before pose is actually recomputed, can lead to segfault... */
+ /* We need to clear pose bone pointers immediately, some code may access those before
+ * pose is actually recomputed, which can lead to segfault. */
BKE_pose_clear_pointers(ob->pose);
}
}
diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c
index 48c98be626d..4bbe3a4a26b 100644
--- a/source/blender/blenkernel/intern/library.c
+++ b/source/blender/blenkernel/intern/library.c
@@ -75,6 +75,12 @@ IDTypeInfo IDType_ID_LI = {
.free_data = library_free_data,
.make_local = NULL,
.foreach_id = library_foreach_id,
+ .foreach_cache = NULL,
+
+ .blend_write = NULL,
+ .blend_read_data = NULL,
+ .blend_read_lib = NULL,
+ .blend_read_expand = NULL,
};
void BKE_library_filepath_set(Main *bmain, Library *lib, const char *filepath)
diff --git a/source/blender/blenkernel/intern/light.c b/source/blender/blenkernel/intern/light.c
index f42df6765c4..976fa010057 100644
--- a/source/blender/blenkernel/intern/light.c
+++ b/source/blender/blenkernel/intern/light.c
@@ -134,6 +134,12 @@ IDTypeInfo IDType_ID_LA = {
.free_data = light_free_data,
.make_local = NULL,
.foreach_id = light_foreach_id,
+ .foreach_cache = NULL,
+
+ .blend_write = NULL,
+ .blend_read_data = NULL,
+ .blend_read_lib = NULL,
+ .blend_read_expand = NULL,
};
Light *BKE_light_add(Main *bmain, const char *name)
diff --git a/source/blender/blenkernel/intern/lightprobe.c b/source/blender/blenkernel/intern/lightprobe.c
index f73df66b43d..b4b13306112 100644
--- a/source/blender/blenkernel/intern/lightprobe.c
+++ b/source/blender/blenkernel/intern/lightprobe.c
@@ -69,6 +69,12 @@ IDTypeInfo IDType_ID_LP = {
.free_data = NULL,
.make_local = NULL,
.foreach_id = lightprobe_foreach_id,
+ .foreach_cache = NULL,
+
+ .blend_write = NULL,
+ .blend_read_data = NULL,
+ .blend_read_lib = NULL,
+ .blend_read_expand = NULL,
};
void BKE_lightprobe_type_set(LightProbe *probe, const short lightprobe_type)
diff --git a/source/blender/blenkernel/intern/linestyle.c b/source/blender/blenkernel/intern/linestyle.c
index a389af5c47f..8dc44a32eaa 100644
--- a/source/blender/blenkernel/intern/linestyle.c
+++ b/source/blender/blenkernel/intern/linestyle.c
@@ -204,6 +204,12 @@ IDTypeInfo IDType_ID_LS = {
.free_data = linestyle_free_data,
.make_local = NULL,
.foreach_id = linestyle_foreach_id,
+ .foreach_cache = NULL,
+
+ .blend_write = NULL,
+ .blend_read_data = NULL,
+ .blend_read_lib = NULL,
+ .blend_read_expand = NULL,
};
static const char *modifier_name[LS_MODIFIER_NUM] = {
diff --git a/source/blender/blenkernel/intern/mask.c b/source/blender/blenkernel/intern/mask.c
index 444ed0c65b5..79b8a30242e 100644
--- a/source/blender/blenkernel/intern/mask.c
+++ b/source/blender/blenkernel/intern/mask.c
@@ -109,6 +109,12 @@ IDTypeInfo IDType_ID_MSK = {
.free_data = mask_free_data,
.make_local = NULL,
.foreach_id = mask_foreach_id,
+ .foreach_cache = NULL,
+
+ .blend_write = NULL,
+ .blend_read_data = NULL,
+ .blend_read_lib = NULL,
+ .blend_read_expand = NULL,
};
static struct {
diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c
index 0520ba3faae..d521d6c8a99 100644
--- a/source/blender/blenkernel/intern/material.c
+++ b/source/blender/blenkernel/intern/material.c
@@ -175,6 +175,12 @@ IDTypeInfo IDType_ID_MA = {
.free_data = material_free_data,
.make_local = NULL,
.foreach_id = material_foreach_id,
+ .foreach_cache = NULL,
+
+ .blend_write = NULL,
+ .blend_read_data = NULL,
+ .blend_read_lib = NULL,
+ .blend_read_expand = NULL,
};
void BKE_gpencil_material_attr_init(Material *ma)
diff --git a/source/blender/blenkernel/intern/mball.c b/source/blender/blenkernel/intern/mball.c
index d2f7ee68430..de477ee03b1 100644
--- a/source/blender/blenkernel/intern/mball.c
+++ b/source/blender/blenkernel/intern/mball.c
@@ -125,6 +125,12 @@ IDTypeInfo IDType_ID_MB = {
.free_data = metaball_free_data,
.make_local = NULL,
.foreach_id = metaball_foreach_id,
+ .foreach_cache = NULL,
+
+ .blend_write = NULL,
+ .blend_read_data = NULL,
+ .blend_read_lib = NULL,
+ .blend_read_expand = NULL,
};
/* Functions */
diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c
index 2a16d0eb0f8..a7568bcd6ea 100644
--- a/source/blender/blenkernel/intern/mesh.c
+++ b/source/blender/blenkernel/intern/mesh.c
@@ -23,6 +23,9 @@
#include "MEM_guardedalloc.h"
+/* Allow using deprecated functionality for .blend file I/O. */
+#define DNA_DEPRECATED_ALLOW
+
#include "DNA_defaults.h"
#include "DNA_key_types.h"
#include "DNA_material_types.h"
@@ -32,6 +35,7 @@
#include "BLI_bitmap.h"
#include "BLI_edgehash.h"
+#include "BLI_endian_switch.h"
#include "BLI_ghash.h"
#include "BLI_hash.h"
#include "BLI_linklist.h"
@@ -43,6 +47,7 @@
#include "BLT_translation.h"
#include "BKE_anim_data.h"
+#include "BKE_deform.h"
#include "BKE_editmesh.h"
#include "BKE_global.h"
#include "BKE_idtype.h"
@@ -63,6 +68,8 @@
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
+#include "BLO_read_write.h"
+
static void mesh_clear_geometry(Mesh *mesh);
static void mesh_tessface_clear_intern(Mesh *mesh, int free_customdata);
@@ -163,6 +170,158 @@ static void mesh_foreach_id(ID *id, LibraryForeachIDData *data)
}
}
+static void mesh_blend_write(BlendWriter *writer, ID *id, const void *id_address)
+{
+ Mesh *mesh = (Mesh *)id;
+ if (mesh->id.us > 0 || BLO_write_is_undo(writer)) {
+ /* cache only - don't write */
+ mesh->mface = NULL;
+ mesh->totface = 0;
+ memset(&mesh->fdata, 0, sizeof(mesh->fdata));
+ memset(&mesh->runtime, 0, sizeof(mesh->runtime));
+
+ BLO_write_id_struct(writer, Mesh, id_address, &mesh->id);
+ BKE_id_blend_write(writer, &mesh->id);
+
+ /* direct data */
+ if (mesh->adt) {
+ BKE_animdata_blend_write(writer, mesh->adt);
+ }
+
+ BLO_write_pointer_array(writer, mesh->totcol, mesh->mat);
+ BLO_write_raw(writer, sizeof(MSelect) * mesh->totselect, mesh->mselect);
+
+ CustomData_blend_write(writer, &mesh->vdata, mesh->totvert, CD_MASK_MESH.vmask, &mesh->id);
+ CustomData_blend_write(writer, &mesh->edata, mesh->totedge, CD_MASK_MESH.emask, &mesh->id);
+ /* fdata is really a dummy - written so slots align */
+ CustomData_blend_write(writer, &mesh->fdata, mesh->totface, CD_MASK_MESH.fmask, &mesh->id);
+ CustomData_blend_write(writer, &mesh->ldata, mesh->totloop, CD_MASK_MESH.lmask, &mesh->id);
+ CustomData_blend_write(writer, &mesh->pdata, mesh->totpoly, CD_MASK_MESH.pmask, &mesh->id);
+ }
+}
+
+static void mesh_blend_read_data(BlendDataReader *reader, ID *id)
+{
+ Mesh *mesh = (Mesh *)id;
+ BLO_read_pointer_array(reader, (void **)&mesh->mat);
+
+ 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->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);
+
+ CustomData_blend_read(reader, &mesh->vdata, mesh->totvert);
+ CustomData_blend_read(reader, &mesh->edata, mesh->totedge);
+ CustomData_blend_read(reader, &mesh->fdata, mesh->totface);
+ CustomData_blend_read(reader, &mesh->ldata, mesh->totloop);
+ CustomData_blend_read(reader, &mesh->pdata, mesh->totpoly);
+
+ mesh->texflag &= ~ME_AUTOSPACE_EVALUATED;
+ mesh->edit_mesh = NULL;
+ BKE_mesh_runtime_reset(mesh);
+
+ /* happens with old files */
+ if (mesh->mselect == NULL) {
+ mesh->totselect = 0;
+ }
+
+ /* Multires data */
+ BLO_read_data_address(reader, &mesh->mr);
+ if (mesh->mr) {
+ BLO_read_list(reader, &mesh->mr->levels);
+ MultiresLevel *lvl = mesh->mr->levels.first;
+
+ CustomData_blend_read(reader, &mesh->mr->vdata, lvl->totvert);
+ BKE_defvert_blend_read(
+ reader, lvl->totvert, CustomData_get(&mesh->mr->vdata, 0, CD_MDEFORMVERT));
+ CustomData_blend_read(reader, &mesh->mr->fdata, lvl->totface);
+
+ BLO_read_data_address(reader, &mesh->mr->edge_flags);
+ BLO_read_data_address(reader, &mesh->mr->edge_creases);
+
+ BLO_read_data_address(reader, &mesh->mr->verts);
+
+ /* If mesh has the same number of vertices as the
+ * highest multires level, load the current mesh verts
+ * into multires and discard the old data. Needed
+ * because some saved files either do not have a verts
+ * array, or the verts array contains out-of-date
+ * data. */
+ if (mesh->totvert == ((MultiresLevel *)mesh->mr->levels.last)->totvert) {
+ if (mesh->mr->verts) {
+ MEM_freeN(mesh->mr->verts);
+ }
+ mesh->mr->verts = MEM_dupallocN(mesh->mvert);
+ }
+
+ for (; lvl; lvl = lvl->next) {
+ BLO_read_data_address(reader, &lvl->verts);
+ BLO_read_data_address(reader, &lvl->faces);
+ BLO_read_data_address(reader, &lvl->edges);
+ BLO_read_data_address(reader, &lvl->colfaces);
+ }
+ }
+
+ /* if multires is present but has no valid vertex data,
+ * there's no way to recover it; silently remove multires */
+ if (mesh->mr && !mesh->mr->verts) {
+ multires_free(mesh->mr);
+ mesh->mr = NULL;
+ }
+
+ if ((BLO_read_requires_endian_switch(reader)) && mesh->tface) {
+ TFace *tf = mesh->tface;
+ for (int i = 0; i < mesh->totface; i++, tf++) {
+ BLI_endian_switch_uint32_array(tf->col, 4);
+ }
+ }
+}
+
+static void mesh_blend_read_lib(BlendLibReader *reader, ID *id)
+{
+ Mesh *me = (Mesh *)id;
+ /* this check added for python created meshes */
+ if (me->mat) {
+ for (int i = 0; i < me->totcol; i++) {
+ BLO_read_id_address(reader, me->id.lib, &me->mat[i]);
+ }
+ }
+ else {
+ me->totcol = 0;
+ }
+
+ BLO_read_id_address(reader, me->id.lib, &me->ipo); // XXX: deprecated: old anim sys
+ BLO_read_id_address(reader, me->id.lib, &me->key);
+ BLO_read_id_address(reader, me->id.lib, &me->texcomesh);
+}
+
+static void mesh_read_expand(BlendExpander *expander, ID *id)
+{
+ Mesh *me = (Mesh *)id;
+ for (int a = 0; a < me->totcol; a++) {
+ BLO_expand(expander, me->mat[a]);
+ }
+
+ BLO_expand(expander, me->key);
+ BLO_expand(expander, me->texcomesh);
+}
+
IDTypeInfo IDType_ID_ME = {
.id_code = ID_ME,
.id_filter = FILTER_ID_ME,
@@ -178,6 +337,12 @@ IDTypeInfo IDType_ID_ME = {
.free_data = mesh_free_data,
.make_local = NULL,
.foreach_id = mesh_foreach_id,
+ .foreach_cache = NULL,
+
+ .blend_write = mesh_blend_write,
+ .blend_read_data = mesh_blend_read_data,
+ .blend_read_lib = mesh_blend_read_lib,
+ .blend_read_expand = mesh_read_expand,
};
enum {
diff --git a/source/blender/blenkernel/intern/mesh_remesh_voxel.c b/source/blender/blenkernel/intern/mesh_remesh_voxel.c
index 05cc9769bf7..ab07e08c928 100644
--- a/source/blender/blenkernel/intern/mesh_remesh_voxel.c
+++ b/source/blender/blenkernel/intern/mesh_remesh_voxel.c
@@ -48,6 +48,8 @@
#include "bmesh_tools.h"
+#include "admmpd_api.h"
+
#ifdef WITH_OPENVDB
# include "openvdb_capi.h"
#endif
@@ -167,11 +169,11 @@ static Mesh *BKE_mesh_remesh_quadriflow(Mesh *input_mesh,
void *update_cb,
void *update_cb_data)
{
- /* Ensure that the triangulated mesh data is up to data */
+ /* Ensure that the triangulated mesh data is up to data. */
BKE_mesh_runtime_looptri_recalc(input_mesh);
const MLoopTri *looptri = BKE_mesh_runtime_looptri_ensure(input_mesh);
- /* Gather the required data for export to the internal quadiflow mesh format */
+ /* Gather the required data for export to the internal quadiflow mesh format. */
MVertTri *verttri = MEM_callocN(sizeof(*verttri) * BKE_mesh_runtime_looptri_len(input_mesh),
"remesh_looptri");
BKE_mesh_runtime_verttri_from_looptri(
@@ -197,7 +199,7 @@ static Mesh *BKE_mesh_remesh_quadriflow(Mesh *input_mesh,
faces[i * 3 + 2] = vt->tri[2];
}
- /* Fill out the required input data */
+ /* Fill out the required input data. */
QuadriflowRemeshData qrd;
qrd.totfaces = totfaces;
@@ -215,7 +217,7 @@ static Mesh *BKE_mesh_remesh_quadriflow(Mesh *input_mesh,
qrd.out_faces = NULL;
- /* Run the remesher */
+ /* Run the remesher. */
QFLOW_quadriflow_remesh(&qrd, update_cb, update_cb_data);
MEM_freeN(verts);
@@ -223,7 +225,7 @@ static Mesh *BKE_mesh_remesh_quadriflow(Mesh *input_mesh,
MEM_freeN(verttri);
if (qrd.out_faces == NULL) {
- /* The remeshing was canceled */
+ /* The remeshing was canceled. */
return NULL;
}
@@ -267,11 +269,11 @@ static Mesh *BKE_mesh_remesh_quadriflow(Mesh *input_mesh,
#ifdef WITH_TETGEN
static Mesh *BKE_mesh_remesh_tetgen(Mesh *input_mesh, unsigned int **tets, int *numtets)
{
- // Ensure that the triangulated mesh data is up to data
+ /* Ensure that the triangulated mesh data is up to data. */
BKE_mesh_runtime_looptri_recalc(input_mesh);
const MLoopTri *looptri = BKE_mesh_runtime_looptri_ensure(input_mesh);
- // Gather the required data
+ /* Gather the required data. */
MVertTri *verttri = MEM_callocN(sizeof(*verttri) * BKE_mesh_runtime_looptri_len(input_mesh),
"remesh_looptri");
BKE_mesh_runtime_verttri_from_looptri(
@@ -297,7 +299,7 @@ static Mesh *BKE_mesh_remesh_tetgen(Mesh *input_mesh, unsigned int **tets, int *
faces[i * 3 + 2] = vt->tri[2];
}
- // Call the tetgen remesher
+ /* Call the tetgen remesher. */
TetGenRemeshData tg;
init_tetgenremeshdata(&tg);
tg.in_totfaces = totfaces;
@@ -317,7 +319,7 @@ static Mesh *BKE_mesh_remesh_tetgen(Mesh *input_mesh, unsigned int **tets, int *
Mesh *mesh = NULL;
if (success) {
- // Construct the new output mesh
+ /* Construct the new output mesh. */
mesh = BKE_mesh_new_nomain(tg.out_totverts, 0, 0, (tg.out_totfacets * 3), tg.out_totfacets);
for (int i = 0; i < tg.out_totverts; i++) {
@@ -339,10 +341,9 @@ static Mesh *BKE_mesh_remesh_tetgen(Mesh *input_mesh, unsigned int **tets, int *
*numtets = tg.out_tottets;
*tets = (unsigned int *)MEM_malloc_arrayN(
tg.out_tottets * 4, sizeof(unsigned int), "remesh_output_tets");
- //*tets = (unsigned int *)malloc(tg.out_tottets*4*sizeof(unsigned int));
memcpy(*tets, tg.out_tets, tg.out_tottets * 4 * sizeof(unsigned int));
- } // end success
+ }
if (tg.out_verts != NULL) {
MEM_freeN(tg.out_verts);
@@ -375,6 +376,118 @@ struct Mesh *BKE_mesh_remesh_tetgen_to_mesh_nomain(struct Mesh *mesh,
return NULL;
}
+static Mesh *BKE_mesh_remesh_tetlattice(struct Mesh *input_mesh,
+ int subdivisions,
+ unsigned int **tets,
+ int *numtets)
+{
+
+ /* Ensure that the triangulated mesh data is up to data. */
+ BKE_mesh_runtime_looptri_recalc(input_mesh);
+ const MLoopTri *looptri = BKE_mesh_runtime_looptri_ensure(input_mesh);
+
+ /* Gather the required data. */
+ MVertTri *verttri = 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));
+
+ unsigned int totfaces = BKE_mesh_runtime_looptri_len(input_mesh);
+ unsigned int totverts = input_mesh->totvert;
+ float *verts = (float *)MEM_malloc_arrayN(totverts * 3, sizeof(float), "remesh_input_verts");
+ unsigned int *faces = (unsigned int *)MEM_malloc_arrayN(
+ totfaces * 3, sizeof(unsigned int), "remesh_intput_faces");
+
+ for (unsigned int i = 0; i < totverts; i++) {
+ MVert *mvert = &input_mesh->mvert[i];
+ verts[i * 3] = mvert->co[0];
+ verts[i * 3 + 1] = mvert->co[1];
+ verts[i * 3 + 2] = mvert->co[2];
+ }
+
+ for (unsigned int i = 0; i < totfaces; i++) {
+ MVertTri *vt = &verttri[i];
+ faces[i * 3] = vt->tri[0];
+ faces[i * 3 + 1] = vt->tri[1];
+ faces[i * 3 + 2] = vt->tri[2];
+ }
+
+ float *out_verts = NULL;
+ int out_totverts = 0;
+ admmpd_compute_lattice(
+ subdivisions,
+ verts, totverts,
+ faces, totfaces,
+ &out_verts, &out_totverts,
+ tets, numtets);
+ bool success = out_totverts>0 && *numtets>0;
+
+ MEM_freeN(verts);
+ verts = NULL;
+ MEM_freeN(faces);
+ faces = NULL;
+ MEM_freeN(verttri);
+ verttri = NULL;
+
+ Mesh *mesh = NULL;
+ if (success) {
+
+ int nt = *numtets;
+ int nf = *numtets * 4;
+
+ /* Construct the new output mesh. */
+ mesh = BKE_mesh_new_nomain(out_totverts, 0, 0, (nf*3), nf);
+
+ for (int i = 0; i < out_totverts; i++) {
+ copy_v3_v3(mesh->mvert[i].co, &out_verts[i * 3]);
+ }
+
+ MPoly *mp = mesh->mpoly;
+ MLoop *ml = mesh->mloop;
+ for (int i=0; i<nt; ++i) {
+
+ int tet[4];
+ tet[0] = (*tets)[i*4+0];
+ tet[1] = (*tets)[i*4+1];
+ tet[2] = (*tets)[i*4+2];
+ tet[3] = (*tets)[i*4+3];
+
+ int tet_facets[4*3] = {
+ 0, 2, 1,
+ 0, 1, 3,
+ 0, 3, 2,
+ 1, 2, 3
+ };
+
+ for (int j = 0; j < 4; j++, mp++, ml += 3) {
+ mp->loopstart = (int)(ml - mesh->mloop);
+ mp->totloop = 3;
+ ml[0].v = tet[tet_facets[j*3+0]];
+ ml[1].v = tet[tet_facets[j*3+1]];
+ ml[2].v = tet[tet_facets[j*3+2]];
+ }
+ }
+
+ }
+ BKE_mesh_calc_edges(mesh, false, false);
+ BKE_mesh_calc_normals(mesh);
+
+ if (out_verts != NULL) {
+ MEM_freeN(out_verts);
+ out_verts = NULL;
+ }
+
+ return mesh;
+}
+
+struct Mesh *BKE_mesh_remesh_tetlattice_to_mesh_nomain(struct Mesh *mesh,
+ int subdivisions,
+ unsigned int **tets,
+ int *numtets)
+{
+ return BKE_mesh_remesh_tetlattice(mesh, subdivisions, tets, numtets);
+}
+
Mesh *BKE_mesh_remesh_quadriflow_to_mesh_nomain(Mesh *mesh,
int target_faces,
int seed,
diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c
index dcac7b01899..55e43a2b8ed 100644
--- a/source/blender/blenkernel/intern/movieclip.c
+++ b/source/blender/blenkernel/intern/movieclip.c
@@ -162,6 +162,11 @@ IDTypeInfo IDType_ID_MC = {
.make_local = NULL,
.foreach_id = movie_clip_foreach_id,
.foreach_cache = movie_clip_foreach_cache,
+
+ .blend_write = NULL,
+ .blend_read_data = NULL,
+ .blend_read_lib = NULL,
+ .blend_read_expand = NULL,
};
/*********************** movieclip buffer loaders *************************/
diff --git a/source/blender/blenkernel/intern/multires_reshape.c b/source/blender/blenkernel/intern/multires_reshape.c
index 64cc9130e25..5bcf8f62f86 100644
--- a/source/blender/blenkernel/intern/multires_reshape.c
+++ b/source/blender/blenkernel/intern/multires_reshape.c
@@ -189,6 +189,12 @@ void multiresModifier_subdivide_to_level(struct Object *object,
}
Mesh *coarse_mesh = object->data;
+ if (coarse_mesh->totloop == 0) {
+ /* If there are no loops in the mesh implies there is no CD_MDISPS as well. So can early output
+ * from here as there is nothing to subdivide. */
+ return;
+ }
+
MultiresReshapeContext reshape_context;
/* There was no multires at all, all displacement is at 0. Can simply make sure all mdisps grids
diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c
index 1ba82b352d1..0c3abc70a43 100644
--- a/source/blender/blenkernel/intern/nla.c
+++ b/source/blender/blenkernel/intern/nla.c
@@ -54,6 +54,8 @@
#include "BKE_nla.h"
#include "BKE_sound.h"
+#include "BLO_read_write.h"
+
#include "RNA_access.h"
#include "nla_private.h"
@@ -1361,6 +1363,25 @@ static void nlastrip_fix_resize_overlaps(NlaStrip *strip)
}
}
+/** Recalculate the start and end frames for the strip to match the bounds of its action such that
+ * the overall NLA animation result is unchanged. */
+void BKE_nlastrip_recalculate_bounds_sync_action(NlaStrip *strip)
+{
+ float prev_actstart;
+
+ if (strip == NULL || strip->type != NLASTRIP_TYPE_CLIP) {
+ return;
+ }
+
+ prev_actstart = strip->actstart;
+
+ calc_action_range(strip->act, &strip->actstart, &strip->actend, 0);
+
+ /* Set start such that key's do not visually move, to preserve the overall animation result. */
+ strip->start += (strip->actstart - prev_actstart) * strip->scale;
+
+ BKE_nlastrip_recalculate_bounds(strip);
+}
/* Recalculate the start and end frames for the current strip, after changing
* the extents of the action or the mapping (repeats or scale factor) info
*/
@@ -2133,11 +2154,7 @@ void BKE_nla_tweakmode_exit(AnimData *adt)
/* must be action-clip only (transitions don't have scale) */
if ((strip->type == NLASTRIP_TYPE_CLIP) && (strip->act)) {
- /* recalculate the length of the action */
- calc_action_range(strip->act, &strip->actstart, &strip->actend, 0);
-
- /* adjust the strip extents in response to this */
- BKE_nlastrip_recalculate_bounds(strip);
+ BKE_nlastrip_recalculate_bounds_sync_action(strip);
}
}
@@ -2151,11 +2168,7 @@ void BKE_nla_tweakmode_exit(AnimData *adt)
/* sync strip extents if this strip uses the same action */
if ((adt->actstrip) && (adt->actstrip->act == strip->act) &&
(strip->flag & NLASTRIP_FLAG_SYNC_LENGTH)) {
- /* recalculate the length of the action */
- calc_action_range(strip->act, &strip->actstart, &strip->actend, 0);
-
- /* adjust the strip extents in response to this */
- BKE_nlastrip_recalculate_bounds(strip);
+ BKE_nlastrip_recalculate_bounds_sync_action(strip);
}
/* clear tweakuser flag */
@@ -2179,3 +2192,103 @@ void BKE_nla_tweakmode_exit(AnimData *adt)
adt->actstrip = NULL;
adt->flag &= ~ADT_NLA_EDIT_ON;
}
+
+static void blend_write_nla_strips(BlendWriter *writer, ListBase *strips)
+{
+ BLO_write_struct_list(writer, NlaStrip, strips);
+ LISTBASE_FOREACH (NlaStrip *, strip, strips) {
+ /* write the strip's F-Curves and modifiers */
+ BKE_fcurve_blend_write(writer, &strip->fcurves);
+ BKE_fmodifiers_blend_write(writer, &strip->modifiers);
+
+ /* write the strip's children */
+ blend_write_nla_strips(writer, &strip->strips);
+ }
+}
+
+static void blend_data_read_nla_strips(BlendDataReader *reader, ListBase *strips)
+{
+ LISTBASE_FOREACH (NlaStrip *, strip, strips) {
+ /* strip's child strips */
+ BLO_read_list(reader, &strip->strips);
+ blend_data_read_nla_strips(reader, &strip->strips);
+
+ /* strip's F-Curves */
+ BLO_read_list(reader, &strip->fcurves);
+ BKE_fcurve_blend_read_data(reader, &strip->fcurves);
+
+ /* strip's F-Modifiers */
+ BLO_read_list(reader, &strip->modifiers);
+ BKE_fmodifiers_blend_read_data(reader, &strip->modifiers, NULL);
+ }
+}
+
+static void blend_lib_read_nla_strips(BlendLibReader *reader, ID *id, ListBase *strips)
+{
+ LISTBASE_FOREACH (NlaStrip *, strip, strips) {
+ /* check strip's children */
+ blend_lib_read_nla_strips(reader, id, &strip->strips);
+
+ /* check strip's F-Curves */
+ BKE_fcurve_blend_read_lib(reader, id, &strip->fcurves);
+
+ /* reassign the counted-reference to action */
+ BLO_read_id_address(reader, id->lib, &strip->act);
+ }
+}
+
+static void blend_read_expand_nla_strips(BlendExpander *expander, ListBase *strips)
+{
+ LISTBASE_FOREACH (NlaStrip *, strip, strips) {
+ /* check child strips */
+ blend_read_expand_nla_strips(expander, &strip->strips);
+
+ /* check F-Curves */
+ BKE_fcurve_blend_read_expand(expander, &strip->fcurves);
+
+ /* check F-Modifiers */
+ BKE_fmodifiers_blend_read_expand(expander, &strip->modifiers);
+
+ /* relink referenced action */
+ BLO_expand(expander, strip->act);
+ }
+}
+
+void BKE_nla_blend_write(BlendWriter *writer, ListBase *tracks)
+{
+ /* write all the tracks */
+ LISTBASE_FOREACH (NlaTrack *, nlt, tracks) {
+ /* write the track first */
+ BLO_write_struct(writer, NlaTrack, nlt);
+
+ /* write the track's strips */
+ blend_write_nla_strips(writer, &nlt->strips);
+ }
+}
+
+void BKE_nla_blend_read_data(BlendDataReader *reader, ListBase *tracks)
+{
+ LISTBASE_FOREACH (NlaTrack *, nlt, tracks) {
+ /* relink list of strips */
+ BLO_read_list(reader, &nlt->strips);
+
+ /* relink strip data */
+ blend_data_read_nla_strips(reader, &nlt->strips);
+ }
+}
+
+void BKE_nla_blend_read_lib(BlendLibReader *reader, ID *id, ListBase *tracks)
+{
+ /* we only care about the NLA strips inside the tracks */
+ LISTBASE_FOREACH (NlaTrack *, nlt, tracks) {
+ blend_lib_read_nla_strips(reader, id, &nlt->strips);
+ }
+}
+
+void BKE_nla_blend_read_expand(struct BlendExpander *expander, struct ListBase *tracks)
+{
+ /* nla-data - referenced actions */
+ LISTBASE_FOREACH (NlaTrack *, nlt, tracks) {
+ blend_read_expand_nla_strips(expander, &nlt->strips);
+ }
+}
diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c
index a89285a28c1..571a1145958 100644
--- a/source/blender/blenkernel/intern/node.c
+++ b/source/blender/blenkernel/intern/node.c
@@ -359,6 +359,11 @@ IDTypeInfo IDType_ID_NT = {
.make_local = NULL,
.foreach_id = node_foreach_id,
.foreach_cache = node_foreach_cache,
+
+ .blend_write = NULL,
+ .blend_read_data = NULL,
+ .blend_read_lib = NULL,
+ .blend_read_expand = NULL,
};
static void node_add_sockets_from_type(bNodeTree *ntree, bNode *node, bNodeType *ntype)
diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c
index 15ad653e6f8..23f28e4ea23 100644
--- a/source/blender/blenkernel/intern/object.c
+++ b/source/blender/blenkernel/intern/object.c
@@ -156,7 +156,6 @@ static ThreadMutex vparent_lock = BLI_MUTEX_INITIALIZER;
#endif
static void copy_object_pose(Object *obn, const Object *ob, const int flag);
-static void copy_object_lod(Object *obn, const Object *ob, const int flag);
static void object_init_data(ID *id)
{
@@ -264,8 +263,6 @@ static void object_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const in
ob_dst->avs = ob_src->avs;
ob_dst->mpath = animviz_copy_motionpath(ob_src->mpath);
- copy_object_lod(ob_dst, ob_src, flag_subdata);
-
/* Do not copy object's preview
* (mostly due to the fact renderers create temp copy of objects). */
if ((flag & LIB_ID_COPY_NO_PREVIEW) == 0 && false) { /* XXX TODO temp hack */
@@ -314,8 +311,6 @@ static void object_free_data(ID *id)
BLI_freelistN(&ob->pc_ids);
- BLI_freelistN(&ob->lodlevels);
-
/* Free runtime curves data. */
if (ob->runtime.curve_cache) {
BKE_curve_bevelList_free(&ob->runtime.curve_cache->bev);
@@ -499,12 +494,6 @@ static void object_foreach_id(ID *id, LibraryForeachIDData *data)
BKE_LIB_FOREACHID_PROCESS(data, object->rigidbody_constraint->ob2, IDWALK_CB_NEVER_SELF);
}
- if (object->lodlevels.first) {
- LISTBASE_FOREACH (LodLevel *, level, &object->lodlevels) {
- BKE_LIB_FOREACHID_PROCESS(data, level->source, IDWALK_CB_NEVER_SELF);
- }
- }
-
BKE_modifiers_foreach_ID_link(object, library_foreach_modifiersForeachIDLink, data);
BKE_gpencil_modifiers_foreach_ID_link(
object, library_foreach_gpencil_modifiersForeachIDLink, data);
@@ -539,6 +528,12 @@ IDTypeInfo IDType_ID_OB = {
.free_data = object_free_data,
.make_local = object_make_local,
.foreach_id = object_foreach_id,
+ .foreach_cache = NULL,
+
+ .blend_write = NULL,
+ .blend_read_data = NULL,
+ .blend_read_lib = NULL,
+ .blend_read_expand = NULL,
};
void BKE_object_workob_clear(Object *workob)
@@ -1395,7 +1390,6 @@ Object *BKE_object_add_for_data(
void BKE_object_copy_softbody(struct Object *ob_dst, const struct Object *ob_src, const int flag)
{
-
SoftBody *sb = ob_src->soft;
SoftBody *sbn;
bool tagged_no_main = ob_dst->id.tag & LIB_TAG_NO_MAIN;
@@ -1408,6 +1402,8 @@ void BKE_object_copy_softbody(struct Object *ob_dst, const struct Object *ob_src
sbn = MEM_dupallocN(sb);
+ sbExternalCopy(ob_dst,ob_src);
+
if ((flag & LIB_ID_COPY_CACHES) == 0) {
sbn->totspring = sbn->totpoint = 0;
sbn->bpoint = NULL;
@@ -1586,13 +1582,6 @@ static void copy_object_pose(Object *obn, const Object *ob, const int flag)
}
}
-static void copy_object_lod(Object *obn, const Object *ob, const int UNUSED(flag))
-{
- BLI_duplicatelist(&obn->lodlevels, &ob->lodlevels);
-
- obn->currentlod = (LodLevel *)obn->lodlevels.first;
-}
-
bool BKE_object_pose_context_check(const Object *ob)
{
if ((ob) && (ob->type == OB_ARMATURE) && (ob->pose) && (ob->mode & OB_MODE_POSE)) {
diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c
index e3c209b60e6..bc089d7bd80 100644
--- a/source/blender/blenkernel/intern/paint.c
+++ b/source/blender/blenkernel/intern/paint.c
@@ -117,6 +117,12 @@ IDTypeInfo IDType_ID_PAL = {
.free_data = palette_free_data,
.make_local = NULL,
.foreach_id = NULL,
+ .foreach_cache = NULL,
+
+ .blend_write = NULL,
+ .blend_read_data = NULL,
+ .blend_read_lib = NULL,
+ .blend_read_expand = NULL,
};
static void paint_curve_copy_data(Main *UNUSED(bmain),
@@ -155,6 +161,12 @@ IDTypeInfo IDType_ID_PC = {
.free_data = paint_curve_free_data,
.make_local = NULL,
.foreach_id = NULL,
+ .foreach_cache = NULL,
+
+ .blend_write = NULL,
+ .blend_read_data = NULL,
+ .blend_read_lib = NULL,
+ .blend_read_expand = NULL,
};
const char PAINT_CURSOR_SCULPT[3] = {255, 100, 100};
diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c
index b3da6c53b34..e837c57400a 100644
--- a/source/blender/blenkernel/intern/particle.c
+++ b/source/blender/blenkernel/intern/particle.c
@@ -209,6 +209,12 @@ IDTypeInfo IDType_ID_PA = {
.free_data = particle_settings_free_data,
.make_local = NULL,
.foreach_id = particle_settings_foreach_id,
+ .foreach_cache = NULL,
+
+ .blend_write = NULL,
+ .blend_read_data = NULL,
+ .blend_read_lib = NULL,
+ .blend_read_expand = NULL,
};
unsigned int PSYS_FRAND_SEED_OFFSET[PSYS_FRAND_COUNT];
diff --git a/source/blender/blenkernel/intern/pointcloud.c b/source/blender/blenkernel/intern/pointcloud.c
index 21889acba3c..fb10c9f03e3 100644
--- a/source/blender/blenkernel/intern/pointcloud.c
+++ b/source/blender/blenkernel/intern/pointcloud.c
@@ -113,6 +113,12 @@ IDTypeInfo IDType_ID_PT = {
.free_data = pointcloud_free_data,
.make_local = NULL,
.foreach_id = pointcloud_foreach_id,
+ .foreach_cache = NULL,
+
+ .blend_write = NULL,
+ .blend_read_data = NULL,
+ .blend_read_lib = NULL,
+ .blend_read_expand = NULL,
};
static void pointcloud_random(PointCloud *pointcloud)
diff --git a/source/blender/blenkernel/intern/rigidbody.c b/source/blender/blenkernel/intern/rigidbody.c
index ece7d0f9136..95a8b3b3c15 100644
--- a/source/blender/blenkernel/intern/rigidbody.c
+++ b/source/blender/blenkernel/intern/rigidbody.c
@@ -229,6 +229,23 @@ void BKE_rigidbody_free_constraint(Object *ob)
ob->rigidbody_constraint = NULL;
}
+bool BKE_rigidbody_is_affected_by_simulation(Object *ob)
+{
+ /* Check if the object will have its transform changed by the rigidbody simulation. */
+
+ /* True if the shape of this object's parent is of type compound */
+ bool obCompoundParent = (ob->parent != NULL && ob->parent->rigidbody_object != NULL &&
+ ob->parent->rigidbody_object->shape == RB_SHAPE_COMPOUND);
+
+ RigidBodyOb *rbo = ob->rigidbody_object;
+ if (rbo == NULL || rbo->flag & RBO_FLAG_KINEMATIC || rbo->type == RBO_TYPE_PASSIVE ||
+ obCompoundParent) {
+ return false;
+ }
+
+ return true;
+}
+
#ifdef WITH_BULLET
/* Copying Methods --------------------- */
@@ -1904,18 +1921,13 @@ bool BKE_rigidbody_check_sim_running(RigidBodyWorld *rbw, float ctime)
/* Sync rigid body and object transformations */
void BKE_rigidbody_sync_transforms(RigidBodyWorld *rbw, Object *ob, float ctime)
{
- RigidBodyOb *rbo = ob->rigidbody_object;
-
- /* True if the shape of this object's parent is of type compound */
- bool obCompoundParent = (ob->parent != NULL && ob->parent->rigidbody_object != NULL &&
- ob->parent->rigidbody_object->shape == RB_SHAPE_COMPOUND);
-
- /* keep original transform for kinematic and passive objects */
- if (ELEM(NULL, rbw, rbo) || rbo->flag & RBO_FLAG_KINEMATIC || rbo->type == RBO_TYPE_PASSIVE ||
- obCompoundParent) {
+ if (!BKE_rigidbody_is_affected_by_simulation(ob)) {
+ /* Don't sync transforms for objects that are not affected/changed by the simulation. */
return;
}
+ RigidBodyOb *rbo = ob->rigidbody_object;
+
/* use rigid body transform after cache start frame if objects is not being transformed */
if (BKE_rigidbody_check_sim_running(rbw, ctime) &&
!(ob->base_flag & BASE_SELECTED && G.moving & G_TRANSFORM_OBJ)) {
@@ -1941,8 +1953,8 @@ void BKE_rigidbody_sync_transforms(RigidBodyWorld *rbw, Object *ob, float ctime)
void BKE_rigidbody_aftertrans_update(
Object *ob, float loc[3], float rot[3], float quat[4], float rotAxis[3], float rotAngle)
{
+ bool correct_delta = BKE_rigidbody_is_affected_by_simulation(ob);
RigidBodyOb *rbo = ob->rigidbody_object;
- bool correct_delta = !(rbo->flag & RBO_FLAG_KINEMATIC || rbo->type == RBO_TYPE_PASSIVE);
/* return rigid body and object to their initial states */
copy_v3_v3(rbo->pos, ob->loc);
diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c
index 1dc51c9ddae..e74fcbb84bd 100644
--- a/source/blender/blenkernel/intern/scene.c
+++ b/source/blender/blenkernel/intern/scene.c
@@ -469,7 +469,7 @@ static void scene_foreach_id(ID *id, LibraryForeachIDData *data)
}
if (scene->ed) {
Sequence *seq;
- SEQP_BEGIN (scene->ed, seq) {
+ SEQ_ALL_BEGIN (scene->ed, seq) {
BKE_LIB_FOREACHID_PROCESS(data, seq->scene, IDWALK_CB_NEVER_SELF);
BKE_LIB_FOREACHID_PROCESS(data, seq->scene_camera, IDWALK_CB_NOP);
BKE_LIB_FOREACHID_PROCESS(data, seq->clip, IDWALK_CB_USER);
@@ -486,7 +486,7 @@ static void scene_foreach_id(ID *id, LibraryForeachIDData *data)
BKE_LIB_FOREACHID_PROCESS(data, text_data->text_font, IDWALK_CB_USER);
}
}
- SEQ_END;
+ SEQ_ALL_END;
}
/* This pointer can be NULL during old files reading, better be safe than sorry. */
@@ -606,6 +606,11 @@ IDTypeInfo IDType_ID_SCE = {
.make_local = NULL,
.foreach_id = scene_foreach_id,
.foreach_cache = scene_foreach_cache,
+
+ .blend_write = NULL,
+ .blend_read_data = NULL,
+ .blend_read_lib = NULL,
+ .blend_read_expand = NULL,
};
const char *RE_engine_id_BLENDER_EEVEE = "BLENDER_EEVEE";
@@ -1129,15 +1134,9 @@ int BKE_scene_base_iter_next(
return iter->phase;
}
-Scene *BKE_scene_find_from_view_layer(const Main *bmain, const ViewLayer *layer)
+bool BKE_scene_has_view_layer(const Scene *scene, const ViewLayer *layer)
{
- for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) {
- if (BLI_findindex(&scene->view_layers, layer) != -1) {
- return scene;
- }
- }
-
- return NULL;
+ return BLI_findindex(&scene->view_layers, layer) != -1;
}
Scene *BKE_scene_find_from_collection(const Main *bmain, const Collection *collection)
@@ -1615,7 +1614,7 @@ void BKE_scene_graph_update_for_newframe(Depsgraph *depsgraph)
*/
void BKE_scene_view_layer_graph_evaluated_ensure(Main *bmain, Scene *scene, ViewLayer *view_layer)
{
- Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, true);
+ Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer);
DEG_make_active(depsgraph);
BKE_scene_graph_update_tagged(depsgraph, bmain);
}
@@ -2249,15 +2248,15 @@ void BKE_scene_free_view_layer_depsgraph(Scene *scene, ViewLayer *view_layer)
/* Query depsgraph for a specific contexts. */
-static Depsgraph **scene_get_depsgraph_p(Main *bmain,
- Scene *scene,
+static Depsgraph **scene_get_depsgraph_p(Scene *scene,
ViewLayer *view_layer,
- const bool allocate_ghash_entry,
- const bool allocate_depsgraph)
+ const bool allocate_ghash_entry)
{
+ /* bmain may be NULL here! */
BLI_assert(scene != NULL);
BLI_assert(view_layer != NULL);
- BLI_assert(BKE_scene_find_from_view_layer(bmain, view_layer) == scene);
+ BLI_assert(BKE_scene_has_view_layer(scene, view_layer));
+
/* Make sure hash itself exists. */
if (allocate_ghash_entry) {
BKE_scene_ensure_depsgraph_hash(scene);
@@ -2265,42 +2264,68 @@ static Depsgraph **scene_get_depsgraph_p(Main *bmain,
if (scene->depsgraph_hash == NULL) {
return NULL;
}
- /* Either ensure item is in the hash or simply return NULL if it's not,
- * depending on whether caller wants us to create depsgraph or not.
- */
+
DepsgraphKey key;
key.view_layer = view_layer;
+
Depsgraph **depsgraph_ptr;
- if (allocate_ghash_entry) {
- DepsgraphKey **key_ptr;
- if (!BLI_ghash_ensure_p_ex(
- scene->depsgraph_hash, &key, (void ***)&key_ptr, (void ***)&depsgraph_ptr)) {
- *key_ptr = MEM_mallocN(sizeof(DepsgraphKey), __func__);
- **key_ptr = key;
- if (allocate_depsgraph) {
- *depsgraph_ptr = DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_VIEWPORT);
- /* TODO(sergey): Would be cool to avoid string format print,
- * but is a bit tricky because we can't know in advance whether
- * we will ever enable debug messages for this depsgraph.
- */
- char name[1024];
- BLI_snprintf(name, sizeof(name), "%s :: %s", scene->id.name, view_layer->name);
- DEG_debug_name_set(*depsgraph_ptr, name);
- }
- else {
- *depsgraph_ptr = NULL;
- }
- }
- }
- else {
+ if (!allocate_ghash_entry) {
depsgraph_ptr = (Depsgraph **)BLI_ghash_lookup_p(scene->depsgraph_hash, &key);
+ return depsgraph_ptr;
+ }
+
+ DepsgraphKey **key_ptr;
+ if (BLI_ghash_ensure_p_ex(
+ scene->depsgraph_hash, &key, (void ***)&key_ptr, (void ***)&depsgraph_ptr)) {
+ return depsgraph_ptr;
}
+
+ /* Depsgraph was not found in the ghash, but the key still needs allocating. */
+ *key_ptr = MEM_mallocN(sizeof(DepsgraphKey), __func__);
+ **key_ptr = key;
+
+ *depsgraph_ptr = NULL;
+ return depsgraph_ptr;
+}
+
+static Depsgraph **scene_ensure_depsgraph_p(Main *bmain, Scene *scene, ViewLayer *view_layer)
+{
+ BLI_assert(bmain != NULL);
+
+ Depsgraph **depsgraph_ptr = scene_get_depsgraph_p(scene, view_layer, true);
+ if (depsgraph_ptr == NULL) {
+ /* The scene has no depsgraph hash. */
+ return NULL;
+ }
+ if (*depsgraph_ptr != NULL) {
+ /* The depsgraph was found, no need to allocate. */
+ return depsgraph_ptr;
+ }
+
+ /* Allocate a new depsgraph. scene_get_depsgraph_p() already ensured that the pointer is stored
+ * in the scene's depsgraph hash. */
+ *depsgraph_ptr = DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_VIEWPORT);
+
+ /* TODO(sergey): Would be cool to avoid string format print,
+ * but is a bit tricky because we can't know in advance whether
+ * we will ever enable debug messages for this depsgraph.
+ */
+ char name[1024];
+ BLI_snprintf(name, sizeof(name), "%s :: %s", scene->id.name, view_layer->name);
+ DEG_debug_name_set(*depsgraph_ptr, name);
+
return depsgraph_ptr;
}
-Depsgraph *BKE_scene_get_depsgraph(Main *bmain, Scene *scene, ViewLayer *view_layer, bool allocate)
+Depsgraph *BKE_scene_get_depsgraph(Scene *scene, ViewLayer *view_layer)
+{
+ Depsgraph **depsgraph_ptr = scene_get_depsgraph_p(scene, view_layer, false);
+ return (depsgraph_ptr != NULL) ? *depsgraph_ptr : NULL;
+}
+
+Depsgraph *BKE_scene_ensure_depsgraph(Main *bmain, Scene *scene, ViewLayer *view_layer)
{
- Depsgraph **depsgraph_ptr = scene_get_depsgraph_p(bmain, scene, view_layer, allocate, allocate);
+ Depsgraph **depsgraph_ptr = scene_ensure_depsgraph_p(bmain, scene, view_layer);
return (depsgraph_ptr != NULL) ? *depsgraph_ptr : NULL;
}
@@ -2366,8 +2391,7 @@ void BKE_scene_undo_depsgraphs_restore(Main *bmain, GHash *depsgraph_extract)
}
BLI_assert(*depsgraph_extract_ptr != NULL);
- Depsgraph **depsgraph_scene_ptr = scene_get_depsgraph_p(
- bmain, scene, view_layer, true, false);
+ Depsgraph **depsgraph_scene_ptr = scene_get_depsgraph_p(scene, view_layer, true);
BLI_assert(depsgraph_scene_ptr != NULL);
BLI_assert(*depsgraph_scene_ptr == NULL);
@@ -2551,13 +2575,13 @@ static void scene_sequencer_disable_sound_strips(Scene *scene)
return;
}
Sequence *seq;
- SEQ_BEGIN (scene->ed, seq) {
+ SEQ_ALL_BEGIN (scene->ed, seq) {
if (seq->scene_sound != NULL) {
BKE_sound_remove_scene_sound(scene, seq->scene_sound);
seq->scene_sound = NULL;
}
}
- SEQ_END;
+ SEQ_ALL_END;
}
void BKE_scene_eval_sequencer_sequences(Depsgraph *depsgraph, Scene *scene)
@@ -2568,7 +2592,7 @@ void BKE_scene_eval_sequencer_sequences(Depsgraph *depsgraph, Scene *scene)
}
BKE_sound_ensure_scene(scene);
Sequence *seq;
- SEQ_BEGIN (scene->ed, seq) {
+ SEQ_ALL_BEGIN (scene->ed, seq) {
if (seq->scene_sound == NULL) {
if (seq->sound != NULL) {
if (seq->scene_sound == NULL) {
@@ -2606,7 +2630,7 @@ void BKE_scene_eval_sequencer_sequences(Depsgraph *depsgraph, Scene *scene)
seq->scene_sound, seq->pan, (seq->flag & SEQ_AUDIO_PAN_ANIMATED) != 0);
}
}
- SEQ_END;
+ SEQ_ALL_END;
BKE_sequencer_update_muting(scene->ed);
BKE_sequencer_update_sound_bounds_all(scene);
}
diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c
index 3a49c180172..d56658a6709 100644
--- a/source/blender/blenkernel/intern/screen.c
+++ b/source/blender/blenkernel/intern/screen.c
@@ -245,6 +245,12 @@ IDTypeInfo IDType_ID_SCR = {
.free_data = screen_free_data,
.make_local = NULL,
.foreach_id = screen_foreach_id,
+ .foreach_cache = NULL,
+
+ .blend_write = NULL,
+ .blend_read_data = NULL,
+ .blend_read_lib = NULL,
+ .blend_read_expand = NULL,
};
/* ************ Spacetype/regiontype handling ************** */
diff --git a/source/blender/blenkernel/intern/seqeffects.c b/source/blender/blenkernel/intern/seqeffects.c
index c6daecbcee6..aba9b255f40 100644
--- a/source/blender/blenkernel/intern/seqeffects.c
+++ b/source/blender/blenkernel/intern/seqeffects.c
@@ -2437,16 +2437,16 @@ static void transform_image(int x,
}
static void do_transform_effect(const SeqRenderData *context,
- Sequence *seq,
- float UNUSED(cfra),
- float UNUSED(facf0),
- float UNUSED(facf1),
- ImBuf *ibuf1,
- ImBuf *UNUSED(ibuf2),
- ImBuf *UNUSED(ibuf3),
- int start_line,
- int total_lines,
- ImBuf *out)
+ Sequence *seq,
+ float UNUSED(cfra),
+ float UNUSED(facf0),
+ float UNUSED(facf1),
+ ImBuf *ibuf1,
+ ImBuf *UNUSED(ibuf2),
+ ImBuf *UNUSED(ibuf3),
+ int start_line,
+ int total_lines,
+ ImBuf *out)
{
Scene *scene = context->scene;
TransformVars *transform = (TransformVars *)seq->effectdata;
diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c
index b4da0c5bd33..630c5f9fb1f 100644
--- a/source/blender/blenkernel/intern/sequencer.c
+++ b/source/blender/blenkernel/intern/sequencer.c
@@ -507,11 +507,11 @@ void BKE_sequencer_editing_free(Scene *scene, const bool do_id_user)
BKE_sequencer_prefetch_free(scene);
BKE_sequencer_cache_destruct(scene);
- SEQ_BEGIN (ed, seq) {
+ SEQ_ALL_BEGIN (ed, seq) {
/* handle cache freeing above */
BKE_sequence_free_ex(scene, seq, false, do_id_user, false);
}
- SEQ_END;
+ SEQ_ALL_END;
BLI_freelistN(&ed->metastack);
MEM_freeN(ed);
@@ -662,7 +662,7 @@ void BKE_sequencer_new_render_data(Main *bmain,
/* ************************* iterator ************************** */
/* *************** (replaces old WHILE_SEQ) ********************* */
-/* **************** use now SEQ_BEGIN () SEQ_END ***************** */
+/* **************** use now SEQ_ALL_BEGIN () SEQ_ALL_END ***************** */
/* sequence strip iterator:
* - builds a full array, recursively into meta strips
@@ -697,7 +697,10 @@ static void seq_build_array(ListBase *seqbase, Sequence ***array, int depth)
}
}
-static void seq_array(Editing *ed, Sequence ***seqarray, int *tot, bool use_pointer)
+static void seq_array(Editing *ed,
+ Sequence ***seqarray,
+ int *tot,
+ const bool use_current_sequences)
{
Sequence **array;
@@ -708,7 +711,7 @@ static void seq_array(Editing *ed, Sequence ***seqarray, int *tot, bool use_poin
return;
}
- if (use_pointer) {
+ if (use_current_sequences) {
seq_count(ed->seqbasep, tot);
}
else {
@@ -720,7 +723,7 @@ static void seq_array(Editing *ed, Sequence ***seqarray, int *tot, bool use_poin
}
*seqarray = array = MEM_mallocN(sizeof(Sequence *) * (*tot), "SeqArray");
- if (use_pointer) {
+ if (use_current_sequences) {
seq_build_array(ed->seqbasep, &array, 0);
}
else {
@@ -728,10 +731,10 @@ static void seq_array(Editing *ed, Sequence ***seqarray, int *tot, bool use_poin
}
}
-void BKE_sequence_iterator_begin(Editing *ed, SeqIterator *iter, bool use_pointer)
+void BKE_sequence_iterator_begin(Editing *ed, SeqIterator *iter, const bool use_current_sequences)
{
memset(iter, 0, sizeof(*iter));
- seq_array(ed, &iter->array, &iter->tot, use_pointer);
+ seq_array(ed, &iter->array, &iter->tot, use_current_sequences);
if (iter->tot) {
iter->cur = 0;
@@ -3592,7 +3595,7 @@ static ImBuf *seq_render_scene_strip(const SeqRenderData *context,
}
/* opengl offscreen render */
- depsgraph = BKE_scene_get_depsgraph(context->bmain, scene, view_layer, true);
+ depsgraph = BKE_scene_ensure_depsgraph(context->bmain, scene, view_layer);
BKE_scene_graph_update_for_newframe(depsgraph);
ibuf = sequencer_view3d_fn(
/* set for OpenGL render (NULL when scrubbing) */
@@ -6168,7 +6171,7 @@ void BKE_sequencer_check_uuids_unique_and_report(const Scene *scene)
BLI_session_uuid_ghash_hash, BLI_session_uuid_ghash_compare, "sequencer used uuids");
const Sequence *sequence;
- SEQ_BEGIN (scene->ed, sequence) {
+ SEQ_ALL_BEGIN (scene->ed, sequence) {
const SessionUUID *session_uuid = &sequence->runtime.session_uuid;
if (!BLI_session_uuid_is_generated(session_uuid)) {
printf("Sequence %s does not have UUID generated.\n", sequence->name);
@@ -6182,7 +6185,7 @@ void BKE_sequencer_check_uuids_unique_and_report(const Scene *scene)
BLI_gset_insert(used_uuids, (void *)session_uuid);
}
- SEQ_END;
+ SEQ_ALL_END;
BLI_gset_free(used_uuids, NULL);
}
diff --git a/source/blender/blenkernel/intern/simulation.cc b/source/blender/blenkernel/intern/simulation.cc
index 6b03721cab8..9dc1f073e2a 100644
--- a/source/blender/blenkernel/intern/simulation.cc
+++ b/source/blender/blenkernel/intern/simulation.cc
@@ -160,6 +160,12 @@ IDTypeInfo IDType_ID_SIM = {
/* free_data */ simulation_free_data,
/* make_local */ nullptr,
/* foreach_id */ simulation_foreach_id,
+ /* foreach_cache */ NULL,
+
+ /* blend_write */ NULL,
+ /* blend_read_data */ NULL,
+ /* blend_read_lib */ NULL,
+ /* blend_read_expand */ NULL,
};
void *BKE_simulation_add(Main *bmain, const char *name)
diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c
index bca2af1fb18..5743394c079 100644
--- a/source/blender/blenkernel/intern/softbody.c
+++ b/source/blender/blenkernel/intern/softbody.c
@@ -3085,8 +3085,6 @@ static void softbody_to_object(Object *ob, float (*vertexCos)[3], int numVerts,
{
SoftBody *sb = ob->soft;
if (sb) {
- // int sb_totpt = sb->totpoint;
-
BodyPoint *bp = sb->bpoint;
int a;
if (sb->solverflags & SBSO_ESTIMATEIPO) {
@@ -3128,28 +3126,6 @@ SoftBody *sbNew(Scene *scene)
SoftBody *sb;
sb = MEM_callocN(sizeof(SoftBody), "softbody");
- sb->solver_mode = SOLVER_MODE_LEGACY; // SOLVER_MODE_ADMMPD;
- sb->admmpd_mesh_mode = 0; // embedded
- sb->admmpd_substeps = 1;
- sb->admmpd_max_admm_iters = 20;
- sb->admmpd_self_collision = 0;
- sb->admmpd_material = 0; // ARAP
- sb->admmpd_embed_res = 3;
- sb->admmpd_converge_eps = 1e-4;
- sb->admmpd_youngs_exp = 6;
- sb->admmpd_poisson = 0.399;
- sb->admmpd_density_kgm3 = 1522;
- sb->admmpd_ck_exp = 7;
- sb->admmpd_pk_exp = 4;
- sb->admmpd_floor_z = -999;
- sb->admmpd_gravity = -9.8;
- sb->admmpd_strainlimit_min = 0; // no compression
- sb->admmpd_strainlimit_max = 100; // 100x stretch
- sb->admmpd_maxthreads = -1;
- sb->admmpd_loglevel = 1; // low
- sb->admmpd_linsolver = 1; // PCG
- sb->admmpd = MEM_callocN(sizeof(ADMMPDInterfaceData), "SoftBody_admmpd");
-
sb->mediafrict = 0.5f;
sb->nodemass = 1.0f;
sb->grav = 9.8f;
@@ -3190,6 +3166,8 @@ SoftBody *sbNew(Scene *scene)
sb->shared = MEM_callocN(sizeof(*sb->shared), "SoftBody_Shared");
sb->shared->pointcache = BKE_ptcache_add(&sb->shared->ptcaches);
+ sbExternalSetDefault(sb);
+
if (!sb->effector_weights) {
sb->effector_weights = BKE_effector_add_weights(NULL);
}
@@ -3209,15 +3187,34 @@ void sbFree(Object *ob)
free_softbody_intern(sb);
+ /* Only free shared data on non-CoW copies */
if ((ob->id.tag & LIB_TAG_NO_MAIN) == 0) {
- if (sb->admmpd) {
- admmpd_dealloc(sb->admmpd);
- MEM_freeN(sb->admmpd);
- sb->admmpd = NULL;
+ /* Clear the ADMMPD linked list. Because the linked
+ * list pointer is copied to all soft body objects, we
+ * want to be careful not to free twice. */
+ {
+ ADMMPDInterfaceData *admmpd;
+ int cleared_admmpd_list = 0;
+ if (sb->shared->admmpd_list) {
+ while ((admmpd = BLI_pophead(sb->shared->admmpd_list))) {
+ cleared_admmpd_list = 1;
+ if (admmpd != NULL) {
+ admmpd_dealloc(admmpd);
+ MEM_freeN(admmpd);
+ }
+ }
+ }
+ /* If we deleted data from the linked list
+ * we can delete the head ptr */
+ if (cleared_admmpd_list) {
+ MEM_freeN(sb->shared->admmpd_list);
+ }
+ /* Otherwise we've allready freed, just
+ * set the ptr to NULL */
+ sb->shared->admmpd_list = NULL;
}
- /* Only free shared data on non-CoW copies */
BKE_ptcache_free_list(&sb->shared->ptcaches);
sb->shared->pointcache = NULL;
MEM_freeN(sb->shared);
@@ -3564,15 +3561,40 @@ static void sbStoreLastFrame(struct Depsgraph *depsgraph, Object *object, float
object_orig->soft->last_frame = framenr;
}
+static inline ADMMPDInterfaceData* get_admmpd_for_object(Object *ob)
+{
+ SoftBody *sb = ob->soft;
+ if (!sb) {
+ return NULL;
+ }
+ int found = 0;
+ ADMMPDInterfaceData *admmpd = sb->shared->admmpd_list->first;
+ while (admmpd != NULL) {
+ if (strcmp(admmpd->name,ob->id.name)==0) {
+ found = 1;
+ break;
+ }
+ admmpd = admmpd->next;
+ }
+ if (!found) {
+ return NULL;
+ }
+ return admmpd;
+}
+
static void update_collider_admmpd(Depsgraph *depsgraph,
Collection *collection,
Object *vertexowner)
{
+
SoftBody *sb = vertexowner->soft;
if (!sb) {
return;
}
- if (!sb->admmpd) {
+
+ ADMMPDInterfaceData *admmpd = get_admmpd_for_object(vertexowner);
+ if (!admmpd) {
+ CLOG_ERROR(&LOG, "No ADMM-PD data found for object %s", vertexowner->id.name);
return;
}
@@ -3580,20 +3602,32 @@ static void update_collider_admmpd(Depsgraph *depsgraph,
Object **objects = BKE_collision_objects_create(
depsgraph, vertexowner, collection, &numobjects, eModifierType_Collision);
- // How many faces and vertices do we need to allocate?
+ admmpd_update_obstacles(admmpd,objects,numobjects);
+
+ BKE_collision_objects_free(objects);
+
+#if 0
+ /* How many faces and vertices do we need to allocate? */
int tot_verts = 0;
int tot_faces = 0;
for (int i = 0; i < numobjects; ++i) {
Object *ob = objects[i];
- if (ob->type == OB_MESH) {
- if (ob->pd && ob->pd->deflect) {
- CollisionModifierData *cmd = (CollisionModifierData *)BKE_modifiers_findby_type(
- ob, eModifierType_Collision);
- if (!cmd)
- continue;
- tot_verts += cmd->mvert_num;
- tot_faces += cmd->tri_num;
- }
+ if (ob->type != OB_MESH) {
+ continue;
+ }
+ /* If the collision object is the same as the softbody object,
+ * we don't want to add it. Otherwise it creates a duplicate obstacle
+ * at the initial location of the softbody. */
+ if (strcmp(ob->id.name,vertexowner->id.name)==0) {
+ continue;
+ }
+ if (ob->pd && ob->pd->deflect) {
+ CollisionModifierData *cmd = (CollisionModifierData *)BKE_modifiers_findby_type(
+ ob, eModifierType_Collision);
+ if (!cmd)
+ continue;
+ tot_verts += cmd->mvert_num;
+ tot_faces += cmd->tri_num;
}
}
@@ -3601,7 +3635,7 @@ static void update_collider_admmpd(Depsgraph *depsgraph,
float *obs_v1 = MEM_callocN(sizeof(float) * 3 * tot_verts, __func__);
unsigned int *obs_faces = MEM_callocN(sizeof(unsigned int) * 3 * tot_faces, __func__);
- // Copy over vertices and faces
+ /* Copy over vertices and faces */
int curr_verts = 0;
int curr_faces = 0;
for (int i = 0; i < numobjects; ++i) {
@@ -3634,26 +3668,77 @@ static void update_collider_admmpd(Depsgraph *depsgraph,
}
}
- // Update via API
- admmpd_update_obstacles(sb->admmpd, obs_v0, obs_v1, tot_verts, obs_faces, tot_faces);
+ /* Pass obstacle data to ADMMPD */
+ admmpd_update_obstacles(admmpd, obs_v0, obs_v1, tot_verts, obs_faces, tot_faces);
- // Cleanup
MEM_freeN(obs_v0);
MEM_freeN(obs_v1);
MEM_freeN(obs_faces);
BKE_collision_objects_free(objects);
+#endif
}
-void sbCustomRead(struct Object *ob)
+void sbExternalCopy(Object *dest_ob, Object *src_ob)
+{
+ (void)(src_ob);
+ if (!dest_ob) {
+ CLOG_ERROR(&LOG, "No dest object in sbExternalCopy");
+ return;
+ }
+ SoftBody *dest = dest_ob->soft;
+ if (!dest) {
+ CLOG_ERROR(&LOG, "No softbody in sbExternalCopy");
+ return;
+ }
+ if (!dest->shared) {
+ CLOG_ERROR(&LOG, "No shared in sbExternalCopy");
+ return;
+ }
+ /* This can happen on readfile: */
+ if (!dest->shared->admmpd_list) {
+ dest->shared->admmpd_list = MEM_callocN(sizeof(ListBase), "SoftBody_ADMMPD_List");
+ }
+ /* Create ADMM-PD data for this object if it does not already exist. */
+ ADMMPDInterfaceData *admmpd = get_admmpd_for_object(dest_ob);
+ if (admmpd==NULL) {
+ admmpd = MEM_callocN(sizeof(ADMMPDInterfaceData), "ADMMPD");
+ strcpy(admmpd->name, dest_ob->id.name);
+ BLI_addtail(dest->shared->admmpd_list, admmpd);
+ }
+}
+
+void sbExternalSetDefault(SoftBody *sb)
{
- SoftBody *sb = ob->soft;
if (!sb) {
return;
}
+ if (sb->shared == NULL) {
+ sb->shared = MEM_callocN(sizeof(*sb->shared), "SoftBody_Shared");
+ }
+ if (sb->shared->admmpd_list == NULL) {
+ sb->shared->admmpd_list = MEM_callocN(sizeof(ListBase), "SoftBody_ADMMPD_List");
+ }
- // ADMM-PD data is not currently written to file.
- // Instead, we'll create new data when a .blend file is loaded.
- sb->admmpd = MEM_callocN(sizeof(ADMMPDInterfaceData), "SoftBody_admmpd");
+ sb->solver_mode = SOLVER_MODE_LEGACY;
+ sb->admmpd_mesh_mode = 0; /* Embedded. */
+ sb->admmpd_substeps = 1;
+ sb->admmpd_max_admm_iters = 20;
+ sb->admmpd_self_collision = 0;
+ sb->admmpd_material = 0; /* ARAP. */
+ sb->admmpd_embed_res = 3; /* Max subdivisions for embedded lattice. */
+ sb->admmpd_converge_eps = 1e-4; /* Solver residual stop condition. */
+ sb->admmpd_youngs_exp = 6; /* Exponent of Young's mod. */
+ sb->admmpd_poisson = 0.399;
+ sb->admmpd_density_kgm3 = 1522; /* Density of rubber. */
+ sb->admmpd_ck_exp = 7; /* Exponent of collision stiffness. */
+ sb->admmpd_pk_exp = 4; /* Exponent of pin stiffness. */
+ sb->admmpd_floor_z = -999;
+ sb->admmpd_gravity = -9.8;
+ sb->admmpd_strainlimit_min = 0; /* 0 = compression allowed. */
+ sb->admmpd_strainlimit_max = 100; /* 100 = 100x strech allowed. */
+ sb->admmpd_maxthreads = -1; /* Auto detect. */
+ sb->admmpd_loglevel = 1; /* Low terminal output. */
+ sb->admmpd_linsolver = 1; /* PCG solver. */
}
/* simulates one step. framenr is in frames */
@@ -3670,9 +3755,10 @@ void sbObjectStep(struct Depsgraph *depsgraph,
return;
}
- if (sb->admmpd == NULL) {
- CLOG_ERROR(&LOG, "No ADMM-PD data");
- WM_reportf(RPT_ERROR, "No ADMM-PD data");
+ ADMMPDInterfaceData *admmpd = get_admmpd_for_object(ob);
+ if (admmpd == NULL) {
+ CLOG_ERROR(&LOG, "No ADMM-PD data in sbObjectStep");
+ WM_report(RPT_ERROR, "No ADMM-PD data in sbObjectStep");
return;
}
@@ -3714,18 +3800,17 @@ void sbObjectStep(struct Depsgraph *depsgraph,
/* Do we need to initialize the ADMM-PD mesh?
* a) Has never been initialized.
- * b) The mesh topology has changed.
- * TODO: ob->obmat or vertexCos change. */
+ * b) The mesh topology has changed. */
int init_mesh = 0;
- if (is_first_frame || admmpd_mesh_needs_update(sb->admmpd, ob)) {
- init_mesh = admmpd_update_mesh(sb->admmpd, ob, vertexCos);
+ if (is_first_frame || admmpd_mesh_needs_update(admmpd, ob)) {
+ init_mesh = admmpd_update_mesh(admmpd, ob, vertexCos);
if (init_mesh == 0) {
- CLOG_ERROR(&LOG, "%s", sb->admmpd->last_error);
- WM_reportf(RPT_ERROR, sb->admmpd->last_error);
+ CLOG_ERROR(&LOG, "%s", admmpd->last_error);
+ WM_report(RPT_ERROR, admmpd->last_error);
return;
}
else if (init_mesh == -1) {
- WM_reportf(RPT_WARNING, sb->admmpd->last_error);
+ WM_report(RPT_WARNING, admmpd->last_error);
}
}
@@ -3734,22 +3819,22 @@ void sbObjectStep(struct Depsgraph *depsgraph,
* b) Some settings require re-initialization
* c) The mesh has changed */
int init_solver = 0;
- if (is_first_frame || init_mesh || admmpd_solver_needs_update(sb->admmpd, scene, ob)) {
- init_solver = admmpd_update_solver(sb->admmpd, scene, ob, vertexCos);
+ if (is_first_frame || init_mesh || admmpd_solver_needs_update(admmpd, scene, ob)) {
+ init_solver = admmpd_update_solver(admmpd, scene, ob, vertexCos);
if (init_solver == 0) {
- CLOG_ERROR(&LOG, "%s", sb->admmpd->last_error);
- WM_reportf(RPT_ERROR, sb->admmpd->last_error);
+ CLOG_ERROR(&LOG, "%s", admmpd->last_error);
+ WM_report(RPT_ERROR, admmpd->last_error);
return;
}
else if (init_solver == -1) {
- WM_reportf(RPT_WARNING, sb->admmpd->last_error);
+ WM_report(RPT_WARNING, admmpd->last_error);
}
}
/* In case of paramter change, ob->soft->bpoint has not
* been set yet. So we need to resize which can be done
* in the copy_to_object function while leaving vertexCos to null. */
- admmpd_copy_to_object(sb->admmpd, ob, NULL);
+ admmpd_copy_to_object(admmpd, ob, NULL);
if (init_mesh || init_solver) {
BKE_ptcache_invalidate(cache);
@@ -3790,7 +3875,7 @@ void sbObjectStep(struct Depsgraph *depsgraph,
/* first frame, no simulation to do, just set the positions */
if (sb->solver_mode == SOLVER_MODE_ADMMPD) {
- admmpd_copy_from_object(sb->admmpd, ob);
+ admmpd_copy_from_object(admmpd, ob);
}
else if (sb->solver_mode == SOLVER_MODE_LEGACY) {
softbody_update_positions(ob, sb, vertexCos, numVerts);
@@ -3816,10 +3901,10 @@ void sbObjectStep(struct Depsgraph *depsgraph,
if (sb->solver_mode == SOLVER_MODE_ADMMPD) {
/* We have read the cache into softbody, so we need to pass it to ADMM-PD */
- admmpd_copy_from_object(sb->admmpd, ob);
+ admmpd_copy_from_object(admmpd, ob);
/* Now that we have the updated ADMM-PD vertices, we have
* to map them to surface vertices (vertexCos) */
- admmpd_copy_to_object(sb->admmpd, ob, vertexCos);
+ admmpd_copy_to_object(admmpd, ob, vertexCos);
}
else if (sb->solver_mode == SOLVER_MODE_LEGACY) {
softbody_to_object(ob, vertexCos, numVerts, sb->local);
@@ -3858,17 +3943,17 @@ void sbObjectStep(struct Depsgraph *depsgraph,
}
if (sb->solver_mode == SOLVER_MODE_ADMMPD) {
- admmpd_copy_from_object(sb->admmpd, ob);
+ admmpd_copy_from_object(admmpd, ob);
update_collider_admmpd(depsgraph, sb->collision_group, ob);
- int solve_retval = admmpd_solve(sb->admmpd, ob, vertexCos);
+ int solve_retval = admmpd_solve(admmpd, ob, vertexCos);
if (solve_retval == 0) {
- CLOG_ERROR(&LOG, "%s", sb->admmpd->last_error);
- WM_reportf(RPT_ERROR, sb->admmpd->last_error);
+ CLOG_ERROR(&LOG, "%s", admmpd->last_error);
+ WM_report(RPT_ERROR, admmpd->last_error);
}
else if (solve_retval == -1) {
- WM_reportf(RPT_WARNING, sb->admmpd->last_error);
+ WM_report(RPT_WARNING, admmpd->last_error);
}
- admmpd_copy_to_object(sb->admmpd, ob, vertexCos);
+ admmpd_copy_to_object(admmpd, ob, vertexCos);
}
else if (sb->solver_mode == SOLVER_MODE_LEGACY) {
softbody_update_positions(ob, sb, vertexCos, numVerts);
diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c
index b72c5e99b43..8ee6a3627dc 100644
--- a/source/blender/blenkernel/intern/sound.c
+++ b/source/blender/blenkernel/intern/sound.c
@@ -143,6 +143,11 @@ IDTypeInfo IDType_ID_SO = {
.make_local = NULL,
.foreach_id = NULL,
.foreach_cache = sound_foreach_cache,
+
+ .blend_write = NULL,
+ .blend_read_data = NULL,
+ .blend_read_lib = NULL,
+ .blend_read_expand = NULL,
};
#ifdef WITH_AUDASPACE
diff --git a/source/blender/blenkernel/intern/speaker.c b/source/blender/blenkernel/intern/speaker.c
index 1f778f5fcd6..8f403792c46 100644
--- a/source/blender/blenkernel/intern/speaker.c
+++ b/source/blender/blenkernel/intern/speaker.c
@@ -65,6 +65,12 @@ IDTypeInfo IDType_ID_SPK = {
.free_data = NULL,
.make_local = NULL,
.foreach_id = speaker_foreach_id,
+ .foreach_cache = NULL,
+
+ .blend_write = NULL,
+ .blend_read_data = NULL,
+ .blend_read_lib = NULL,
+ .blend_read_expand = NULL,
};
void *BKE_speaker_add(Main *bmain, const char *name)
diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c
index 8a055423d6f..bde9b9ab9b8 100644
--- a/source/blender/blenkernel/intern/text.c
+++ b/source/blender/blenkernel/intern/text.c
@@ -182,6 +182,12 @@ IDTypeInfo IDType_ID_TXT = {
.free_data = text_free_data,
.make_local = NULL,
.foreach_id = NULL,
+ .foreach_cache = NULL,
+
+ .blend_write = NULL,
+ .blend_read_data = NULL,
+ .blend_read_lib = NULL,
+ .blend_read_expand = NULL,
};
/** \} */
diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c
index 8d5a0497e28..9e176f355d3 100644
--- a/source/blender/blenkernel/intern/texture.c
+++ b/source/blender/blenkernel/intern/texture.c
@@ -149,6 +149,12 @@ IDTypeInfo IDType_ID_TE = {
.free_data = texture_free_data,
.make_local = NULL,
.foreach_id = texture_foreach_id,
+ .foreach_cache = NULL,
+
+ .blend_write = NULL,
+ .blend_read_data = NULL,
+ .blend_read_lib = NULL,
+ .blend_read_expand = NULL,
};
/* Utils for all IDs using those texture slots. */
diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc
index 54fb0f612d1..9e873692486 100644
--- a/source/blender/blenkernel/intern/volume.cc
+++ b/source/blender/blenkernel/intern/volume.cc
@@ -525,6 +525,11 @@ IDTypeInfo IDType_ID_VO = {
/* make_local */ nullptr,
/* foreach_id */ volume_foreach_id,
/* foreach_cache */ volume_foreach_cache,
+
+ /* blend_write */ NULL,
+ /* blend_read_data */ NULL,
+ /* blend_read_lib */ NULL,
+ /* blend_read_expand */ NULL,
};
void BKE_volume_init_grids(Volume *volume)
diff --git a/source/blender/blenkernel/intern/workspace.c b/source/blender/blenkernel/intern/workspace.c
index f653a190704..e5e6a5d67c8 100644
--- a/source/blender/blenkernel/intern/workspace.c
+++ b/source/blender/blenkernel/intern/workspace.c
@@ -90,6 +90,12 @@ IDTypeInfo IDType_ID_WS = {
.free_data = workspace_free_data,
.make_local = NULL,
.foreach_id = workspace_foreach_id,
+ .foreach_cache = NULL,
+
+ .blend_write = NULL,
+ .blend_read_data = NULL,
+ .blend_read_lib = NULL,
+ .blend_read_expand = NULL,
};
/** \name Internal Utils
diff --git a/source/blender/blenkernel/intern/world.c b/source/blender/blenkernel/intern/world.c
index e3b58e03d93..25c62f139ed 100644
--- a/source/blender/blenkernel/intern/world.c
+++ b/source/blender/blenkernel/intern/world.c
@@ -137,6 +137,12 @@ IDTypeInfo IDType_ID_WO = {
.free_data = world_free_data,
.make_local = NULL,
.foreach_id = world_foreach_id,
+ .foreach_cache = NULL,
+
+ .blend_write = NULL,
+ .blend_read_data = NULL,
+ .blend_read_lib = NULL,
+ .blend_read_expand = NULL,
};
World *BKE_world_add(Main *bmain, const char *name)
diff --git a/source/blender/blenlib/BLI_array.h b/source/blender/blenlib/BLI_array.h
index 6ea01d45f79..4e37314ed3e 100644
--- a/source/blender/blenlib/BLI_array.h
+++ b/source/blender/blenlib/BLI_array.h
@@ -117,7 +117,7 @@ void _bli_array_grow_func(void **arr_p,
{ \
if (arr && (char *)arr != _##arr##_static) { \
BLI_array_fake_user(arr); \
- MEM_freeN(arr); \
+ MEM_freeN((void *)arr); \
} \
} \
((void)0)
diff --git a/source/blender/blenlib/BLI_array.hh b/source/blender/blenlib/BLI_array.hh
index 9d09bb3559e..dddf4f64ff5 100644
--- a/source/blender/blenlib/BLI_array.hh
+++ b/source/blender/blenlib/BLI_array.hh
@@ -77,32 +77,39 @@ class Array {
/**
* By default an empty array is created.
*/
- Array()
+ Array(Allocator allocator = {}) noexcept : allocator_(allocator)
{
data_ = inline_buffer_;
size_ = 0;
}
+ Array(NoExceptConstructor, Allocator allocator = {}) noexcept : Array(allocator)
+ {
+ }
+
/**
* Create a new array that contains copies of all values.
*/
template<typename U, typename std::enable_if_t<std::is_convertible_v<U, T>> * = nullptr>
- Array(Span<U> values, Allocator allocator = {}) : allocator_(allocator)
+ Array(Span<U> values, Allocator allocator = {}) : Array(NoExceptConstructor(), allocator)
{
- size_ = values.size();
- data_ = this->get_buffer_for_size(values.size());
- uninitialized_convert_n<U, T>(values.data(), size_, data_);
+ const int64_t size = values.size();
+ data_ = this->get_buffer_for_size(size);
+ uninitialized_convert_n<U, T>(values.data(), size, data_);
+ size_ = size;
}
/**
* Create a new array that contains copies of all values.
*/
template<typename U, typename std::enable_if_t<std::is_convertible_v<U, T>> * = nullptr>
- Array(const std::initializer_list<U> &values) : Array(Span<U>(values))
+ Array(const std::initializer_list<U> &values, Allocator allocator = {})
+ : Array(Span<U>(values), allocator)
{
}
- Array(const std::initializer_list<T> &values) : Array(Span<T>(values))
+ Array(const std::initializer_list<T> &values, Allocator allocator = {})
+ : Array(Span<T>(values), allocator)
{
}
@@ -114,23 +121,24 @@ class Array {
* even for non-trivial types. This should not be the default though, because one can easily mess
* up when dealing with uninitialized memory.
*/
- explicit Array(int64_t size)
+ explicit Array(int64_t size, Allocator allocator = {}) : Array(NoExceptConstructor(), allocator)
{
- size_ = size;
data_ = this->get_buffer_for_size(size);
default_construct_n(data_, size);
+ size_ = size;
}
/**
* Create a new array with the given size. All values will be initialized by copying the given
* default.
*/
- Array(int64_t size, const T &value)
+ Array(int64_t size, const T &value, Allocator allocator = {})
+ : Array(NoExceptConstructor(), allocator)
{
BLI_assert(size >= 0);
- size_ = size;
data_ = this->get_buffer_for_size(size);
- uninitialized_fill_n(data_, size_, value);
+ uninitialized_fill_n(data_, size, value);
+ size_ = size;
}
/**
@@ -145,28 +153,28 @@ class Array {
* Usage:
* Array<std::string> my_strings(10, NoInitialization());
*/
- Array(int64_t size, NoInitialization)
+ Array(int64_t size, NoInitialization, Allocator allocator = {})
+ : Array(NoExceptConstructor(), allocator)
{
BLI_assert(size >= 0);
- size_ = size;
data_ = this->get_buffer_for_size(size);
+ size_ = size;
}
Array(const Array &other) : Array(other.as_span(), other.allocator_)
{
}
- Array(Array &&other) noexcept : allocator_(other.allocator_)
+ Array(Array &&other) noexcept(std::is_nothrow_move_constructible_v<T>)
+ : Array(NoExceptConstructor(), other.allocator_)
{
- size_ = other.size_;
-
- if (!other.uses_inline_buffer()) {
- data_ = other.data_;
+ if (other.data_ == other.inline_buffer_) {
+ uninitialized_relocate_n(other.data_, other.size_, data_);
}
else {
- data_ = this->get_buffer_for_size(size_);
- uninitialized_relocate_n(other.data_, size_, data_);
+ data_ = other.data_;
}
+ size_ = other.size_;
other.data_ = other.inline_buffer_;
other.size_ = 0;
@@ -175,31 +183,17 @@ class Array {
~Array()
{
destruct_n(data_, size_);
- if (!this->uses_inline_buffer()) {
- allocator_.deallocate(static_cast<void *>(data_));
- }
+ this->deallocate_if_not_inline(data_);
}
Array &operator=(const Array &other)
{
- if (this == &other) {
- return *this;
- }
-
- this->~Array();
- new (this) Array(other);
- return *this;
+ return copy_assign_container(*this, other);
}
- Array &operator=(Array &&other)
+ Array &operator=(Array &&other) noexcept(std::is_nothrow_move_constructible_v<T>)
{
- if (this == &other) {
- return *this;
- }
-
- this->~Array();
- new (this) Array(std::move(other));
- return *this;
+ return move_assign_container(*this, std::move(other));
}
T &operator[](int64_t index)
@@ -273,6 +267,21 @@ class Array {
}
/**
+ * Return a reference to the last element in the array.
+ * This invokes undefined behavior when the array is empty.
+ */
+ const T &last() const
+ {
+ BLI_assert(size_ > 0);
+ return *(data_ + size_ - 1);
+ }
+ T &last()
+ {
+ BLI_assert(size_ > 0);
+ return *(data_ + size_ - 1);
+ }
+
+ /**
* Get a pointer to the beginning of the array.
*/
const T *data() const
@@ -354,6 +363,37 @@ class Array {
return InlineBufferCapacity;
}
+ /**
+ * Destruct values and create a new array of the given size. The values in the new array are
+ * default constructed.
+ */
+ void reinitialize(const int64_t new_size)
+ {
+ BLI_assert(new_size >= 0);
+ int64_t old_size = size_;
+
+ destruct_n(data_, size_);
+ size_ = 0;
+
+ if (new_size <= old_size) {
+ default_construct_n(data_, new_size);
+ }
+ else {
+ T *new_data = this->get_buffer_for_size(new_size);
+ try {
+ default_construct_n(new_data, new_size);
+ }
+ catch (...) {
+ this->deallocate_if_not_inline(new_data);
+ throw;
+ }
+ this->deallocate_if_not_inline(data_);
+ data_ = new_data;
+ }
+
+ size_ = new_size;
+ }
+
private:
T *get_buffer_for_size(int64_t size)
{
@@ -371,9 +411,11 @@ class Array {
allocator_.allocate(static_cast<size_t>(size) * sizeof(T), alignof(T), AT));
}
- bool uses_inline_buffer() const
+ void deallocate_if_not_inline(T *ptr)
{
- return data_ == inline_buffer_;
+ if (ptr != inline_buffer_) {
+ allocator_.deallocate(ptr);
+ }
}
};
diff --git a/source/blender/blenlib/BLI_delaunay_2d.h b/source/blender/blenlib/BLI_delaunay_2d.h
index a826a6b2677..7027477ac7f 100644
--- a/source/blender/blenlib/BLI_delaunay_2d.h
+++ b/source/blender/blenlib/BLI_delaunay_2d.h
@@ -18,6 +18,9 @@
/** \file
* \ingroup bli
+ *
+ * This header file contains both a C interface and a C++ interface
+ * to the 2D Constrained Delaunay Triangulation library routine.
*/
#ifdef __cplusplus
@@ -107,11 +110,6 @@ extern "C" {
* If zero is supplied for epsilon, an internal value of 1e-8 used
* instead, since this code will not work correctly if it is not allowed
* to merge "too near" vertices.
- *
- * Normally, if epsilon is non-zero, there is an "input modify" pass which
- * checks to see if some vertices are within epsilon of other edges, and
- * snapping them to those edges if so. You can skip this pass by setting
- * skip_input_modify to true. (This is also useful in some unit tests.)
*/
typedef struct CDT_input {
int verts_len;
@@ -123,7 +121,6 @@ typedef struct CDT_input {
int *faces_start_table;
int *faces_len_table;
float epsilon;
- bool skip_input_modify;
} CDT_input;
/**
@@ -150,12 +147,9 @@ typedef struct CDT_input {
* - faces_orig, faces_orig_start_table, faces_orig_len_table
*
* For edges, the edges_orig triple can also say which original face
- * edge is part of a given output edge. If an index in edges_orig
- * is greater than the input's edges_len, then subtract input's edges_len
- * from it to some number i: then the face edge that starts from the
- * input vertex at input's faces[i] is the corresponding face edge.
- * for convenience, face_edge_offset in the result will be the input's
- * edges_len, so that this conversion can be easily done by the caller.
+ * edge is part of a given output edge. See the comment below
+ * on the C++ interface for how to decode the entries in the edges_orig
+ * table.
*/
typedef struct CDT_result {
int verts_len;
@@ -207,4 +201,69 @@ void BLI_delaunay_2d_cdt_free(CDT_result *result);
#ifdef __cplusplus
}
-#endif
+
+/* C++ Interface. */
+
+# include "BLI_array.hh"
+# include "BLI_double2.hh"
+# include "BLI_math_mpq.hh"
+# include "BLI_mpq2.hh"
+# include "BLI_vector.hh"
+
+namespace blender::meshintersect {
+
+/* vec2<Arith_t> is a 2d vector with Arith_t as the type for coordinates. */
+template<typename Arith_t> struct vec2_impl;
+template<> struct vec2_impl<double> {
+ typedef double2 type;
+};
+
+# ifdef WITH_GMP
+template<> struct vec2_impl<mpq_class> {
+ typedef mpq2 type;
+};
+# endif
+
+template<typename Arith_t> using vec2 = typename vec2_impl<Arith_t>::type;
+
+template<typename Arith_t> class CDT_input {
+ public:
+ Array<vec2<Arith_t>> vert;
+ Array<std::pair<int, int>> edge;
+ Array<Vector<int>> face;
+ Arith_t epsilon{0};
+};
+
+template<typename Arith_t> class CDT_result {
+ public:
+ Array<vec2<Arith_t>> vert;
+ Array<std::pair<int, int>> edge;
+ Array<Vector<int>> face;
+ /** For each output vert, which input verts correspond to it? */
+ Array<Vector<int>> vert_orig;
+ /**
+ * For each output edge, which input edges does it overlap?
+ * The input edge ids are encoded as follows:
+ * if the value is less than face_edge_offset, then it is
+ * an index into the input edge[] array.
+ * else let (a, b) = the quotient and remainder of dividing
+ * the edge index by face_edge_offset; "a" will be the input face + 1,
+ * and "b" will be a position within that face.
+ */
+ Array<Vector<int>> edge_orig;
+ /** For each output face, which original faces does it overlap? */
+ Array<Vector<int>> face_orig;
+ /** Used to encode edge_orig (see above). */
+ int face_edge_offset;
+};
+
+CDT_result<double> delaunay_2d_calc(const CDT_input<double> &input, CDT_output_type output_type);
+
+# ifdef WITH_GMP
+CDT_result<mpq_class> delaunay_2d_calc(const CDT_input<mpq_class> &input,
+ CDT_output_type output_type);
+# endif
+
+} /* namespace blender::meshintersect */
+
+#endif /* __cplusplus */
diff --git a/source/blender/blenlib/BLI_double2.hh b/source/blender/blenlib/BLI_double2.hh
new file mode 100644
index 00000000000..3466b946e73
--- /dev/null
+++ b/source/blender/blenlib/BLI_double2.hh
@@ -0,0 +1,143 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+/** \file
+ * \ingroup bli
+ */
+
+#include "BLI_double3.hh"
+
+namespace blender {
+
+struct double2 {
+ double x, y;
+
+ double2() = default;
+
+ double2(const double *ptr) : x{ptr[0]}, y{ptr[1]}
+ {
+ }
+
+ double2(double x, double y) : x(x), y(y)
+ {
+ }
+
+ double2(const double3 &other) : x(other.x), y(other.y)
+ {
+ }
+
+ operator double *()
+ {
+ return &x;
+ }
+
+ operator const double *() const
+ {
+ return &x;
+ }
+
+ float length() const
+ {
+ return len_v2_db(*this);
+ }
+
+ friend double2 operator+(const double2 &a, const double2 &b)
+ {
+ return {a.x + b.x, a.y + b.y};
+ }
+
+ friend double2 operator-(const double2 &a, const double2 &b)
+ {
+ return {a.x - b.x, a.y - b.y};
+ }
+
+ friend double2 operator*(const double2 &a, double b)
+ {
+ return {a.x * b, a.y * b};
+ }
+
+ friend double2 operator/(const double2 &a, double b)
+ {
+ BLI_assert(b != 0.0);
+ return {a.x / b, a.y / b};
+ }
+
+ friend double2 operator*(double a, const double2 &b)
+ {
+ return b * a;
+ }
+
+ friend bool operator==(const double2 &a, const double2 &b)
+ {
+ return a.x == b.x && a.y == b.y;
+ }
+
+ friend bool operator!=(const double2 &a, const double2 &b)
+ {
+ return a.x != b.x || a.y != b.y;
+ }
+
+ friend std::ostream &operator<<(std::ostream &stream, const double2 &v)
+ {
+ stream << "(" << v.x << ", " << v.y << ")";
+ return stream;
+ }
+
+ static double dot(const double2 &a, const double2 &b)
+ {
+ return a.x * b.x + a.y * b.y;
+ }
+
+ static double2 interpolate(const double2 &a, const double2 &b, double t)
+ {
+ return a * (1 - t) + b * t;
+ }
+
+ static double2 abs(const double2 &a)
+ {
+ return double2(fabs(a.x), fabs(a.y));
+ }
+
+ static double distance(const double2 &a, const double2 &b)
+ {
+ return (a - b).length();
+ }
+
+ static double distance_squared(const double2 &a, const double2 &b)
+ {
+ return double2::dot(a, b);
+ }
+
+ struct isect_result {
+ enum {
+ LINE_LINE_COLINEAR = -1,
+ LINE_LINE_NONE = 0,
+ LINE_LINE_EXACT = 1,
+ LINE_LINE_CROSS = 2,
+ } kind;
+ double lambda;
+ double mu;
+ };
+
+ static isect_result isect_seg_seg(const double2 &v1,
+ const double2 &v2,
+ const double2 &v3,
+ const double2 &v4);
+};
+
+} // namespace blender
diff --git a/source/blender/blenlib/BLI_double3.hh b/source/blender/blenlib/BLI_double3.hh
new file mode 100644
index 00000000000..5b6204935d7
--- /dev/null
+++ b/source/blender/blenlib/BLI_double3.hh
@@ -0,0 +1,245 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+/** \file
+ * \ingroup bli
+ */
+
+#include <iostream>
+
+#include "BLI_math_vector.h"
+#include "BLI_span.hh"
+
+namespace blender {
+
+struct double3 {
+ double x, y, z;
+
+ double3() = default;
+
+ double3(const double *ptr) : x{ptr[0]}, y{ptr[1]}, z{ptr[2]}
+ {
+ }
+
+ double3(const double (*ptr)[3]) : double3((const double *)ptr)
+ {
+ }
+
+ explicit double3(double value) : x(value), y(value), z(value)
+ {
+ }
+
+ explicit double3(int value) : x(value), y(value), z(value)
+ {
+ }
+
+ double3(double x, double y, double z) : x{x}, y{y}, z{z}
+ {
+ }
+
+ operator const double *() const
+ {
+ return &x;
+ }
+
+ operator double *()
+ {
+ return &x;
+ }
+
+ double normalize_and_get_length()
+ {
+ return normalize_v3_db(*this);
+ }
+
+ double3 normalized() const
+ {
+ double3 result;
+ normalize_v3_v3_db(result, *this);
+ return result;
+ }
+
+ double length() const
+ {
+ return len_v3_db(*this);
+ }
+
+ double length_squared() const
+ {
+ return len_squared_v3_db(*this);
+ }
+
+ void reflect(const double3 &normal)
+ {
+ *this = this->reflected(normal);
+ }
+
+ double3 reflected(const double3 &normal) const
+ {
+ double3 result;
+ reflect_v3_v3v3_db(result, *this, normal);
+ return result;
+ }
+
+ static double3 safe_divide(const double3 &a, const double3 &b)
+ {
+ double3 result;
+ result.x = (b.x == 0.0) ? 0.0 : a.x / b.x;
+ result.y = (b.y == 0.0) ? 0.0 : a.y / b.y;
+ result.z = (b.z == 0.0) ? 0.0 : a.z / b.z;
+ return result;
+ }
+
+ void invert()
+ {
+ x = -x;
+ y = -y;
+ z = -z;
+ }
+
+ friend double3 operator+(const double3 &a, const double3 &b)
+ {
+ return {a.x + b.x, a.y + b.y, a.z + b.z};
+ }
+
+ void operator+=(const double3 &b)
+ {
+ this->x += b.x;
+ this->y += b.y;
+ this->z += b.z;
+ }
+
+ friend double3 operator-(const double3 &a, const double3 &b)
+ {
+ return {a.x - b.x, a.y - b.y, a.z - b.z};
+ }
+
+ friend double3 operator-(const double3 &a)
+ {
+ return {-a.x, -a.y, -a.z};
+ }
+
+ void operator-=(const double3 &b)
+ {
+ this->x -= b.x;
+ this->y -= b.y;
+ this->z -= b.z;
+ }
+
+ void operator*=(const double &scalar)
+ {
+ this->x *= scalar;
+ this->y *= scalar;
+ this->z *= scalar;
+ }
+
+ void operator*=(const double3 &other)
+ {
+ this->x *= other.x;
+ this->y *= other.y;
+ this->z *= other.z;
+ }
+
+ friend double3 operator*(const double3 &a, const double3 &b)
+ {
+ return {a.x * b.x, a.y * b.y, a.z * b.z};
+ }
+
+ friend double3 operator*(const double3 &a, const double &b)
+ {
+ return {a.x * b, a.y * b, a.z * b};
+ }
+
+ friend double3 operator*(const double &a, const double3 &b)
+ {
+ return b * a;
+ }
+
+ friend double3 operator/(const double3 &a, const double &b)
+ {
+ BLI_assert(b != 0.0);
+ return {a.x / b, a.y / b, a.z / b};
+ }
+
+ friend bool operator==(const double3 &a, const double3 &b)
+ {
+ return a.x == b.x && a.y == b.y && a.z == b.z;
+ }
+
+ friend bool operator!=(const double3 &a, const double3 &b)
+ {
+ return a.x != b.x || a.y != b.y || a.z != b.z;
+ }
+
+ friend std::ostream &operator<<(std::ostream &stream, const double3 &v)
+ {
+ stream << "(" << v.x << ", " << v.y << ", " << v.z << ")";
+ return stream;
+ }
+
+ static double dot(const double3 &a, const double3 &b)
+ {
+ return a.x * b.x + a.y * b.y + a.z * b.z;
+ }
+
+ static double3 cross_high_precision(const double3 &a, const double3 &b)
+ {
+ double3 result;
+ cross_v3_v3v3_db(result, a, b);
+ return result;
+ }
+
+ static double3 project(const double3 &a, const double3 &b)
+ {
+ double3 result;
+ project_v3_v3v3_db(result, a, b);
+ return result;
+ }
+
+ static double distance(const double3 &a, const double3 &b)
+ {
+ return (a - b).length();
+ }
+
+ static double distance_squared(const double3 &a, const double3 &b)
+ {
+ return double3::dot(a, b);
+ }
+
+ static double3 interpolate(const double3 &a, const double3 &b, double t)
+ {
+ return a * (1 - t) + b * t;
+ }
+
+ static double3 abs(const double3 &a)
+ {
+ return double3(fabs(a.x), fabs(a.y), fabs(a.z));
+ }
+
+ static int dominant_axis(const double3 &a)
+ {
+ double x = (a.x >= 0) ? a.x : -a.x;
+ double y = (a.y >= 0) ? a.y : -a.y;
+ double z = (a.z >= 0) ? a.z : -a.z;
+ return ((x > y) ? ((x > z) ? 0 : 2) : ((y > z) ? 1 : 2));
+ }
+
+ static double3 cross_poly(Span<double3> poly);
+};
+
+} // namespace blender
diff --git a/source/blender/blenlib/BLI_float2.hh b/source/blender/blenlib/BLI_float2.hh
index e55a8de4633..1290eb6c65c 100644
--- a/source/blender/blenlib/BLI_float2.hh
+++ b/source/blender/blenlib/BLI_float2.hh
@@ -47,6 +47,11 @@ struct float2 {
return &x;
}
+ float length() const
+ {
+ return len_v2(*this);
+ }
+
float2 &operator+=(const float2 &other)
{
x += other.x;
@@ -107,6 +112,47 @@ struct float2 {
return stream;
}
+ static float dot(const float2 &a, const float2 &b)
+ {
+ return a.x * b.x + a.y * b.y;
+ }
+
+ static float2 interpolate(const float2 &a, const float2 &b, float t)
+ {
+ return a * (1 - t) + b * t;
+ }
+
+ static float2 abs(const float2 &a)
+ {
+ return float2(fabsf(a.x), fabsf(a.y));
+ }
+
+ static float distance(const float2 &a, const float2 &b)
+ {
+ return (a - b).length();
+ }
+
+ static float distance_squared(const float2 &a, const float2 &b)
+ {
+ return float2::dot(a, b);
+ }
+
+ struct isect_result {
+ enum {
+ LINE_LINE_COLINEAR = -1,
+ LINE_LINE_NONE = 0,
+ LINE_LINE_EXACT = 1,
+ LINE_LINE_CROSS = 2,
+ } kind;
+ float lambda;
+ float mu;
+ };
+
+ static isect_result isect_seg_seg(const float2 &v1,
+ const float2 &v2,
+ const float2 &v3,
+ const float2 &v4);
+
friend bool operator==(const float2 &a, const float2 &b)
{
return a.x == b.x && a.y == b.y;
diff --git a/source/blender/blenlib/BLI_float3.hh b/source/blender/blenlib/BLI_float3.hh
index 2d90498fee8..17b3f56453c 100644
--- a/source/blender/blenlib/BLI_float3.hh
+++ b/source/blender/blenlib/BLI_float3.hh
@@ -243,6 +243,11 @@ struct float3 {
{
return a * (1 - t) + b * t;
}
+
+ static float3 abs(const float3 &a)
+ {
+ return float3(fabsf(a.x), fabsf(a.y), fabsf(a.z));
+ }
};
} // namespace blender
diff --git a/source/blender/blenlib/BLI_map.hh b/source/blender/blenlib/BLI_map.hh
index 229bbfad0e4..08fe1a3cdbc 100644
--- a/source/blender/blenlib/BLI_map.hh
+++ b/source/blender/blenlib/BLI_map.hh
@@ -171,14 +171,18 @@ class Map {
* This is necessary to avoid a high cost when no elements are added at all. An optimized grow
* operation is performed on the first insertion.
*/
- Map()
+ Map(Allocator allocator = {}) noexcept
: removed_slots_(0),
occupied_and_removed_slots_(0),
usable_slots_(0),
slot_mask_(0),
hash_(),
is_equal_(),
- slots_(1)
+ slots_(1, allocator)
+ {
+ }
+
+ Map(NoExceptConstructor, Allocator allocator = {}) noexcept : Map(allocator)
{
}
@@ -186,41 +190,38 @@ class Map {
Map(const Map &other) = default;
- Map(Map &&other) noexcept
- : removed_slots_(other.removed_slots_),
- occupied_and_removed_slots_(other.occupied_and_removed_slots_),
- usable_slots_(other.usable_slots_),
- slot_mask_(other.slot_mask_),
- hash_(std::move(other.hash_)),
- is_equal_(std::move(other.is_equal_)),
- slots_(std::move(other.slots_))
+ Map(Map &&other) noexcept(std::is_nothrow_move_constructible_v<SlotArray>)
+ : Map(NoExceptConstructor(), other.slots_.allocator())
{
- other.~Map();
- new (&other) Map();
+ if constexpr (std::is_nothrow_move_constructible_v<SlotArray>) {
+ slots_ = std::move(other.slots_);
+ }
+ else {
+ try {
+ slots_ = std::move(other.slots_);
+ }
+ catch (...) {
+ other.noexcept_reset();
+ throw;
+ }
+ }
+ removed_slots_ = other.removed_slots_;
+ occupied_and_removed_slots_ = other.occupied_and_removed_slots_;
+ usable_slots_ = other.usable_slots_;
+ slot_mask_ = other.slot_mask_;
+ hash_ = std::move(other.hash_);
+ is_equal_ = std::move(other.is_equal_);
+ other.noexcept_reset();
}
Map &operator=(const Map &other)
{
- if (this == &other) {
- return *this;
- }
-
- this->~Map();
- new (this) Map(other);
-
- return *this;
+ return copy_assign_container(*this, other);
}
Map &operator=(Map &&other)
{
- if (this == &other) {
- return *this;
- }
-
- this->~Map();
- new (this) Map(std::move(other));
-
- return *this;
+ return move_assign_container(*this, std::move(other));
}
/**
@@ -315,7 +316,7 @@ class Map {
}
template<typename ForwardKey> bool contains_as(const ForwardKey &key) const
{
- return this->contains__impl(key, hash_(key));
+ return this->lookup_slot_ptr(key, hash_(key)) != nullptr;
}
/**
@@ -330,7 +331,13 @@ class Map {
}
template<typename ForwardKey> bool remove_as(const ForwardKey &key)
{
- return this->remove__impl(key, hash_(key));
+ Slot *slot = this->lookup_slot_ptr(key, hash_(key));
+ if (slot == nullptr) {
+ return false;
+ }
+ slot->remove();
+ removed_slots_++;
+ return true;
}
/**
@@ -343,7 +350,9 @@ class Map {
}
template<typename ForwardKey> void remove_contained_as(const ForwardKey &key)
{
- this->remove_contained__impl(key, hash_(key));
+ Slot &slot = this->lookup_slot(key, hash_(key));
+ slot.remove();
+ removed_slots_++;
}
/**
@@ -356,7 +365,11 @@ class Map {
}
template<typename ForwardKey> Value pop_as(const ForwardKey &key)
{
- return this->pop__impl(key, hash_(key));
+ Slot &slot = this->lookup_slot(key, hash_(key));
+ Value value = std::move(*slot.value());
+ slot.remove();
+ removed_slots_++;
+ return value;
}
/**
@@ -369,7 +382,14 @@ class Map {
}
template<typename ForwardKey> std::optional<Value> pop_try_as(const ForwardKey &key)
{
- return this->pop_try__impl(key, hash_(key));
+ Slot *slot = this->lookup_slot_ptr(key, hash_(key));
+ if (slot == nullptr) {
+ return {};
+ }
+ std::optional<Value> value = std::move(*slot->value());
+ slot->remove();
+ removed_slots_++;
+ return value;
}
/**
@@ -387,7 +407,14 @@ class Map {
template<typename ForwardKey, typename ForwardValue>
Value pop_default_as(const ForwardKey &key, ForwardValue &&default_value)
{
- return this->pop_default__impl(key, std::forward<ForwardValue>(default_value), hash_(key));
+ Slot *slot = this->lookup_slot_ptr(key, hash_(key));
+ if (slot == nullptr) {
+ return std::forward<ForwardValue>(default_value);
+ }
+ Value value = std::move(*slot->value());
+ slot->remove();
+ removed_slots_++;
+ return value;
}
/**
@@ -448,11 +475,12 @@ class Map {
}
template<typename ForwardKey> const Value *lookup_ptr_as(const ForwardKey &key) const
{
- return this->lookup_ptr__impl(key, hash_(key));
+ const Slot *slot = this->lookup_slot_ptr(key, hash_(key));
+ return (slot != nullptr) ? slot->value() : nullptr;
}
template<typename ForwardKey> Value *lookup_ptr_as(const ForwardKey &key)
{
- return const_cast<Value *>(this->lookup_ptr__impl(key, hash_(key)));
+ return const_cast<Value *>(const_cast<const Map *>(this)->lookup_ptr_as(key));
}
/**
@@ -843,8 +871,7 @@ class Map {
*/
void clear()
{
- this->~Map();
- new (this) Map();
+ this->noexcept_reset();
}
/**
@@ -869,8 +896,13 @@ class Map {
* Optimize the case when the map was empty beforehand. We can avoid some copies here.
*/
if (this->size() == 0) {
- slots_.~Array();
- new (&slots_) SlotArray(total_slots);
+ try {
+ slots_.reinitialize(total_slots);
+ }
+ catch (...) {
+ this->noexcept_reset();
+ throw;
+ }
removed_slots_ = 0;
occupied_and_removed_slots_ = 0;
usable_slots_ = usable_slots;
@@ -880,48 +912,44 @@ class Map {
SlotArray new_slots(total_slots);
- for (Slot &slot : slots_) {
- if (slot.is_occupied()) {
- this->add_after_grow_and_destruct_old(slot, new_slots, new_slot_mask);
+ try {
+ for (Slot &slot : slots_) {
+ if (slot.is_occupied()) {
+ this->add_after_grow(slot, new_slots, new_slot_mask);
+ slot.remove();
+ }
}
+ slots_ = std::move(new_slots);
+ }
+ catch (...) {
+ this->noexcept_reset();
+ throw;
}
- /* All occupied slots have been destructed already and empty/removed slots are assumed to be
- * trivially destructible. */
- slots_.clear_without_destruct();
- slots_ = std::move(new_slots);
occupied_and_removed_slots_ -= removed_slots_;
usable_slots_ = usable_slots;
removed_slots_ = 0;
slot_mask_ = new_slot_mask;
}
- void add_after_grow_and_destruct_old(Slot &old_slot,
- SlotArray &new_slots,
- uint64_t new_slot_mask)
+ void add_after_grow(Slot &old_slot, SlotArray &new_slots, uint64_t new_slot_mask)
{
uint64_t hash = old_slot.get_hash(Hash());
SLOT_PROBING_BEGIN (ProbingStrategy, hash, new_slot_mask, slot_index) {
Slot &slot = new_slots[slot_index];
if (slot.is_empty()) {
- slot.relocate_occupied_here(old_slot, hash);
+ slot.occupy(std::move(*old_slot.key()), std::move(*old_slot.value()), hash);
return;
}
}
SLOT_PROBING_END();
}
- template<typename ForwardKey> bool contains__impl(const ForwardKey &key, uint64_t hash) const
+ void noexcept_reset() noexcept
{
- MAP_SLOT_PROBING_BEGIN (hash, slot) {
- if (slot.is_empty()) {
- return false;
- }
- if (slot.contains(key, is_equal_, hash)) {
- return true;
- }
- }
- MAP_SLOT_PROBING_END();
+ Allocator allocator = slots_.allocator();
+ this->~Map();
+ new (this) Map(NoExceptConstructor(), allocator);
}
template<typename ForwardKey, typename ForwardValue>
@@ -930,11 +958,11 @@ class Map {
BLI_assert(!this->contains_as(key));
this->ensure_can_add();
- occupied_and_removed_slots_++;
MAP_SLOT_PROBING_BEGIN (hash, slot) {
if (slot.is_empty()) {
slot.occupy(std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash);
+ occupied_and_removed_slots_++;
return;
}
}
@@ -959,86 +987,6 @@ class Map {
MAP_SLOT_PROBING_END();
}
- template<typename ForwardKey> bool remove__impl(const ForwardKey &key, uint64_t hash)
- {
- MAP_SLOT_PROBING_BEGIN (hash, slot) {
- if (slot.contains(key, is_equal_, hash)) {
- slot.remove();
- removed_slots_++;
- return true;
- }
- if (slot.is_empty()) {
- return false;
- }
- }
- MAP_SLOT_PROBING_END();
- }
-
- template<typename ForwardKey> void remove_contained__impl(const ForwardKey &key, uint64_t hash)
- {
- BLI_assert(this->contains_as(key));
-
- removed_slots_++;
-
- MAP_SLOT_PROBING_BEGIN (hash, slot) {
- if (slot.contains(key, is_equal_, hash)) {
- slot.remove();
- return;
- }
- }
- MAP_SLOT_PROBING_END();
- }
-
- template<typename ForwardKey> Value pop__impl(const ForwardKey &key, uint64_t hash)
- {
- BLI_assert(this->contains_as(key));
-
- removed_slots_++;
-
- MAP_SLOT_PROBING_BEGIN (hash, slot) {
- if (slot.contains(key, is_equal_, hash)) {
- Value value = std::move(*slot.value());
- slot.remove();
- return value;
- }
- }
- MAP_SLOT_PROBING_END();
- }
-
- template<typename ForwardKey>
- std::optional<Value> pop_try__impl(const ForwardKey &key, uint64_t hash)
- {
- MAP_SLOT_PROBING_BEGIN (hash, slot) {
- if (slot.contains(key, is_equal_, hash)) {
- std::optional<Value> value = std::move(*slot.value());
- slot.remove();
- removed_slots_++;
- return value;
- }
- if (slot.is_empty()) {
- return {};
- }
- }
- MAP_SLOT_PROBING_END();
- }
-
- template<typename ForwardKey, typename ForwardValue>
- Value pop_default__impl(const ForwardKey &key, ForwardValue &&default_value, uint64_t hash)
- {
- MAP_SLOT_PROBING_BEGIN (hash, slot) {
- if (slot.contains(key, is_equal_, hash)) {
- Value value = std::move(*slot.value());
- slot.remove();
- removed_slots_++;
- return value;
- }
- if (slot.is_empty()) {
- return std::forward<ForwardValue>(default_value);
- }
- }
- MAP_SLOT_PROBING_END();
- }
-
template<typename ForwardKey, typename CreateValueF, typename ModifyValueF>
auto add_or_modify__impl(ForwardKey &&key,
const CreateValueF &create_value,
@@ -1054,10 +1002,19 @@ class Map {
MAP_SLOT_PROBING_BEGIN (hash, slot) {
if (slot.is_empty()) {
- occupied_and_removed_slots_++;
- slot.occupy_without_value(std::forward<ForwardKey>(key), hash);
Value *value_ptr = slot.value();
- return create_value(value_ptr);
+ if constexpr (std::is_void_v<CreateReturnT>) {
+ create_value(value_ptr);
+ slot.occupy_no_value(std::forward<ForwardKey>(key), hash);
+ occupied_and_removed_slots_++;
+ return;
+ }
+ else {
+ auto return_value = create_value(value_ptr);
+ slot.occupy_no_value(std::forward<ForwardKey>(key), hash);
+ occupied_and_removed_slots_++;
+ return return_value;
+ }
}
if (slot.contains(key, is_equal_, hash)) {
Value *value_ptr = slot.value();
@@ -1119,19 +1076,41 @@ class Map {
}
template<typename ForwardKey>
- const Value *lookup_ptr__impl(const ForwardKey &key, uint64_t hash) const
+ const Slot &lookup_slot(const ForwardKey &key, const uint64_t hash) const
{
+ BLI_assert(this->contains_as(key));
MAP_SLOT_PROBING_BEGIN (hash, slot) {
- if (slot.is_empty()) {
- return nullptr;
+ if (slot.contains(key, is_equal_, hash)) {
+ return slot;
}
+ }
+ MAP_SLOT_PROBING_END();
+ }
+
+ template<typename ForwardKey> Slot &lookup_slot(const ForwardKey &key, const uint64_t hash)
+ {
+ return const_cast<Slot &>(const_cast<const Map *>(this)->lookup_slot(key, hash));
+ }
+
+ template<typename ForwardKey>
+ const Slot *lookup_slot_ptr(const ForwardKey &key, const uint64_t hash) const
+ {
+ MAP_SLOT_PROBING_BEGIN (hash, slot) {
if (slot.contains(key, is_equal_, hash)) {
- return slot.value();
+ return &slot;
+ }
+ if (slot.is_empty()) {
+ return nullptr;
}
}
MAP_SLOT_PROBING_END();
}
+ template<typename ForwardKey> Slot *lookup_slot_ptr(const ForwardKey &key, const uint64_t hash)
+ {
+ return const_cast<Slot *>(const_cast<const Map *>(this)->lookup_slot_ptr(key, hash));
+ }
+
template<typename ForwardKey>
int64_t count_collisions__impl(const ForwardKey &key, uint64_t hash) const
{
diff --git a/source/blender/blenlib/BLI_map_slots.hh b/source/blender/blenlib/BLI_map_slots.hh
index 25fb92d61a3..c0cb3091a8e 100644
--- a/source/blender/blenlib/BLI_map_slots.hh
+++ b/source/blender/blenlib/BLI_map_slots.hh
@@ -38,6 +38,19 @@
namespace blender {
+template<typename Src1, typename Src2, typename Dst1, typename Dst2>
+void initialize_pointer_pair(Src1 &&src1, Src2 &&src2, Dst1 *dst1, Dst2 *dst2)
+{
+ new ((void *)dst1) Dst1(std::forward<Src1>(src1));
+ try {
+ new ((void *)dst2) Dst2(std::forward<Src2>(src2));
+ }
+ catch (...) {
+ dst1->~Dst1();
+ throw;
+ }
+}
+
/**
* The simplest possible map slot. It stores the slot state and the optional key and value
* instances in separate variables. Depending on the alignment requirement of the key and value,
@@ -83,8 +96,10 @@ template<typename Key, typename Value> class SimpleMapSlot {
{
state_ = other.state_;
if (other.state_ == Occupied) {
- new (&key_buffer_) Key(*other.key_buffer_);
- new (&value_buffer_) Value(*other.value_buffer_);
+ initialize_pointer_pair(other.key_buffer_.ref(),
+ other.value_buffer_.ref(),
+ key_buffer_.ptr(),
+ value_buffer_.ptr());
}
}
@@ -93,12 +108,15 @@ template<typename Key, typename Value> class SimpleMapSlot {
* from the other have to moved as well. The other slot stays in the state it was in before. Its
* optionally stored key and value remain in a moved-from state.
*/
- SimpleMapSlot(SimpleMapSlot &&other) noexcept
+ SimpleMapSlot(SimpleMapSlot &&other) noexcept(
+ std::is_nothrow_move_constructible_v<Key> &&std::is_nothrow_move_constructible_v<Value>)
{
state_ = other.state_;
if (other.state_ == Occupied) {
- new (&key_buffer_) Key(std::move(*other.key_buffer_));
- new (&value_buffer_) Value(std::move(*other.value_buffer_));
+ initialize_pointer_pair(std::move(other.key_buffer_.ref()),
+ std::move(other.value_buffer_.ref()),
+ key_buffer_.ptr(),
+ value_buffer_.ptr());
}
}
@@ -161,21 +179,6 @@ template<typename Key, typename Value> class SimpleMapSlot {
}
/**
- * Move the other slot into this slot and destruct it. We do destruction here, because this way
- * we can avoid a comparison with the state, since we know the slot is occupied.
- */
- void relocate_occupied_here(SimpleMapSlot &other, uint64_t UNUSED(hash))
- {
- BLI_assert(!this->is_occupied());
- BLI_assert(other.is_occupied());
- state_ = Occupied;
- new (&key_buffer_) Key(std::move(*other.key_buffer_));
- new (&value_buffer_) Value(std::move(*other.value_buffer_));
- other.key_buffer_.ref().~Key();
- other.value_buffer_.ref().~Value();
- }
-
- /**
* Returns true, when this slot is occupied and contains a key that compares equal to the given
* key. The hash can be used by other slot implementations to determine inequality faster.
*/
@@ -196,19 +199,27 @@ template<typename Key, typename Value> class SimpleMapSlot {
void occupy(ForwardKey &&key, ForwardValue &&value, uint64_t hash)
{
BLI_assert(!this->is_occupied());
- this->occupy_without_value(std::forward<ForwardKey>(key), hash);
new (&value_buffer_) Value(std::forward<ForwardValue>(value));
+ this->occupy_no_value(std::forward<ForwardKey>(key), hash);
+ state_ = Occupied;
}
/**
- * Change the state of this slot from empty/removed to occupied, but leave the value
- * uninitialized. The caller is responsible to construct the value afterwards.
+ * Change the state of this slot from empty/removed to occupied. The value is assumed to be
+ * constructed already.
*/
- template<typename ForwardKey> void occupy_without_value(ForwardKey &&key, uint64_t UNUSED(hash))
+ template<typename ForwardKey> void occupy_no_value(ForwardKey &&key, uint64_t UNUSED(hash))
{
BLI_assert(!this->is_occupied());
+ try {
+ new (&key_buffer_) Key(std::forward<ForwardKey>(key));
+ }
+ catch (...) {
+ /* The value is assumed to be constructed already, so it has to be destructed as well. */
+ value_buffer_.ref().~Value();
+ throw;
+ }
state_ = Occupied;
- new (&key_buffer_) Key(std::forward<ForwardKey>(key));
}
/**
@@ -218,17 +229,17 @@ template<typename Key, typename Value> class SimpleMapSlot {
void remove()
{
BLI_assert(this->is_occupied());
- state_ = Removed;
key_buffer_.ref().~Key();
value_buffer_.ref().~Value();
+ state_ = Removed;
}
};
/**
- * An IntrusiveMapSlot uses two special values of the key to indicate whether the slot is empty or
- * removed. This saves some memory in all cases and is more efficient in many cases. The KeyInfo
- * type indicates which specific values are used. An example for a KeyInfo implementation is
- * PointerKeyInfo.
+ * An IntrusiveMapSlot uses two special values of the key to indicate whether the slot is empty
+ * or removed. This saves some memory in all cases and is more efficient in many cases. The
+ * KeyInfo type indicates which specific values are used. An example for a KeyInfo
+ * implementation is PointerKeyInfo.
*
* The special key values are expected to be trivially destructible.
*/
@@ -297,16 +308,6 @@ template<typename Key, typename Value, typename KeyInfo> class IntrusiveMapSlot
return hash(key_);
}
- void relocate_occupied_here(IntrusiveMapSlot &other, uint64_t UNUSED(hash))
- {
- BLI_assert(!this->is_occupied());
- BLI_assert(other.is_occupied());
- key_ = std::move(other.key_);
- new (&value_buffer_) Value(std::move(*other.value_buffer_));
- other.key_.~Key();
- other.value_buffer_.ref().~Value();
- }
-
template<typename ForwardKey, typename IsEqual>
bool contains(const ForwardKey &key, const IsEqual &is_equal, uint64_t UNUSED(hash)) const
{
@@ -319,22 +320,28 @@ template<typename Key, typename Value, typename KeyInfo> class IntrusiveMapSlot
{
BLI_assert(!this->is_occupied());
BLI_assert(KeyInfo::is_not_empty_or_removed(key));
- this->occupy_without_value(std::forward<ForwardKey>(key), hash);
new (&value_buffer_) Value(std::forward<ForwardValue>(value));
+ this->occupy_no_value(std::forward<ForwardKey>(key), hash);
}
- template<typename ForwardKey> void occupy_without_value(ForwardKey &&key, uint64_t UNUSED(hash))
+ template<typename ForwardKey> void occupy_no_value(ForwardKey &&key, uint64_t UNUSED(hash))
{
BLI_assert(!this->is_occupied());
BLI_assert(KeyInfo::is_not_empty_or_removed(key));
- key_ = std::forward<ForwardKey>(key);
+ try {
+ key_ = std::forward<ForwardKey>(key);
+ }
+ catch (...) {
+ value_buffer_.ref().~Value();
+ throw;
+ }
}
void remove()
{
BLI_assert(this->is_occupied());
- KeyInfo::remove(key_);
value_buffer_.ref().~Value();
+ KeyInfo::remove(key_);
}
};
diff --git a/source/blender/blenlib/BLI_math_base.h b/source/blender/blenlib/BLI_math_base.h
index f407da3133f..d2bb60717ca 100644
--- a/source/blender/blenlib/BLI_math_base.h
+++ b/source/blender/blenlib/BLI_math_base.h
@@ -242,6 +242,14 @@ double double_round(double x, int ndigits);
} \
(void)0
+# define BLI_ASSERT_UNIT_V3_DB(v) \
+ { \
+ const double _test_unit = len_squared_v3_db(v); \
+ BLI_assert(!(fabs(_test_unit - 1.0) >= BLI_ASSERT_UNIT_EPSILON) || \
+ !(fabs(_test_unit) >= BLI_ASSERT_UNIT_EPSILON)); \
+ } \
+ (void)0
+
# define BLI_ASSERT_UNIT_V2(v) \
{ \
const float _test_unit = len_squared_v2(v); \
diff --git a/source/blender/blenlib/BLI_math_boolean.hh b/source/blender/blenlib/BLI_math_boolean.hh
new file mode 100644
index 00000000000..79b1483bfb8
--- /dev/null
+++ b/source/blender/blenlib/BLI_math_boolean.hh
@@ -0,0 +1,62 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+/** \file
+ * \ingroup bli
+ * \brief Math vector functions needed specifically for mesh intersect and boolean.
+ */
+
+#include "BLI_double2.hh"
+#include "BLI_double3.hh"
+
+#ifdef WITH_GMP
+# include "BLI_math_mpq.hh"
+# include "BLI_mpq2.hh"
+# include "BLI_mpq3.hh"
+#endif
+
+namespace blender {
+
+/* #orient2d gives the exact result, using multi-precision arithmetic when result
+ * is close to zero. orient3d_fast just uses double arithmetic, so may be
+ * wrong if the answer is very close to zero.
+ * Similarly, for #incircle and #incircle_fast. */
+int orient2d(const double2 &a, const double2 &b, const double2 &c);
+int orient2d_fast(const double2 &a, const double2 &b, const double2 &c);
+
+int incircle(const double2 &a, const double2 &b, const double2 &c, const double2 &d);
+int incircle_fast(const double2 &a, const double2 &b, const double2 &c, const double2 &d);
+
+/* #orient3d gives the exact result, using multi-precision arithmetic when result
+ * is close to zero. orient3d_fast just uses double arithmetic, so may be
+ * wrong if the answer is very close to zero.
+ * Similarly, for #insphere and #insphere_fast. */
+int orient3d(const double3 &a, const double3 &b, const double3 &c, const double3 &d);
+int orient3d_fast(const double3 &a, const double3 &b, const double3 &c, const double3 &d);
+
+int insphere(
+ const double3 &a, const double3 &b, const double3 &c, const double3 &d, const double3 &e);
+int insphere_fast(
+ const double3 &a, const double3 &b, const double3 &c, const double3 &d, const double3 &e);
+
+#ifdef WITH_GMP
+int orient2d(const mpq2 &a, const mpq2 &b, const mpq2 &c);
+int incircle(const mpq2 &a, const mpq2 &b, const mpq2 &c, const mpq2 &d);
+int orient3d(const mpq3 &a, const mpq3 &b, const mpq3 &c, const mpq3 &d);
+#endif
+} // namespace blender
diff --git a/source/blender/blenlib/BLI_math_matrix.h b/source/blender/blenlib/BLI_math_matrix.h
index a00fdaa0ae9..09f2d5a7cdc 100644
--- a/source/blender/blenlib/BLI_math_matrix.h
+++ b/source/blender/blenlib/BLI_math_matrix.h
@@ -299,6 +299,9 @@ 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_m3_m3_safe_ortho(float Ainv[3][3], const float A[3][3]);
+void invert_m4_m4_safe_ortho(float Ainv[4][4], const float A[4][4]);
+
/****************************** Transformations ******************************/
void scale_m3_fl(float R[3][3], float scale);
diff --git a/source/blender/blenlib/BLI_math_mpq.hh b/source/blender/blenlib/BLI_math_mpq.hh
new file mode 100644
index 00000000000..3d8c6349187
--- /dev/null
+++ b/source/blender/blenlib/BLI_math_mpq.hh
@@ -0,0 +1,36 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+/** \file
+ * \ingroup bli
+ */
+
+#ifdef WITH_GMP
+
+/* This file uses an external file header to define the multi-precision
+ * rational type, mpq_class.
+ * This class keeps separate multi-precision integer numerator and
+ * denominator, reduced to lowest terms after each arithmetic operation.
+ * It can be used where it is important to have exact arithmetic results.
+ *
+ * See gmplib.org for full documentation. In particular:
+ * https://gmplib.org/manual/C_002b_002b-Interface-Rationals
+ */
+# include "gmpxx.h"
+
+#endif /* WITH_GMP */
diff --git a/source/blender/blenlib/BLI_math_vector.h b/source/blender/blenlib/BLI_math_vector.h
index 1ccfe5d86b1..aa639cd3a5d 100644
--- a/source/blender/blenlib/BLI_math_vector.h
+++ b/source/blender/blenlib/BLI_math_vector.h
@@ -140,6 +140,7 @@ MINLINE void mul_v2_v2fl(float r[2], const float a[2], float f);
MINLINE void mul_v3_fl(float r[3], float f);
MINLINE void mul_v3db_db(double r[3], double f);
MINLINE void mul_v3_v3fl(float r[3], const float a[3], float f);
+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]);
@@ -236,17 +237,21 @@ MINLINE float len_manhattan_v3v3(const float a[3], const float b[3]) ATTR_WARN_U
MINLINE float len_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT;
MINLINE float len_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT;
+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], const float unit_scale);
MINLINE float normalize_v2_v2_length(float r[2], const float a[2], const float unit_scale);
MINLINE float normalize_v3_length(float r[3], const float unit_scale);
MINLINE float normalize_v3_v3_length(float r[3], const float a[3], const float unit_scale);
-MINLINE double normalize_v3_length_d(double n[3], const double unit_scale);
+MINLINE double normalize_v3_length_db(double n[3], const double unit_scale);
+MINLINE double normalize_v3_v3_length_db(double r[3], const double a[3], const double unit_scale);
MINLINE float normalize_v2(float r[2]);
MINLINE float normalize_v2_v2(float r[2], const float a[2]);
MINLINE float normalize_v3(float r[3]);
MINLINE float normalize_v3_v3(float r[3], const float a[3]);
-MINLINE double normalize_v3_d(double n[3]);
+MINLINE double normalize_v3_v3_db(double r[3], const double a[3]);
+MINLINE double normalize_v3_db(double n[3]);
/******************************* Interpolation *******************************/
@@ -402,6 +407,7 @@ void angle_poly_v3(float *angles, const float *verts[3], int len);
void project_v2_v2v2(float out[2], const float p[2], const float v_proj[2]);
void project_v3_v3v3(float out[3], const float p[3], const float v_proj[3]);
+void project_v3_v3v3_db(double out[3], const double p[3], const double v_proj[3]);
void project_v2_v2v2_normalized(float out[2], const float p[2], const float v_proj[2]);
void project_v3_v3v3_normalized(float out[3], const float p[3], const float v_proj[3]);
void project_plane_v3_v3v3(float out[3], const float p[3], const float v_plane[3]);
@@ -410,6 +416,7 @@ void project_plane_normalized_v3_v3v3(float out[3], const float p[3], const floa
void project_plane_normalized_v2_v2v2(float out[2], const float p[2], const float v_plane[2]);
void project_v3_plane(float out[3], const float plane_no[3], const float plane_co[3]);
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 ortho_basis_v3v3_v3(float r_n1[3], float r_n2[3], const float n[3]);
void ortho_v3_v3(float out[3], const float v[3]);
void ortho_v2_v2(float out[2], const float v[2]);
diff --git a/source/blender/blenlib/BLI_memory_utils.hh b/source/blender/blenlib/BLI_memory_utils.hh
index 7216536a884..49076bb1aae 100644
--- a/source/blender/blenlib/BLI_memory_utils.hh
+++ b/source/blender/blenlib/BLI_memory_utils.hh
@@ -162,7 +162,7 @@ void uninitialized_convert_n(const From *src, int64_t n, To *dst)
int64_t current = 0;
try {
for (; current < n; current++) {
- new (static_cast<void *>(dst + current)) To((To)src[current]);
+ new (static_cast<void *>(dst + current)) To(static_cast<To>(src[current]));
}
}
catch (...) {
@@ -410,6 +410,15 @@ class NoInitialization {
};
/**
+ * This can be used to mark a constructor of an object that does not throw exceptions. Other
+ * constructors can delegate to this constructor to make sure that the object lifetime starts.
+ * With this, the destructor of the object will be called, even when the remaining constructor
+ * throws.
+ */
+class NoExceptConstructor {
+};
+
+/**
* Helper variable that checks if a pointer type can be converted into another pointer type without
* issues. Possible issues are casting away const and casting a pointer to a child class.
* Adding const or casting to a parent class is fine.
@@ -427,4 +436,49 @@ inline constexpr int64_t default_inline_buffer_capacity(size_t element_size)
return (static_cast<int64_t>(element_size) < 100) ? 4 : 0;
}
+/**
+ * This can be used by containers to implement an exception-safe copy-assignment-operator.
+ * It assumes that the container has an exception safe copy constructor and an exception-safe
+ * move-assignment-operator.
+ */
+template<typename Container> Container &copy_assign_container(Container &dst, const Container &src)
+{
+ if (&src == &dst) {
+ return dst;
+ }
+
+ Container container_copy{src};
+ dst = std::move(container_copy);
+ return dst;
+}
+
+/**
+ * This can be used by containers to implement an exception-safe move-assignment-operator.
+ * It assumes that the container has an exception-safe move-constructor and a noexcept constructor
+ * tagged with the NoExceptConstructor tag.
+ */
+template<typename Container>
+Container &move_assign_container(Container &dst, Container &&src) noexcept(
+ std::is_nothrow_move_constructible_v<Container>)
+{
+ if (&dst == &src) {
+ return dst;
+ }
+
+ dst.~Container();
+ if constexpr (std::is_nothrow_move_constructible_v<Container>) {
+ new (&dst) Container(std::move(src));
+ }
+ else {
+ try {
+ new (&dst) Container(std::move(src));
+ }
+ catch (...) {
+ new (&dst) Container(NoExceptConstructor());
+ throw;
+ }
+ }
+ return dst;
+}
+
} // namespace blender
diff --git a/source/blender/blenlib/BLI_mesh_boolean.hh b/source/blender/blenlib/BLI_mesh_boolean.hh
new file mode 100644
index 00000000000..693639f20f2
--- /dev/null
+++ b/source/blender/blenlib/BLI_mesh_boolean.hh
@@ -0,0 +1,79 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+/** \file
+ * \ingroup bli
+ */
+
+/* The boolean functions in Blenlib use exact arithmetic, so require GMP. */
+#ifdef WITH_GMP
+
+# include "BLI_mesh_intersect.hh"
+# include <functional>
+
+namespace blender::meshintersect {
+
+/**
+ * Enum values after BOOLEAN_NONE need to match BMESH_ISECT_BOOLEAN_... values in
+ * editmesh_intersect.c. */
+enum class BoolOpType {
+ None = -1,
+ /* Aligned with #BooleanModifierOp. */
+ Intersect = 0,
+ Union = 1,
+ Difference = 2,
+};
+
+/**
+ * Do the boolean operation op on the mesh pm_in.
+ * The boolean operation has nshapes input shapes. Each is a disjoint subset of the input mesh.
+ * The shape_fn argument, when applied to an input face argument, says which shape it is in
+ * (should be a value from -1 to nshapes - 1: if -1, it is not part of any shape).
+ * The use_self arg says whether or not the function should assume that faces in the
+ * same shape intersect - if the argument is true, such self-intersections will be found.
+ * Sometimes the caller has already done a triangulation of the faces,
+ * and if so, *pm_triangulated contains a triangulation: if non-null, it contains a mesh
+ * of triangles, each of whose orig_field says which face in pm that triangle belongs to.
+ * pm arg isn't const because we may populate its verts (for debugging).
+ * Same goes for the pm_triangulated arg.
+ * The output IMesh will have faces whose orig fields map back to faces and edges in
+ * the input mesh.
+ */
+IMesh boolean_mesh(IMesh &imesh,
+ BoolOpType op,
+ int nshapes,
+ std::function<int(int)> shape_fn,
+ bool use_self,
+ IMesh *pm_triangulated,
+ IMeshArena *arena);
+
+/**
+ * This is like boolean, but operates on IMesh's whose faces are all triangles.
+ * It is exposed mainly for unit testing, at the moment: boolean_mesh() uses
+ * it to do most of its work.
+ */
+IMesh boolean_trimesh(IMesh &trimesh,
+ BoolOpType op,
+ int nshapes,
+ std::function<int(int)> shape_fn,
+ bool use_self,
+ IMeshArena *arena);
+
+} // namespace blender::meshintersect
+
+#endif /* WITH_GMP */
diff --git a/source/blender/blenlib/BLI_mesh_intersect.hh b/source/blender/blenlib/BLI_mesh_intersect.hh
new file mode 100644
index 00000000000..877363b998a
--- /dev/null
+++ b/source/blender/blenlib/BLI_mesh_intersect.hh
@@ -0,0 +1,359 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+/** \file
+ * \ingroup bli
+ *
+ * Mesh intersection library functions.
+ * Uses exact arithmetic, so need GMP.
+ */
+
+#ifdef WITH_GMP
+
+# include <iostream>
+
+# include "BLI_array.hh"
+# include "BLI_double3.hh"
+# include "BLI_index_range.hh"
+# include "BLI_map.hh"
+# include "BLI_math_mpq.hh"
+# include "BLI_mpq3.hh"
+# include "BLI_span.hh"
+# include "BLI_utility_mixins.hh"
+# include "BLI_vector.hh"
+
+namespace blender::meshintersect {
+
+constexpr int NO_INDEX = -1;
+
+/**
+ * Vertex coordinates are stored both as #double3 and #mpq3, which should agree.
+ * Most calculations are done in exact arithmetic, using the mpq3 version,
+ * but some predicates can be sped up by operating on doubles and using error analysis
+ * to find the cases where that is good enough.
+ * Vertices also carry along an id, created on allocation. The id
+ * is useful for making algorithms that don't depend on pointers.
+ * Also, they are easier to read while debugging.
+ * They also carry an orig index, which can be used to tie them back to
+ * vertices that the caller may have in a different way (e.g., #BMVert).
+ * An orig index can be #NO_INDEX, indicating the Vert was created by
+ * the algorithm and doesn't match an original Vert.
+ * Vertices can be reliably compared for equality,
+ * and hashed (on their co_exact field).
+ */
+struct Vert {
+ mpq3 co_exact;
+ double3 co;
+ int id = NO_INDEX;
+ int orig = NO_INDEX;
+
+ Vert() = default;
+ Vert(const mpq3 &mco, const double3 &dco, int id, int orig);
+ ~Vert() = default;
+
+ /** Test equality on the co_exact field. */
+ bool operator==(const Vert &other) const;
+
+ /** Hash on the co_exact field. */
+ uint64_t hash() const;
+};
+
+std::ostream &operator<<(std::ostream &os, const Vert *v);
+
+/**
+ * A Plane whose equation is `dot(norm, p) + d = 0`.
+ * The norm and d fields are always present, but the norm_exact
+ * and d_exact fields may be lazily populated. Since we don't
+ * store degenerate planes, we can tell if a the exact versions
+ * are not populated yet by having `norm_exact == 0`.
+ */
+struct Plane {
+ mpq3 norm_exact;
+ mpq_class d_exact;
+ double3 norm;
+ double d;
+
+ Plane() = default;
+ Plane(const mpq3 &norm_exact, const mpq_class &d_exact);
+ Plane(const double3 &norm, const double d);
+
+ /* Test equality on the exact fields. */
+ bool operator==(const Plane &other) const;
+
+ /* Hash onthe exact fields. */
+ uint64_t hash() const;
+
+ void make_canonical();
+ bool exact_populated() const;
+ void populate_exact();
+};
+
+std::ostream &operator<<(std::ostream &os, const Plane *plane);
+
+/**
+ * A #Face has a sequence of Verts that for a CCW ordering around them.
+ * Faces carry an index, created at allocation time, useful for making
+ * pointer-independent algorithms, and for debugging.
+ * They also carry an original index, meaningful to the caller.
+ * And they carry original edge indices too: each is a number meaningful
+ * to the caller for the edge starting from the corresponding face position.
+ * A "face position" is the index of a vertex around a face.
+ * Faces don't own the memory pointed at by the vert array.
+ * Also indexed by face position, the is_intersect array says
+ * for each edge whether or not it is the result of intersecting
+ * with another face in the intersect algorithm.
+ * Since the intersect algorithm needs the plane for each face,
+ * a #Face also stores the Plane of the face, but this is only
+ * populate later because not all faces will be intersected.
+ */
+struct Face : NonCopyable {
+ Array<const Vert *> vert;
+ Array<int> edge_orig;
+ Array<bool> is_intersect;
+ Plane *plane = nullptr;
+ int id = NO_INDEX;
+ int orig = NO_INDEX;
+
+ using FacePos = int;
+
+ Face() = default;
+ Face(Span<const Vert *> verts, int id, int orig, Span<int> edge_origs, Span<bool> is_intersect);
+ Face(Span<const Vert *> verts, int id, int orig);
+ ~Face();
+
+ bool is_tri() const
+ {
+ return vert.size() == 3;
+ }
+
+ /* Test equality of verts, in same positions. */
+ bool operator==(const Face &other) const;
+
+ /* Test equaliy faces allowing cyclic shifts. */
+ bool cyclic_equal(const Face &other) const;
+
+ FacePos next_pos(FacePos p) const
+ {
+ return (p + 1) % vert.size();
+ }
+
+ FacePos prev_pos(FacePos p) const
+ {
+ return (p + vert.size() - 1) % vert.size();
+ }
+
+ const Vert *const &operator[](int index) const
+ {
+ return vert[index];
+ }
+
+ int size() const
+ {
+ return vert.size();
+ }
+
+ const Vert *const *begin() const
+ {
+ return vert.begin();
+ }
+
+ const Vert *const *end() const
+ {
+ return vert.end();
+ }
+
+ IndexRange index_range() const
+ {
+ return IndexRange(vert.size());
+ }
+
+ void populate_plane(bool need_exact);
+
+ bool plane_populated() const
+ {
+ return plane != nullptr;
+ }
+};
+
+std::ostream &operator<<(std::ostream &os, const Face *f);
+
+/**
+ * #IMeshArena is the owner of the Vert and Face resources used
+ * during a run of one of the mesh-intersect main functions.
+ * It also keeps has a hash table of all Verts created so that it can
+ * ensure that only one instance of a Vert with a given co_exact will
+ * exist. I.e., it de-duplicates the vertices.
+ */
+class IMeshArena : NonCopyable, NonMovable {
+ class IMeshArenaImpl;
+ std::unique_ptr<IMeshArenaImpl> pimpl_;
+
+ public:
+ IMeshArena();
+ ~IMeshArena();
+
+ /**
+ * Provide hints to number of expected Verts and Faces expected
+ * to be allocated.
+ */
+ void reserve(int vert_num_hint, int face_num_hint);
+
+ int tot_allocated_verts() const;
+ int tot_allocated_faces() const;
+
+ /**
+ * These add routines find and return an existing Vert with the same
+ * co_exact, if it exists (the orig argument is ignored in this case),
+ * or else allocates and returns a new one. The index field of a
+ * newly allocated Vert will be the index in creation order.
+ */
+ const Vert *add_or_find_vert(const mpq3 &co, int orig);
+ const Vert *add_or_find_vert(const double3 &co, int orig);
+
+ Face *add_face(Span<const Vert *> verts,
+ int orig,
+ Span<int> edge_origs,
+ Span<bool> is_intersect);
+ Face *add_face(Span<const Vert *> verts, int orig, Span<int> edge_origs);
+ Face *add_face(Span<const Vert *> verts, int orig);
+
+ /** The following return #nullptr if not found. */
+ const Vert *find_vert(const mpq3 &co) const;
+ const Face *find_face(Span<const Vert *> verts) const;
+};
+
+/**
+ * A #blender::meshintersect::IMesh is a self-contained mesh structure
+ * that can be used in `blenlib` without depending on the rest of Blender.
+ * The Vert and #Face resources used in the #IMesh should be owned by
+ * some #IMeshArena.
+ * The Verts used by a #IMesh can be recovered from the Faces, so
+ * are usually not stored, but on request, the #IMesh can populate
+ * internal structures for indexing exactly the set of needed Verts,
+ * and also going from a Vert pointer to the index in that system.
+ */
+
+class IMesh {
+ Array<Face *> face_; /* Not `const` so can lazily populate planes. */
+ Array<const Vert *> vert_; /* Only valid if vert_populated_. */
+ Map<const Vert *, int> vert_to_index_; /* Only valid if vert_populated_. */
+ bool vert_populated_ = false;
+
+ public:
+ IMesh() = default;
+ IMesh(Span<Face *> faces) : face_(faces)
+ {
+ }
+
+ void set_faces(Span<Face *> faces);
+ Face *face(int index) const
+ {
+ return face_[index];
+ }
+
+ int face_size() const
+ {
+ return face_.size();
+ }
+
+ int vert_size() const
+ {
+ return vert_.size();
+ }
+
+ bool has_verts() const
+ {
+ return vert_populated_;
+ }
+
+ void set_dirty_verts()
+ {
+ vert_populated_ = false;
+ vert_to_index_.clear();
+ vert_ = Array<const Vert *>();
+ }
+
+ /* Pass `max_verts` if there is a good bound estimate on the maximum number of verts. */
+ void populate_vert();
+ void populate_vert(int max_verts);
+
+ const Vert *vert(int index) const
+ {
+ BLI_assert(vert_populated_);
+ return vert_[index];
+ }
+
+ /** Returns index in vert_ where v is, or #NO_INDEX. */
+ int lookup_vert(const Vert *v) const;
+
+ IndexRange vert_index_range() const
+ {
+ BLI_assert(vert_populated_);
+ return IndexRange(vert_.size());
+ }
+
+ IndexRange face_index_range() const
+ {
+ return IndexRange(face_.size());
+ }
+
+ Span<const Vert *> vertices() const
+ {
+ BLI_assert(vert_populated_);
+ return Span<const Vert *>(vert_);
+ }
+
+ Span<Face *> faces() const
+ {
+ return Span<Face *>(face_);
+ }
+
+ /**
+ * Replace face at given index with one that elides the
+ * vertices at the positions in face_pos_erase that are true.
+ * Use arena to allocate the new face in.
+ */
+ void erase_face_positions(int f_index, Span<bool> face_pos_erase, IMeshArena *arena);
+};
+
+std::ostream &operator<<(std::ostream &os, const IMesh &mesh);
+
+/**
+ * The output will have duplicate vertices merged and degenerate triangles ignored.
+ * If the input has overlapping co-planar triangles, then there will be
+ * as many duplicates as there are overlaps in each overlapping triangular region.
+ * The orig field of each #IndexedTriangle will give the orig index in the input #IMesh
+ * that the output triangle was a part of (input can have -1 for that field and then
+ * the index in `tri[]` will be used as the original index).
+ * The orig structure of the output #IMesh gives the originals for vertices and edges.
+ * Note: if the input tm_in has a non-empty orig structure, then it is ignored.
+ */
+IMesh trimesh_self_intersect(const IMesh &tm_in, IMeshArena *arena);
+
+IMesh trimesh_nary_intersect(const IMesh &tm_in,
+ int nshapes,
+ std::function<int(int)> shape_fn,
+ bool use_self,
+ IMeshArena *arena);
+
+/** This has the side effect of populating verts in the #IMesh. */
+void write_obj_mesh(IMesh &m, const std::string &objname);
+
+} /* namespace blender::meshintersect */
+
+#endif /* WITH_GMP */
diff --git a/source/blender/blenlib/BLI_mpq2.hh b/source/blender/blenlib/BLI_mpq2.hh
new file mode 100644
index 00000000000..6261b50466b
--- /dev/null
+++ b/source/blender/blenlib/BLI_mpq2.hh
@@ -0,0 +1,184 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+/** \file
+ * \ingroup bli
+ */
+
+#ifdef WITH_GMP
+
+# include "BLI_math_mpq.hh"
+# include "BLI_mpq3.hh"
+
+namespace blender {
+
+struct mpq2 {
+ mpq_class x, y;
+
+ mpq2() = default;
+
+ mpq2(const mpq_class *ptr) : x{ptr[0]}, y{ptr[1]}
+ {
+ }
+
+ mpq2(mpq_class x, mpq_class y) : x(x), y(y)
+ {
+ }
+
+ mpq2(const mpq2 &other) : x(other.x), y(other.y)
+ {
+ }
+
+ mpq2(mpq2 &&other) noexcept : x(std::move(other.x)), y(std::move(other.y))
+ {
+ }
+
+ ~mpq2() = default;
+
+ mpq2 &operator=(const mpq2 &other)
+ {
+ if (this != &other) {
+ x = other.x;
+ y = other.y;
+ }
+ return *this;
+ }
+
+ mpq2 &operator=(mpq2 &&other) noexcept
+ {
+ x = std::move(other.x);
+ y = std::move(other.y);
+ return *this;
+ }
+
+ mpq2(const mpq3 &other) : x(other.x), y(other.y)
+ {
+ }
+
+ operator mpq_class *()
+ {
+ return &x;
+ }
+
+ operator const mpq_class *() const
+ {
+ return &x;
+ }
+
+ /**
+ * Cannot do this exactly in rational arithmetic!
+ * Approximate by going in and out of doubles.
+ */
+ mpq_class length() const
+ {
+ mpq_class lsquared = dot(*this, *this);
+ return mpq_class(sqrt(lsquared.get_d()));
+ }
+
+ friend mpq2 operator+(const mpq2 &a, const mpq2 &b)
+ {
+ return {a.x + b.x, a.y + b.y};
+ }
+
+ friend mpq2 operator-(const mpq2 &a, const mpq2 &b)
+ {
+ return {a.x - b.x, a.y - b.y};
+ }
+
+ friend mpq2 operator*(const mpq2 &a, mpq_class b)
+ {
+ return {a.x * b, a.y * b};
+ }
+
+ friend mpq2 operator/(const mpq2 &a, mpq_class b)
+ {
+ BLI_assert(b != 0);
+ return {a.x / b, a.y / b};
+ }
+
+ friend mpq2 operator*(mpq_class a, const mpq2 &b)
+ {
+ return b * a;
+ }
+
+ friend bool operator==(const mpq2 &a, const mpq2 &b)
+ {
+ return a.x == b.x && a.y == b.y;
+ }
+
+ friend bool operator!=(const mpq2 &a, const mpq2 &b)
+ {
+ return a.x != b.x || a.y != b.y;
+ }
+
+ friend std::ostream &operator<<(std::ostream &stream, const mpq2 &v)
+ {
+ stream << "(" << v.x << ", " << v.y << ")";
+ return stream;
+ }
+
+ static mpq_class dot(const mpq2 &a, const mpq2 &b)
+ {
+ return a.x * b.x + a.y * b.y;
+ }
+
+ static mpq2 interpolate(const mpq2 &a, const mpq2 &b, mpq_class t)
+ {
+ return a * (1 - t) + b * t;
+ }
+
+ static mpq2 abs(const mpq2 &a)
+ {
+ mpq_class abs_x = (a.x >= 0) ? a.x : -a.x;
+ mpq_class abs_y = (a.y >= 0) ? a.y : -a.y;
+ return mpq2(abs_x, abs_y);
+ }
+
+ static mpq_class distance(const mpq2 &a, const mpq2 &b)
+ {
+ return (a - b).length();
+ }
+
+ static mpq_class distance_squared(const mpq2 &a, const mpq2 &b)
+ {
+ return dot(a, b);
+ }
+
+ struct isect_result {
+ enum {
+ LINE_LINE_COLINEAR = -1,
+ LINE_LINE_NONE = 0,
+ LINE_LINE_EXACT = 1,
+ LINE_LINE_CROSS = 2,
+ } kind;
+ mpq_class lambda;
+ mpq_class mu;
+ };
+
+ static isect_result isect_seg_seg(const mpq2 &v1,
+ const mpq2 &v2,
+ const mpq2 &v3,
+ const mpq2 &v4);
+
+ /** There is a sensible use for hashing on exact arithmetic types. */
+ uint64_t hash() const;
+};
+
+} // namespace blender
+
+#endif /* WITH_GMP */
diff --git a/source/blender/blenlib/BLI_mpq3.hh b/source/blender/blenlib/BLI_mpq3.hh
new file mode 100644
index 00000000000..fb5e2b61cdb
--- /dev/null
+++ b/source/blender/blenlib/BLI_mpq3.hh
@@ -0,0 +1,281 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+/** \file
+ * \ingroup bli
+ */
+
+#ifdef WITH_GMP
+
+# include <iostream>
+
+# include "BLI_math.h"
+# include "BLI_math_mpq.hh"
+# include "BLI_span.hh"
+
+namespace blender {
+
+struct mpq3 {
+ mpq_class x, y, z;
+
+ mpq3() = default;
+
+ mpq3(const mpq_class *ptr) : x{ptr[0]}, y{ptr[1]}, z{ptr[2]}
+ {
+ }
+
+ mpq3(const mpq_class (*ptr)[3]) : mpq3((const mpq_class *)ptr)
+ {
+ }
+
+ explicit mpq3(mpq_class value) : x(value), y(value), z(value)
+ {
+ }
+
+ explicit mpq3(int value) : x(value), y(value), z(value)
+ {
+ }
+
+ mpq3(mpq_class x, mpq_class y, mpq_class z) : x{x}, y{y}, z{z}
+ {
+ }
+
+ operator const mpq_class *() const
+ {
+ return &x;
+ }
+
+ operator mpq_class *()
+ {
+ return &x;
+ }
+
+ /* Cannot do this exactly in rational arithmetic!
+ * Approximate by going in and out of doubles.
+ */
+ mpq_class normalize_and_get_length()
+ {
+ double dv[3] = {x.get_d(), y.get_d(), z.get_d()};
+ double len = normalize_v3_db(dv);
+ this->x = mpq_class(dv[0]);
+ this->y = mpq_class(dv[1]);
+ this->z = mpq_class(dv[2]);
+ return len;
+ }
+
+ mpq3 normalized() const
+ {
+ double dv[3] = {x.get_d(), y.get_d(), z.get_d()};
+ double dr[3];
+ normalize_v3_v3_db(dr, dv);
+ return mpq3(mpq_class(dr[0]), mpq_class(dr[1]), mpq_class(dr[2]));
+ }
+
+ /* Cannot do this exactly in rational arithmetic!
+ * Approximate by going in and out of double.
+ */
+ mpq_class length() const
+ {
+ mpq_class lsquared = this->length_squared();
+ double dsquared = lsquared.get_d();
+ double d = sqrt(dsquared);
+ return mpq_class(d);
+ }
+
+ mpq_class length_squared() const
+ {
+ return x * x + y * y + z * z;
+ }
+
+ void reflect(const mpq3 &normal)
+ {
+ *this = this->reflected(normal);
+ }
+
+ mpq3 reflected(const mpq3 &normal) const
+ {
+ mpq3 result;
+ const mpq_class dot2 = 2 * dot(*this, normal);
+ result.x = this->x - (dot2 * normal.x);
+ result.y = this->y - (dot2 * normal.y);
+ result.z = this->z - (dot2 * normal.z);
+ return result;
+ }
+
+ static mpq3 safe_divide(const mpq3 &a, const mpq3 &b)
+ {
+ mpq3 result;
+ result.x = (b.x == 0) ? mpq_class(0) : a.x / b.x;
+ result.y = (b.y == 0) ? mpq_class(0) : a.y / b.y;
+ result.z = (b.z == 0) ? mpq_class(0) : a.z / b.z;
+ return result;
+ }
+
+ void invert()
+ {
+ x = -x;
+ y = -y;
+ z = -z;
+ }
+
+ friend mpq3 operator+(const mpq3 &a, const mpq3 &b)
+ {
+ return mpq3(a.x + b.x, a.y + b.y, a.z + b.z);
+ }
+
+ void operator+=(const mpq3 &b)
+ {
+ this->x += b.x;
+ this->y += b.y;
+ this->z += b.z;
+ }
+
+ friend mpq3 operator-(const mpq3 &a, const mpq3 &b)
+ {
+ return mpq3(a.x - b.x, a.y - b.y, a.z - b.z);
+ }
+
+ friend mpq3 operator-(const mpq3 &a)
+ {
+ return mpq3(-a.x, -a.y, -a.z);
+ }
+
+ void operator-=(const mpq3 &b)
+ {
+ this->x -= b.x;
+ this->y -= b.y;
+ this->z -= b.z;
+ }
+
+ void operator*=(mpq_class scalar)
+ {
+ this->x *= scalar;
+ this->y *= scalar;
+ this->z *= scalar;
+ }
+
+ void operator*=(const mpq3 &other)
+ {
+ this->x *= other.x;
+ this->y *= other.y;
+ this->z *= other.z;
+ }
+
+ friend mpq3 operator*(const mpq3 &a, const mpq3 &b)
+ {
+ return {a.x * b.x, a.y * b.y, a.z * b.z};
+ }
+
+ friend mpq3 operator*(const mpq3 &a, const mpq_class &b)
+ {
+ return mpq3(a.x * b, a.y * b, a.z * b);
+ }
+
+ friend mpq3 operator*(const mpq_class &a, const mpq3 &b)
+ {
+ return mpq3(a * b.x, a * b.y, a * b.z);
+ }
+
+ friend mpq3 operator/(const mpq3 &a, const mpq_class &b)
+ {
+ BLI_assert(b != 0);
+ return mpq3(a.x / b, a.y / b, a.z / b);
+ }
+
+ friend bool operator==(const mpq3 &a, const mpq3 &b)
+ {
+ return a.x == b.x && a.y == b.y && a.z == b.z;
+ }
+
+ friend bool operator!=(const mpq3 &a, const mpq3 &b)
+ {
+ return a.x != b.x || a.y != b.y || a.z != b.z;
+ }
+
+ friend std::ostream &operator<<(std::ostream &stream, const mpq3 &v)
+ {
+ stream << "(" << v.x << ", " << v.y << ", " << v.z << ")";
+ return stream;
+ }
+
+ static mpq_class dot(const mpq3 &a, const mpq3 &b)
+ {
+ return a.x * b.x + a.y * b.y + a.z * b.z;
+ }
+
+ static mpq3 cross(const mpq3 &a, const mpq3 &b)
+ {
+ return mpq3(a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]);
+ }
+
+ static mpq3 cross_high_precision(const mpq3 &a, const mpq3 &b)
+ {
+ return cross(a, b);
+ }
+
+ static mpq3 project(const mpq3 &a, const mpq3 &b)
+ {
+ const mpq_class mul = mpq3::dot(a, b) / mpq3::dot(b, b);
+ return mpq3(mul * b[0], mul * b[1], mul * b[2]);
+ }
+
+ static mpq_class distance(const mpq3 &a, const mpq3 &b)
+ {
+ mpq3 diff(a.x - b.x, a.y - b.y, a.z - b.z);
+ return diff.length();
+ }
+
+ static mpq_class distance_squared(const mpq3 &a, const mpq3 &b)
+ {
+ mpq3 diff(a.x - b.x, a.y - b.y, a.z - b.z);
+ return mpq3::dot(diff, diff);
+ }
+
+ static mpq3 interpolate(const mpq3 &a, const mpq3 &b, mpq_class t)
+ {
+ mpq_class s = 1 - t;
+ return mpq3(a.x * s + b.x * t, a.y * s + b.y * t, a.z * s + b.z * t);
+ }
+
+ static mpq3 abs(const mpq3 &a)
+ {
+ mpq_class abs_x = (a.x >= 0) ? a.x : -a.x;
+ mpq_class abs_y = (a.y >= 0) ? a.y : -a.y;
+ mpq_class abs_z = (a.z >= 0) ? a.z : -a.z;
+ return mpq3(abs_x, abs_y, abs_z);
+ }
+
+ static int dominant_axis(const mpq3 &a)
+ {
+ mpq_class x = (a.x >= 0) ? a.x : -a.x;
+ mpq_class y = (a.y >= 0) ? a.y : -a.y;
+ mpq_class z = (a.z >= 0) ? a.z : -a.z;
+ return ((x > y) ? ((x > z) ? 0 : 2) : ((y > z) ? 1 : 2));
+ }
+
+ static mpq3 cross_poly(Span<mpq3> poly);
+
+ /** There is a sensible use for hashing on exact arithmetic types. */
+ uint64_t hash() const;
+};
+
+uint64_t hash_mpq_class(const mpq_class &value);
+
+} // namespace blender
+
+#endif /* WITH_GMP */
diff --git a/source/blender/blenlib/BLI_set.hh b/source/blender/blenlib/BLI_set.hh
index 477a03cf623..9684f372db7 100644
--- a/source/blender/blenlib/BLI_set.hh
+++ b/source/blender/blenlib/BLI_set.hh
@@ -170,62 +170,68 @@ class Set {
* is. This is necessary to avoid a high cost when no elements are added at all. An optimized
* grow operation is performed on the first insertion.
*/
- Set()
+ Set(Allocator allocator = {}) noexcept
: removed_slots_(0),
occupied_and_removed_slots_(0),
usable_slots_(0),
slot_mask_(0),
- slots_(1)
+ slots_(1, allocator)
{
}
- ~Set() = default;
+ Set(NoExceptConstructor, Allocator allocator = {}) noexcept : Set(allocator)
+ {
+ }
+
+ Set(Span<Key> values, Allocator allocator = {}) : Set(NoExceptConstructor(), allocator)
+ {
+ this->add_multiple(values);
+ }
/**
* Construct a set that contains the given keys. Duplicates will be removed automatically.
*/
- Set(const std::initializer_list<Key> &list) : Set()
+ Set(const std::initializer_list<Key> &values) : Set(Span<Key>(values))
{
- this->add_multiple(list);
}
+ ~Set() = default;
+
Set(const Set &other) = default;
- Set(Set &&other) noexcept
- : removed_slots_(other.removed_slots_),
- occupied_and_removed_slots_(other.occupied_and_removed_slots_),
- usable_slots_(other.usable_slots_),
- slot_mask_(other.slot_mask_),
- hash_(std::move(other.hash_)),
- is_equal_(std::move(other.is_equal_)),
- slots_(std::move(other.slots_))
+ Set(Set &&other) noexcept(std::is_nothrow_move_constructible_v<SlotArray>)
+ : Set(NoExceptConstructor(), other.slots_.allocator())
+
{
- other.~Set();
- new (&other) Set();
+ if constexpr (std::is_nothrow_move_constructible_v<SlotArray>) {
+ slots_ = std::move(other.slots_);
+ }
+ else {
+ try {
+ slots_ = std::move(other.slots_);
+ }
+ catch (...) {
+ other.noexcept_reset();
+ throw;
+ }
+ }
+ removed_slots_ = other.removed_slots_;
+ occupied_and_removed_slots_ = other.occupied_and_removed_slots_;
+ usable_slots_ = other.usable_slots_;
+ slot_mask_ = other.slot_mask_;
+ hash_ = std::move(other.hash_);
+ is_equal_ = std::move(other.is_equal_);
+ other.noexcept_reset();
}
Set &operator=(const Set &other)
{
- if (this == &other) {
- return *this;
- }
-
- this->~Set();
- new (this) Set(other);
-
- return *this;
+ return copy_assign_container(*this, other);
}
Set &operator=(Set &&other)
{
- if (this == &other) {
- return *this;
- }
-
- this->~Set();
- new (this) Set(std::move(other));
-
- return *this;
+ return move_assign_container(*this, std::move(other));
}
/**
@@ -562,8 +568,13 @@ class Set {
* Optimize the case when the set was empty beforehand. We can avoid some copies here.
*/
if (this->size() == 0) {
- slots_.~Array();
- new (&slots_) SlotArray(total_slots);
+ try {
+ slots_.reinitialize(total_slots);
+ }
+ catch (...) {
+ this->noexcept_reset();
+ throw;
+ }
removed_slots_ = 0;
occupied_and_removed_slots_ = 0;
usable_slots_ = usable_slots;
@@ -574,38 +585,51 @@ class Set {
/* The grown array that we insert the keys into. */
SlotArray new_slots(total_slots);
- for (Slot &slot : slots_) {
- if (slot.is_occupied()) {
- this->add_after_grow_and_destruct_old(slot, new_slots, new_slot_mask);
+ try {
+ for (Slot &slot : slots_) {
+ if (slot.is_occupied()) {
+ this->add_after_grow(slot, new_slots, new_slot_mask);
+ slot.remove();
+ }
}
+ slots_ = std::move(new_slots);
+ }
+ catch (...) {
+ this->noexcept_reset();
+ throw;
}
- /* All occupied slots have been destructed already and empty/removed slots are assumed to be
- * trivially destructible. */
- slots_.clear_without_destruct();
- slots_ = std::move(new_slots);
occupied_and_removed_slots_ -= removed_slots_;
usable_slots_ = usable_slots;
removed_slots_ = 0;
slot_mask_ = new_slot_mask;
}
- void add_after_grow_and_destruct_old(Slot &old_slot,
- SlotArray &new_slots,
- const uint64_t new_slot_mask)
+ void add_after_grow(Slot &old_slot, SlotArray &new_slots, const uint64_t new_slot_mask)
{
const uint64_t hash = old_slot.get_hash(Hash());
SLOT_PROBING_BEGIN (ProbingStrategy, hash, new_slot_mask, slot_index) {
Slot &slot = new_slots[slot_index];
if (slot.is_empty()) {
- slot.relocate_occupied_here(old_slot, hash);
+ slot.occupy(std::move(*old_slot.key()), hash);
return;
}
}
SLOT_PROBING_END();
}
+ /**
+ * In some cases when exceptions are thrown, it's best to just reset the entire container to make
+ * sure that invariants are maintained. This should happen very rarely in practice.
+ */
+ void noexcept_reset() noexcept
+ {
+ Allocator allocator = slots_.allocator();
+ this->~Set();
+ new (this) Set(NoExceptConstructor(), allocator);
+ }
+
template<typename ForwardKey>
bool contains__impl(const ForwardKey &key, const uint64_t hash) const
{
@@ -699,11 +723,11 @@ class Set {
void remove_contained__impl(const ForwardKey &key, const uint64_t hash)
{
BLI_assert(this->contains_as(key));
- removed_slots_++;
SET_SLOT_PROBING_BEGIN (hash, slot) {
if (slot.contains(key, is_equal_, hash)) {
slot.remove();
+ removed_slots_++;
return;
}
}
diff --git a/source/blender/blenlib/BLI_set_slots.hh b/source/blender/blenlib/BLI_set_slots.hh
index ee5da17fcaf..a4d01dfdb68 100644
--- a/source/blender/blenlib/BLI_set_slots.hh
+++ b/source/blender/blenlib/BLI_set_slots.hh
@@ -88,7 +88,7 @@ template<typename Key> class SimpleSetSlot {
* other slot has to be moved as well. The other slot stays in the state it was in before. Its
* optionally stored key remains in a moved-from state.
*/
- SimpleSetSlot(SimpleSetSlot &&other) noexcept
+ SimpleSetSlot(SimpleSetSlot &&other) noexcept(std::is_nothrow_move_constructible_v<Key>)
{
state_ = other.state_;
if (other.state_ == Occupied) {
@@ -139,19 +139,6 @@ template<typename Key> class SimpleSetSlot {
}
/**
- * Move the other slot into this slot and destruct it. We do destruction here, because this way
- * we can avoid a comparison with the state, since we know the slot is occupied.
- */
- void relocate_occupied_here(SimpleSetSlot &other, uint64_t UNUSED(hash))
- {
- BLI_assert(!this->is_occupied());
- BLI_assert(other.is_occupied());
- state_ = Occupied;
- new (&key_buffer_) Key(std::move(*other.key_buffer_));
- other.key_buffer_.ref().~Key();
- }
-
- /**
* Return true, when this slot is occupied and contains a key that compares equal to the given
* key. The hash is used by other slot implementations to determine inequality faster.
*/
@@ -171,8 +158,8 @@ template<typename Key> class SimpleSetSlot {
template<typename ForwardKey> void occupy(ForwardKey &&key, uint64_t UNUSED(hash))
{
BLI_assert(!this->is_occupied());
- state_ = Occupied;
new (&key_buffer_) Key(std::forward<ForwardKey>(key));
+ state_ = Occupied;
}
/**
@@ -181,8 +168,8 @@ template<typename Key> class SimpleSetSlot {
void remove()
{
BLI_assert(this->is_occupied());
- state_ = Removed;
key_buffer_.ref().~Key();
+ state_ = Removed;
}
};
@@ -224,7 +211,7 @@ template<typename Key> class HashedSetSlot {
}
}
- HashedSetSlot(HashedSetSlot &&other) noexcept
+ HashedSetSlot(HashedSetSlot &&other) noexcept(std::is_nothrow_move_constructible_v<Key>)
{
state_ = other.state_;
if (other.state_ == Occupied) {
@@ -259,16 +246,6 @@ template<typename Key> class HashedSetSlot {
return hash_;
}
- void relocate_occupied_here(HashedSetSlot &other, const uint64_t hash)
- {
- BLI_assert(!this->is_occupied());
- BLI_assert(other.is_occupied());
- state_ = Occupied;
- hash_ = hash;
- new (&key_buffer_) Key(std::move(*other.key_buffer_));
- key_buffer_.ref().~Key();
- }
-
template<typename ForwardKey, typename IsEqual>
bool contains(const ForwardKey &key, const IsEqual &is_equal, const uint64_t hash) const
{
@@ -284,16 +261,16 @@ template<typename Key> class HashedSetSlot {
template<typename ForwardKey> void occupy(ForwardKey &&key, const uint64_t hash)
{
BLI_assert(!this->is_occupied());
+ new (&key_buffer_) Key(std::forward<ForwardKey>(key));
state_ = Occupied;
hash_ = hash;
- new (&key_buffer_) Key(std::forward<ForwardKey>(key));
}
void remove()
{
BLI_assert(this->is_occupied());
- state_ = Removed;
key_buffer_.ref().~Key();
+ state_ = Removed;
}
};
@@ -313,7 +290,8 @@ template<typename Key, typename KeyInfo> class IntrusiveSetSlot {
IntrusiveSetSlot() = default;
~IntrusiveSetSlot() = default;
IntrusiveSetSlot(const IntrusiveSetSlot &other) = default;
- IntrusiveSetSlot(IntrusiveSetSlot &&other) noexcept = default;
+ IntrusiveSetSlot(IntrusiveSetSlot &&other) noexcept(std::is_nothrow_move_constructible_v<Key>) =
+ default;
Key *key()
{
@@ -341,14 +319,6 @@ template<typename Key, typename KeyInfo> class IntrusiveSetSlot {
return hash(key_);
}
- void relocate_occupied_here(IntrusiveSetSlot &other, const uint64_t UNUSED(hash))
- {
- BLI_assert(!this->is_occupied());
- BLI_assert(other.is_occupied());
- key_ = std::move(other.key_);
- other.key_.~Key();
- }
-
template<typename ForwardKey, typename IsEqual>
bool contains(const ForwardKey &key, const IsEqual &is_equal, const uint64_t UNUSED(hash)) const
{
@@ -360,7 +330,6 @@ template<typename Key, typename KeyInfo> class IntrusiveSetSlot {
{
BLI_assert(!this->is_occupied());
BLI_assert(KeyInfo::is_not_empty_or_removed(key));
-
key_ = std::forward<ForwardKey>(key);
}
diff --git a/source/blender/blenlib/BLI_stack.hh b/source/blender/blenlib/BLI_stack.hh
index 8eca356ec54..a463ac102f1 100644
--- a/source/blender/blenlib/BLI_stack.hh
+++ b/source/blender/blenlib/BLI_stack.hh
@@ -117,7 +117,7 @@ class Stack {
/**
* Initialize an empty stack. No heap allocation is done.
*/
- Stack(Allocator allocator = {}) : allocator_(allocator)
+ Stack(Allocator allocator = {}) noexcept : allocator_(allocator)
{
inline_chunk_.below = nullptr;
inline_chunk_.above = nullptr;
@@ -129,11 +129,15 @@ class Stack {
size_ = 0;
}
+ Stack(NoExceptConstructor, Allocator allocator = {}) noexcept : Stack(allocator)
+ {
+ }
+
/**
* Create a new stack that contains the given elements. The values are pushed to the stack in
* the order they are in the array.
*/
- Stack(Span<T> values) : Stack()
+ Stack(Span<T> values, Allocator allocator = {}) : Stack(NoExceptConstructor(), allocator)
{
this->push_multiple(values);
}
@@ -147,11 +151,12 @@ class Stack {
* assert(stack.pop() == 6);
* assert(stack.pop() == 5);
*/
- Stack(const std::initializer_list<T> &values) : Stack(Span<T>(values))
+ Stack(const std::initializer_list<T> &values, Allocator allocator = {})
+ : Stack(Span<T>(values), allocator)
{
}
- Stack(const Stack &other) : Stack(other.allocator_)
+ Stack(const Stack &other) : Stack(NoExceptConstructor(), other.allocator_)
{
for (const Chunk *chunk = &other.inline_chunk_; chunk; chunk = chunk->above) {
const T *begin = chunk->begin;
@@ -160,7 +165,8 @@ class Stack {
}
}
- Stack(Stack &&other) noexcept : Stack(other.allocator_)
+ Stack(Stack &&other) noexcept(std::is_nothrow_move_constructible_v<T>)
+ : Stack(NoExceptConstructor(), other.allocator_)
{
uninitialized_relocate_n<T>(
other.inline_buffer_, std::min(other.size_, InlineBufferCapacity), inline_buffer_);
@@ -197,28 +203,14 @@ class Stack {
}
}
- Stack &operator=(const Stack &stack)
+ Stack &operator=(const Stack &other)
{
- if (this == &stack) {
- return *this;
- }
-
- this->~Stack();
- new (this) Stack(stack);
-
- return *this;
+ return copy_assign_container(*this, other);
}
- Stack &operator=(Stack &&stack)
+ Stack &operator=(Stack &&other)
{
- if (this == &stack) {
- return *this;
- }
-
- this->~Stack();
- new (this) Stack(std::move(stack));
-
- return *this;
+ return move_assign_container(*this, std::move(other));
}
/**
@@ -226,21 +218,26 @@ class Stack {
*/
void push(const T &value)
{
- if (top_ == top_chunk_->capacity_end) {
- this->activate_next_chunk(1);
- }
- new (top_) T(value);
- top_++;
- size_++;
+ this->push_as(value);
}
void push(T &&value)
{
+ this->push_as(std::move(value));
+ }
+ template<typename ForwardT> void push_as(ForwardT &&value)
+ {
if (top_ == top_chunk_->capacity_end) {
this->activate_next_chunk(1);
}
- new (top_) T(std::move(value));
- top_++;
- size_++;
+ try {
+ new (top_) T(std::forward<ForwardT>(value));
+ top_++;
+ size_++;
+ }
+ catch (...) {
+ this->move_top_pointer_back_to_below_chunk();
+ throw;
+ }
}
/**
@@ -250,8 +247,8 @@ class Stack {
T pop()
{
BLI_assert(size_ > 0);
+ T value = std::move(*(top_ - 1));
top_--;
- T value = std::move(*top_);
top_->~T();
size_--;
@@ -296,13 +293,18 @@ class Stack {
const int64_t remaining_capacity = top_chunk_->capacity_end - top_;
const int64_t amount = std::min(remaining_values.size(), remaining_capacity);
- uninitialized_copy_n(remaining_values.data(), amount, top_);
+ try {
+ uninitialized_copy_n(remaining_values.data(), amount, top_);
+ }
+ catch (...) {
+ this->move_top_pointer_back_to_below_chunk();
+ throw;
+ }
top_ += amount;
+ size_ += amount;
remaining_values = remaining_values.drop_front(amount);
}
-
- size_ += values.size();
}
/**
@@ -332,6 +334,15 @@ class Stack {
top_ = top_chunk_->begin;
}
+ /* This should only be called by unit tests. */
+ bool is_invariant_maintained() const
+ {
+ if (size_ == 0) {
+ return top_ == inline_chunk_.begin;
+ }
+ return top_ > top_chunk_->begin;
+ }
+
private:
/**
* Changes top_chunk_ to point to a new chunk that is above the current one. The new chunk might
@@ -365,6 +376,18 @@ class Stack {
top_ = top_chunk_->begin;
}
+ void move_top_pointer_back_to_below_chunk()
+ {
+ /* This makes sure that the invariant stays intact after a failed push. */
+ if (size_ == 0) {
+ top_ = inline_chunk_.begin;
+ }
+ else if (top_ == top_chunk_->begin) {
+ top_chunk_ = top_chunk_->below;
+ top_ = top_chunk_->capacity_end;
+ }
+ }
+
void destruct_all_elements()
{
for (T *value = top_chunk_->begin; value != top_; value++) {
diff --git a/source/blender/blenlib/BLI_vector.hh b/source/blender/blenlib/BLI_vector.hh
index 74ce8dd42e7..3c90e1ab755 100644
--- a/source/blender/blenlib/BLI_vector.hh
+++ b/source/blender/blenlib/BLI_vector.hh
@@ -118,7 +118,7 @@ class Vector {
* Create an empty vector.
* This does not do any memory allocation.
*/
- Vector(Allocator allocator = {}) : allocator_(allocator)
+ Vector(Allocator allocator = {}) noexcept : allocator_(allocator)
{
begin_ = inline_buffer_;
end_ = begin_;
@@ -126,12 +126,17 @@ class Vector {
UPDATE_VECTOR_SIZE(this);
}
+ Vector(NoExceptConstructor, Allocator allocator = {}) noexcept : Vector(allocator)
+ {
+ }
+
/**
* Create a vector with a specific size.
* The elements will be default constructed.
* If T is trivially constructible, the elements in the vector are not touched.
*/
- explicit Vector(int64_t size) : Vector()
+ explicit Vector(int64_t size, Allocator allocator = {})
+ : Vector(NoExceptConstructor(), allocator)
{
this->resize(size);
}
@@ -139,7 +144,8 @@ class Vector {
/**
* Create a vector filled with a specific value.
*/
- Vector(int64_t size, const T &value) : Vector()
+ Vector(int64_t size, const T &value, Allocator allocator = {})
+ : Vector(NoExceptConstructor(), allocator)
{
this->resize(size, value);
}
@@ -148,12 +154,12 @@ class Vector {
* Create a vector from an array ref. The values in the vector are copy constructed.
*/
template<typename U, typename std::enable_if_t<std::is_convertible_v<U, T>> * = nullptr>
- Vector(Span<U> values, Allocator allocator = {}) : Vector(allocator)
+ Vector(Span<U> values, Allocator allocator = {}) : Vector(NoExceptConstructor(), allocator)
{
const int64_t size = values.size();
this->reserve(size);
- this->increase_size_by_unchecked(size);
uninitialized_convert_n<U, T>(values.data(), size, begin_);
+ this->increase_size_by_unchecked(size);
}
/**
@@ -182,7 +188,8 @@ class Vector {
/* This constructor should not be called with e.g. Vector(3, 10), because that is
expected to produce the vector (10, 10, 10). */
typename std::enable_if_t<!std::is_convertible_v<InputIt, int>> * = nullptr>
- Vector(InputIt first, InputIt last, Allocator allocator = {}) : Vector(std::move(allocator))
+ Vector(InputIt first, InputIt last, Allocator allocator = {})
+ : Vector(NoExceptConstructor(), allocator)
{
for (InputIt current = first; current != last; ++current) {
this->append(*current);
@@ -196,7 +203,7 @@ class Vector {
* Example Usage:
* Vector<ModifierData *> modifiers(ob->modifiers);
*/
- Vector(ListBase &values) : Vector()
+ Vector(ListBase &values, Allocator allocator = {}) : Vector(NoExceptConstructor(), allocator)
{
LISTBASE_FOREACH (T, value, &values) {
this->append(value);
@@ -226,27 +233,26 @@ class Vector {
* have zero elements afterwards.
*/
template<int64_t OtherInlineBufferCapacity>
- Vector(Vector<T, OtherInlineBufferCapacity, Allocator> &&other) noexcept
- : allocator_(other.allocator_)
+ Vector(Vector<T, OtherInlineBufferCapacity, Allocator> &&other) noexcept(
+ std::is_nothrow_move_constructible_v<T>)
+ : Vector(NoExceptConstructor(), other.allocator_)
{
const int64_t size = other.size();
if (other.is_inline()) {
if (size <= InlineBufferCapacity) {
/* Copy between inline buffers. */
- begin_ = inline_buffer_;
- end_ = begin_ + size;
- capacity_end_ = begin_ + InlineBufferCapacity;
uninitialized_relocate_n(other.begin_, size, begin_);
+ end_ = begin_ + size;
}
else {
/* Copy from inline buffer to newly allocated buffer. */
const int64_t capacity = size;
begin_ = static_cast<T *>(
allocator_.allocate(sizeof(T) * static_cast<size_t>(capacity), alignof(T), AT));
- end_ = begin_ + size;
capacity_end_ = begin_ + capacity;
uninitialized_relocate_n(other.begin_, size, begin_);
+ end_ = begin_ + size;
}
}
else {
@@ -273,28 +279,12 @@ class Vector {
Vector &operator=(const Vector &other)
{
- if (this == &other) {
- return *this;
- }
-
- this->~Vector();
- new (this) Vector(other);
-
- return *this;
+ return copy_assign_container(*this, other);
}
Vector &operator=(Vector &&other)
{
- if (this == &other) {
- return *this;
- }
-
- /* This can be incorrect, when the vector is used to build a recursive data structure. However,
- we don't take care of it at this low level. See https://youtu.be/7Qgd9B1KuMQ?t=840. */
- this->~Vector();
- new (this) Vector(std::move(other));
-
- return *this;
+ return move_assign_container(*this, std::move(other));
}
/**
@@ -474,17 +464,10 @@ class Vector {
* behavior when not enough capacity has been reserved beforehand. Only use this in performance
* critical code.
*/
- void append_unchecked(const T &value)
- {
- BLI_assert(end_ < capacity_end_);
- new (end_) T(value);
- end_++;
- UPDATE_VECTOR_SIZE(this);
- }
- void append_unchecked(T &&value)
+ template<typename ForwardT> void append_unchecked(ForwardT &&value)
{
BLI_assert(end_ < capacity_end_);
- new (end_) T(std::move(value));
+ new (end_) T(std::forward<ForwardT>(value));
end_++;
UPDATE_VECTOR_SIZE(this);
}
@@ -497,7 +480,7 @@ class Vector {
{
BLI_assert(n >= 0);
this->reserve(this->size() + n);
- blender::uninitialized_fill_n(end_, n, value);
+ uninitialized_fill_n(end_, n, value);
this->increase_size_by_unchecked(n);
}
@@ -507,7 +490,7 @@ class Vector {
* useful when you want to call constructors in the vector yourself. This should only be done in
* very rare cases and has to be justified every time.
*/
- void increase_size_by_unchecked(const int64_t n)
+ void increase_size_by_unchecked(const int64_t n) noexcept
{
BLI_assert(end_ + n <= capacity_end_);
end_ += n;
@@ -553,7 +536,7 @@ class Vector {
{
BLI_assert(amount >= 0);
BLI_assert(begin_ + amount <= capacity_end_);
- blender::uninitialized_copy_n(start, amount, end_);
+ uninitialized_copy_n(start, amount, end_);
end_ += amount;
UPDATE_VECTOR_SIZE(this);
}
@@ -600,11 +583,29 @@ class Vector {
for (int64_t i = 0; i < move_amount; i++) {
const int64_t src_index = insert_index + move_amount - i - 1;
const int64_t dst_index = new_size - i - 1;
- new (static_cast<void *>(begin_ + dst_index)) T(std::move(begin_[src_index]));
+ try {
+ new (static_cast<void *>(begin_ + dst_index)) T(std::move(begin_[src_index]));
+ }
+ catch (...) {
+ /* Destruct all values that have been moved already. */
+ destruct_n(begin_ + dst_index + 1, i);
+ end_ = begin_ + src_index + 1;
+ UPDATE_VECTOR_SIZE(this);
+ throw;
+ }
begin_[src_index].~T();
}
- std::uninitialized_copy_n(first, insert_amount, begin_ + insert_index);
+ try {
+ std::uninitialized_copy_n(first, insert_amount, begin_ + insert_index);
+ }
+ catch (...) {
+ /* Destruct all values that have been moved. */
+ destruct_n(begin_ + new_size - move_amount, move_amount);
+ end_ = begin_ + insert_index;
+ UPDATE_VECTOR_SIZE(this);
+ throw;
+ }
end_ = begin_ + new_size;
UPDATE_VECTOR_SIZE(this);
}
@@ -686,8 +687,8 @@ class Vector {
T pop_last()
{
BLI_assert(!this->is_empty());
+ T value = std::move(*(end_ - 1));
end_--;
- T value = std::move(*end_);
end_->~T();
UPDATE_VECTOR_SIZE(this);
return value;
@@ -702,10 +703,10 @@ class Vector {
BLI_assert(index >= 0);
BLI_assert(index < this->size());
T *element_to_remove = begin_ + index;
- end_--;
if (element_to_remove < end_) {
- *element_to_remove = std::move(*end_);
+ *element_to_remove = std::move(*(end_ - 1));
}
+ end_--;
end_->~T();
UPDATE_VECTOR_SIZE(this);
}
@@ -741,6 +742,27 @@ class Vector {
}
/**
+ * Remove a contiguous chunk of elements and move all values coming after it towards the front.
+ * This takes O(n) time.
+ *
+ * This is similar to std::vector::erase.
+ */
+ void remove(const int64_t start_index, const int64_t amount)
+ {
+ const int64_t old_size = this->size();
+ BLI_assert(start_index >= 0);
+ BLI_assert(amount >= 0);
+ BLI_assert(start_index + amount <= old_size);
+ const int64_t move_amount = old_size - start_index - amount;
+ for (int64_t i = 0; i < move_amount; i++) {
+ begin_[start_index + i] = std::move(begin_[start_index + amount + i]);
+ }
+ destruct_n(end_ - amount, amount);
+ end_ -= amount;
+ UPDATE_VECTOR_SIZE(this);
+ }
+
+ /**
* Do a linear search to find the value in the vector.
* When found, return the first index, otherwise return -1.
*/
@@ -901,7 +923,13 @@ class Vector {
T *new_array = static_cast<T *>(
allocator_.allocate(static_cast<size_t>(new_capacity) * sizeof(T), alignof(T), AT));
- uninitialized_relocate_n(begin_, size, new_array);
+ try {
+ uninitialized_relocate_n(begin_, size, new_array);
+ }
+ catch (...) {
+ allocator_.deallocate(new_array);
+ throw;
+ }
if (!this->is_inline()) {
allocator_.deallocate(begin_);
diff --git a/source/blender/blenlib/BLI_winstuff.h b/source/blender/blenlib/BLI_winstuff.h
index 2de6098f6be..8076724a214 100644
--- a/source/blender/blenlib/BLI_winstuff.h
+++ b/source/blender/blenlib/BLI_winstuff.h
@@ -28,6 +28,8 @@
# error "This include is for Windows only!"
#endif
+#include "BLI_sys_types.h"
+
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
@@ -86,6 +88,7 @@ typedef long ssize_t;
# endif
#endif
+/* Directory reading compatibility with UNIX. */
struct dirent {
int d_ino;
int d_off;
@@ -99,13 +102,12 @@ typedef struct __dirstream DIR;
DIR *opendir(const char *path);
struct dirent *readdir(DIR *dp);
int closedir(DIR *dp);
-
-void RegisterBlendExtension(void);
-void get_default_root(char *root);
-int check_file_chars(char *filename);
const char *dirname(char *path);
-int BLI_getInstallationDir(char *str);
+/* Windows utility functions. */
+void BLI_windows_register_blend_extension(const bool background);
+void BLI_windows_get_default_root_dir(char *root_dir);
+int BLI_windows_get_executable_dir(char *str);
#ifdef __cplusplus
}
diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt
index 819c74b6946..1db45cff09a 100644
--- a/source/blender/blenlib/CMakeLists.txt
+++ b/source/blender/blenlib/CMakeLists.txt
@@ -32,6 +32,7 @@ set(INC
set(INC_SYS
${ZLIB_INCLUDE_DIRS}
${FREETYPE_INCLUDE_DIRS}
+ ${GMP_INCLUDE_DIRS}
)
set(SRC
@@ -64,7 +65,7 @@ set(SRC
intern/boxpack_2d.c
intern/buffer.c
intern/convexhull_2d.c
- intern/delaunay_2d.c
+ intern/delaunay_2d.cc
intern/dot_export.cc
intern/dynlib.c
intern/easing.c
@@ -89,6 +90,7 @@ set(SRC
intern/math_base_inline.c
intern/math_base_safe_inline.c
intern/math_bits_inline.c
+ intern/math_boolean.cc
intern/math_color.c
intern/math_color_blend_inline.c
intern/math_color_inline.c
@@ -99,9 +101,12 @@ set(SRC
intern/math_rotation.c
intern/math_solvers.c
intern/math_statistics.c
+ intern/math_vec.cc
intern/math_vector.c
intern/math_vector_inline.c
intern/memory_utils.c
+ intern/mesh_boolean.cc
+ intern/mesh_intersect.cc
intern/noise.c
intern/path_util.c
intern/polyfill_2d.c
@@ -170,6 +175,8 @@ set(SRC
BLI_dlrbTree.h
BLI_dot_export.hh
BLI_dot_export_attribute_enums.hh
+ BLI_double2.hh
+ BLI_double3.hh
BLI_dynlib.h
BLI_dynstr.h
BLI_easing.h
@@ -214,11 +221,13 @@ set(SRC
BLI_math_base.h
BLI_math_base_safe.h
BLI_math_bits.h
+ BLI_math_boolean.hh
BLI_math_color.h
BLI_math_color_blend.h
BLI_math_geom.h
BLI_math_inline.h
BLI_math_interp.h
+ BLI_math_mpq.hh
BLI_math_matrix.h
BLI_math_rotation.h
BLI_math_solvers.h
@@ -230,6 +239,10 @@ set(SRC
BLI_memory_utils.h
BLI_memory_utils.hh
BLI_mempool.h
+ BLI_mesh_boolean.hh
+ BLI_mesh_intersect.hh
+ BLI_mpq2.hh
+ BLI_mpq3.hh
BLI_noise.h
BLI_path_util.h
BLI_polyfill_2d.h
@@ -306,6 +319,18 @@ if(WITH_TBB)
)
endif()
+if(WITH_GMP)
+ add_definitions(-DWITH_GMP)
+
+ list(APPEND INC_SYS
+ ${GMP_INCLUDE_DIRS}
+ )
+
+ list(APPEND LIB
+ ${GMP_LIBRARIES}
+ )
+endif()
+
if(WIN32)
list(APPEND INC
../../../intern/utfconv
@@ -374,6 +399,8 @@ if(WITH_GTESTS)
tests/BLI_math_vector_test.cc
tests/BLI_memiter_test.cc
tests/BLI_memory_utils_test.cc
+ tests/BLI_mesh_boolean_test.cc
+ tests/BLI_mesh_intersect_test.cc
tests/BLI_multi_value_map_test.cc
tests/BLI_path_util_test.cc
tests/BLI_polyfill_2d_test.cc
@@ -390,6 +417,8 @@ if(WITH_GTESTS)
tests/BLI_task_test.cc
tests/BLI_vector_set_test.cc
tests/BLI_vector_test.cc
+
+ tests/BLI_exception_safety_test_utils.hh
)
set(TEST_INC
../imbuf
diff --git a/source/blender/blenlib/intern/delaunay_2d.c b/source/blender/blenlib/intern/delaunay_2d.c
deleted file mode 100644
index baf49ddaffd..00000000000
--- a/source/blender/blenlib/intern/delaunay_2d.c
+++ /dev/null
@@ -1,5170 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-/** \file
- * \ingroup bli
- *
- * Constrained 2d Delaunay Triangulation.
- */
-
-#include "MEM_guardedalloc.h"
-
-#include "BLI_array.h"
-#include "BLI_bitmap.h"
-#include "BLI_linklist.h"
-#include "BLI_math.h"
-#include "BLI_memarena.h"
-#include "BLI_mempool.h"
-
-#include "BLI_delaunay_2d.h"
-
-/* Uncomment this define to get helpful debugging functions etc. defined. */
-// #define DEBUG_CDT
-
-struct CDTEdge;
-struct CDTFace;
-struct CDTVert;
-
-typedef struct SymEdge {
- struct SymEdge *next; /* In face, doing CCW traversal of face. */
- struct SymEdge *rot; /* CCW around vert. */
- struct CDTVert *vert; /* Vert at origin. */
- struct CDTEdge *edge; /* Undirected edge this is for. */
- struct CDTFace *face; /* Face on left side. */
-} SymEdge;
-
-typedef struct CDTVert {
- double co[2]; /* Coordinate. */
- SymEdge *symedge; /* Some edge attached to it. */
- LinkNode *input_ids; /* List of corresponding vertex input ids. */
- int index; /* Index into array that cdt keeps. */
- int merge_to_index; /* Index of a CDTVert that this has merged to. -1 if no merge. */
- int visit_index; /* Which visit epoch has this been seen. */
-} CDTVert;
-
-typedef struct CDTEdge {
- LinkNode *input_ids; /* List of input edge ids that this is part of. */
- SymEdge symedges[2]; /* The directed edges for this edge. */
- bool in_queue; /* Used in flipping algorithm. */
-} CDTEdge;
-
-typedef struct CDTFace {
- SymEdge *symedge; /* A symedge in face; only used during output, so only valid then. */
- LinkNode *input_ids; /* List of input face ids that this is part of. */
- int visit_index; /* Which visit epoch has this been seen. */
- bool deleted; /* Marks this face no longer used. */
- bool in_queue; /* Used in remove_small_features algorithm. */
-} CDTFace;
-
-typedef struct CDT_state {
- LinkNode *edges; /* List of CDTEdge pointer. */
- LinkNode *faces; /* List of CDTFace pointer. */
- CDTFace *outer_face; /* Which CDTFace is the outer face. */
- CDTVert **vert_array; /* Array of CDTVert pointer, grows. */
- int vert_array_len; /* Current length of vert_array. */
- int vert_array_len_alloc; /* Allocated length of vert_array. */
- int input_vert_tot; /* How many verts were in input (will be first in vert_array). */
- double minx; /* Used for debug drawing. */
- double miny; /* Used for debug drawing. */
- double maxx; /* Used for debug drawing. */
- double maxy; /* Used for debug drawing. */
- double margin; /* Used for debug drawing. */
- int visit_count; /* Used for visiting things without having to initialized their visit fields. */
- int face_edge_offset; /* Input edge id where we start numbering the face edges. */
- MemArena *arena; /* Most allocations are done from here, so can free all at once at end. */
- BLI_mempool *listpool; /* Allocations of ListNodes done from this pool. */
- double epsilon; /* The user-specified nearness limit. */
- double epsilon_squared; /* Square of epsilon. */
- bool output_prepared; /* Set after the mesh has been modified for output (may not be all
- triangles now). */
-} CDT_state;
-
-#define DLNY_ARENASIZE 1 << 14
-
-#ifdef DEBUG_CDT
-# ifdef __GNUC__
-# define ATTU __attribute__((unused))
-# else
-# define ATTU
-# endif
-# define F2(p) p[0], p[1]
-# define F3(p) p[0], p[1], p[2]
-struct CrossData;
-ATTU static void dump_se(const SymEdge *se, const char *lab);
-ATTU static void dump_se_short(const SymEdge *se, const char *lab);
-ATTU static void dump_v(const CDTVert *v, const char *lab);
-ATTU static void dump_se_cycle(const SymEdge *se, const char *lab, const int limit);
-ATTU static void dump_id_list(const LinkNode *id_list, const char *lab);
-ATTU static void dump_cross_data(struct CrossData *cd, const char *lab);
-ATTU static void dump_cdt(const CDT_state *cdt, const char *lab);
-ATTU static void dump_cdt_vert_neighborhood(CDT_state *cdt, int v, int maxdist, const char *lab);
-ATTU static void cdt_draw(CDT_state *cdt, const char *lab);
-ATTU static void cdt_draw_region(
- CDT_state *cdt, const char *lab, double minx, double miny, double maxx, double maxy);
-
-ATTU static void cdt_draw_vertex_region(CDT_state *cdt, int v, double dist, const char *lab);
-ATTU static void cdt_draw_edge_region(
- CDT_state *cdt, int v1, int v2, double dist, const char *lab);
-ATTU static void write_cdt_input_to_file(const CDT_input *inp);
-ATTU static void validate_cdt(CDT_state *cdt,
- bool check_all_tris,
- bool check_delaunay,
- bool check_visibility);
-#endif
-
-static void exactinit(void);
-static double orient2d(const double *pa, const double *pb, const double *pc);
-static double incircle(const double *pa, const double *pb, const double *pc, const double *pd);
-
-/** Return other #SymEdge for same #CDTEdge as se. */
-BLI_INLINE SymEdge *sym(const SymEdge *se)
-{
- return se->next->rot;
-}
-
-/** Return SymEdge whose next is se. */
-BLI_INLINE SymEdge *prev(const SymEdge *se)
-{
- return se->rot->next->rot;
-}
-
-/**
- * Return true if a -- b -- c are in that order, assuming they are on a straight line according to
- * orient2d and we know the order is either `abc` or `bac`.
- * This means `ab . ac` and `bc . ac` must both be non-negative. */
-static bool in_line(const double a[2], const double b[2], const double c[2])
-{
- double ab[2], bc[2], ac[2];
- sub_v2_v2v2_db(ab, b, a);
- sub_v2_v2v2_db(bc, c, b);
- sub_v2_v2v2_db(ac, c, a);
- if (dot_v2v2_db(ab, ac) < 0.0) {
- return false;
- }
- return dot_v2v2_db(bc, ac) >= 0.0;
-}
-
-#ifndef NDEBUG
-/** Is s2 reachable from s1 by next pointers with < limit hops? */
-static bool reachable(SymEdge *s1, SymEdge *s2, int limit)
-{
- int count = 0;
- for (SymEdge *s = s1; s && count < limit; s = s->next) {
- if (s == s2) {
- return true;
- }
- count++;
- }
- return false;
-}
-#endif
-
-/** Using array to store these instead of linked list so can make a random selection from them. */
-static CDTVert *add_cdtvert(CDT_state *cdt, double x, double y)
-{
- CDTVert *v = BLI_memarena_alloc(cdt->arena, sizeof(*v));
- v->co[0] = x;
- v->co[1] = y;
- v->input_ids = NULL;
- v->symedge = NULL;
- if (cdt->vert_array_len == cdt->vert_array_len_alloc) {
- CDTVert **old_array = cdt->vert_array;
- cdt->vert_array_len_alloc *= 4;
- cdt->vert_array = BLI_memarena_alloc(cdt->arena,
- cdt->vert_array_len_alloc * sizeof(cdt->vert_array[0]));
- memmove(cdt->vert_array, old_array, cdt->vert_array_len * sizeof(cdt->vert_array[0]));
- }
- BLI_assert(cdt->vert_array_len < cdt->vert_array_len_alloc);
- v->index = cdt->vert_array_len;
- v->merge_to_index = -1;
- v->visit_index = 0;
- cdt->vert_array[cdt->vert_array_len++] = v;
- return v;
-}
-
-static CDTEdge *add_cdtedge(
- CDT_state *cdt, CDTVert *v1, CDTVert *v2, CDTFace *fleft, CDTFace *fright)
-{
- CDTEdge *e = BLI_memarena_alloc(cdt->arena, sizeof(*e));
- SymEdge *se = &e->symedges[0];
- SymEdge *sesym = &e->symedges[1];
- e->input_ids = NULL;
- e->in_queue = false;
- BLI_linklist_prepend_arena(&cdt->edges, (void *)e, cdt->arena);
- se->edge = sesym->edge = e;
- se->face = fleft;
- sesym->face = fright;
- se->vert = v1;
- if (v1->symedge == NULL) {
- v1->symedge = se;
- }
- sesym->vert = v2;
- if (v2->symedge == NULL) {
- v2->symedge = sesym;
- }
- se->next = sesym->next = se->rot = sesym->rot = NULL;
- return e;
-}
-
-static CDTFace *add_cdtface(CDT_state *cdt)
-{
- CDTFace *f = BLI_memarena_alloc(cdt->arena, sizeof(*f));
- f->visit_index = 0;
- f->deleted = false;
- f->symedge = NULL;
- f->input_ids = NULL;
- f->in_queue = false;
- BLI_linklist_prepend_arena(&cdt->faces, (void *)f, cdt->arena);
- return f;
-}
-
-static bool id_in_list(const LinkNode *id_list, int id)
-{
- const LinkNode *ln;
-
- for (ln = id_list; ln; ln = ln->next) {
- if (POINTER_AS_INT(ln->link) == id) {
- return true;
- }
- }
- return false;
-}
-
-/** is any id in (range_start, range_start+1, ... , range_end) in id_list? */
-static bool id_range_in_list(const LinkNode *id_list, int range_start, int range_end)
-{
- const LinkNode *ln;
- int id;
-
- for (ln = id_list; ln; ln = ln->next) {
- id = POINTER_AS_INT(ln->link);
- if (id >= range_start && id <= range_end) {
- return true;
- }
- }
- return false;
-}
-
-static void add_to_input_ids(LinkNode **dst, int input_id, CDT_state *cdt)
-{
- if (!id_in_list(*dst, input_id)) {
- BLI_linklist_prepend_arena(dst, POINTER_FROM_INT(input_id), cdt->arena);
- }
-}
-
-static void add_list_to_input_ids(LinkNode **dst, const LinkNode *src, CDT_state *cdt)
-{
- const LinkNode *ln;
-
- for (ln = src; ln; ln = ln->next) {
- add_to_input_ids(dst, POINTER_AS_INT(ln->link), cdt);
- }
-}
-
-BLI_INLINE bool is_border_edge(const CDTEdge *e, const CDT_state *cdt)
-{
- return e->symedges[0].face == cdt->outer_face || e->symedges[1].face == cdt->outer_face;
-}
-
-BLI_INLINE bool is_constrained_edge(const CDTEdge *e)
-{
- return e->input_ids != NULL;
-}
-
-BLI_INLINE bool is_deleted_edge(const CDTEdge *e)
-{
- return e->symedges[0].next == NULL;
-}
-
-BLI_INLINE bool is_original_vert(const CDTVert *v, CDT_state *cdt)
-{
- return (v->index < cdt->input_vert_tot);
-}
-
-/** Return the Symedge that goes from v1 to v2, if it exists, else return NULL. */
-static SymEdge *find_symedge_between_verts(const CDTVert *v1, const CDTVert *v2)
-{
- SymEdge *tstart, *t;
-
- t = tstart = v1->symedge;
- do {
- if (t->next->vert == v2) {
- return t;
- }
- } while ((t = t->rot) != tstart);
- return NULL;
-}
-
-/** Return the SymEdge attached to v that has face f, if it exists, else return NULL. */
-static SymEdge *find_symedge_with_face(const CDTVert *v, const CDTFace *f)
-{
- SymEdge *tstart, *t;
-
- t = tstart = v->symedge;
- do {
- if (t->face == f) {
- return t;
- }
- } while ((t = t->rot) != tstart);
- return NULL;
-}
-
-/** Is there already an edge between a and b? */
-static inline bool exists_edge(const CDTVert *v1, const CDTVert *v2)
-{
- return find_symedge_between_verts(v1, v2) != NULL;
-}
-
-/** Is the vertex v incident on face f? */
-static bool vert_touches_face(const CDTVert *v, const CDTFace *f)
-{
- SymEdge *se = v->symedge;
- do {
- if (se->face == f) {
- return true;
- }
- } while ((se = se->rot) != v->symedge);
- return false;
-}
-
-/**
- * Assume s1 and s2 are both SymEdges in a face with > 3 sides,
- * and one is not the next of the other.
- * Add an edge from s1->v to s2->v, splitting the face in two.
- * The original face will continue to be associated with the subface
- * that has s1, and a new face will be made for s2's new face.
- * Return the new diagonal's CDTEdge *.
- */
-static CDTEdge *add_diagonal(CDT_state *cdt, SymEdge *s1, SymEdge *s2)
-{
- CDTEdge *ediag;
- CDTFace *fold, *fnew;
- SymEdge *sdiag, *sdiagsym;
- SymEdge *s1prev, *s1prevsym, *s2prev, *s2prevsym, *se;
- BLI_assert(reachable(s1, s2, 20000));
- BLI_assert(reachable(s2, s1, 20000));
- fold = s1->face;
- fnew = add_cdtface(cdt);
- s1prev = prev(s1);
- s1prevsym = sym(s1prev);
- s2prev = prev(s2);
- s2prevsym = sym(s2prev);
- ediag = add_cdtedge(cdt, s1->vert, s2->vert, fnew, fold);
- sdiag = &ediag->symedges[0];
- sdiagsym = &ediag->symedges[1];
- sdiag->next = s2;
- sdiagsym->next = s1;
- s2prev->next = sdiagsym;
- s1prev->next = sdiag;
- s1->rot = sdiag;
- sdiag->rot = s1prevsym;
- s2->rot = sdiagsym;
- sdiagsym->rot = s2prevsym;
-#ifdef DEBUG_CDT
- BLI_assert(reachable(s2, sdiag, 2000));
-#endif
- for (se = s2; se != sdiag; se = se->next) {
- se->face = fnew;
- }
- add_list_to_input_ids(&fnew->input_ids, fold->input_ids, cdt);
- return ediag;
-}
-
-/**
- * Add a dangling edge from an isolated v to the vert at se in the same face as se->face.
- */
-static CDTEdge *add_vert_to_symedge_edge(CDT_state *cdt, CDTVert *v, SymEdge *se)
-{
- CDTEdge *e;
- SymEdge *se_rot, *se_rotsym, *new_se, *new_se_sym;
-
- se_rot = se->rot;
- se_rotsym = sym(se_rot);
- e = add_cdtedge(cdt, v, se->vert, se->face, se->face);
- new_se = &e->symedges[0];
- new_se_sym = &e->symedges[1];
- new_se->next = se;
- new_se_sym->next = new_se;
- new_se->rot = new_se;
- new_se_sym->rot = se_rot;
- se->rot = new_se_sym;
- se_rotsym->next = new_se_sym;
- return e;
-}
-
-/**
- * Connect the verts of se1 and se2, assuming that currently those two #SymEdges are on
- * the outer boundary (have face == outer_face) of two components that are isolated from
- * each other.
- */
-static CDTEdge *connect_separate_parts(CDT_state *cdt, SymEdge *se1, SymEdge *se2)
-{
- CDTEdge *e;
- SymEdge *se1_rot, *se1_rotsym, *se2_rot, *se2_rotsym, *new_se, *new_se_sym;
-
- BLI_assert(se1->face == cdt->outer_face && se2->face == cdt->outer_face);
- se1_rot = se1->rot;
- se1_rotsym = sym(se1_rot);
- se2_rot = se2->rot;
- se2_rotsym = sym(se2_rot);
- e = add_cdtedge(cdt, se1->vert, se2->vert, cdt->outer_face, cdt->outer_face);
- new_se = &e->symedges[0];
- new_se_sym = &e->symedges[1];
- new_se->next = se2;
- new_se_sym->next = se1;
- new_se->rot = se1_rot;
- new_se_sym->rot = se2_rot;
- se1->rot = new_se;
- se2->rot = new_se_sym;
- se1_rotsym->next = new_se;
- se2_rotsym->next = new_se_sym;
- return e;
-}
-
-/**
- * Split \a se at fraction \a lambda,
- * and return the new #CDTEdge that is the new second half.
- * Copy the edge input_ids into the new one.
- */
-static CDTEdge *split_edge(CDT_state *cdt, SymEdge *se, double lambda)
-{
- const double *a, *b;
- double p[2];
- CDTVert *v;
- CDTEdge *e;
- SymEdge *sesym, *newse, *newsesym, *senext, *sesymprev, *sesymprevsym;
- /* Split e at lambda. */
- a = se->vert->co;
- b = se->next->vert->co;
- sesym = sym(se);
- sesymprev = prev(sesym);
- sesymprevsym = sym(sesymprev);
- senext = se->next;
- p[0] = (1.0 - lambda) * a[0] + lambda * b[0];
- p[1] = (1.0 - lambda) * a[1] + lambda * b[1];
- v = add_cdtvert(cdt, p[0], p[1]);
- e = add_cdtedge(cdt, v, se->next->vert, se->face, sesym->face);
- sesym->vert = v;
- newse = &e->symedges[0];
- newsesym = &e->symedges[1];
- se->next = newse;
- newsesym->next = sesym;
- newse->next = senext;
- newse->rot = sesym;
- sesym->rot = newse;
- senext->rot = newsesym;
- newsesym->rot = sesymprevsym;
- sesymprev->next = newsesym;
- if (newsesym->vert->symedge == sesym) {
- newsesym->vert->symedge = newsesym;
- }
- add_list_to_input_ids(&e->input_ids, se->edge->input_ids, cdt);
- return e;
-}
-
-/**
- * Delete an edge from the structure. The new combined face on either side of
- * the deleted edge will be the one that was e's face.
- * There will be now an unused face, marked by setting its deleted flag,
- * and an unused #CDTEdge, marked by setting the next and rot pointers of
- * its #SymEdge(s) to NULL.
- * <pre>
- * . v2 .
- * / \ / \
- * /f|j\ / \
- * / | \ / \
- * |
- * A | B A
- * \ e| / \ /
- * \ | / \ /
- * \h|i/ \ /
- * . v1 .
- * </pre>
- * Also handle variant cases where one or both ends
- * are attached only to e.
- */
-static void delete_edge(CDT_state *cdt, SymEdge *e)
-{
- SymEdge *esym, *f, *h, *i, *j, *k, *jsym, *hsym;
- CDTFace *aface, *bface;
- CDTVert *v1, *v2;
- bool v1_isolated, v2_isolated;
-
- esym = sym(e);
- v1 = e->vert;
- v2 = esym->vert;
- aface = e->face;
- bface = esym->face;
- f = e->next;
- h = prev(e);
- i = esym->next;
- j = prev(esym);
- jsym = sym(j);
- hsym = sym(h);
- v1_isolated = (i == e);
- v2_isolated = (f == esym);
-
- if (!v1_isolated) {
- h->next = i;
- i->rot = hsym;
- }
- if (!v2_isolated) {
- j->next = f;
- f->rot = jsym;
- }
- if (!v1_isolated && !v2_isolated && aface != bface) {
- for (k = i; k != f; k = k->next) {
- k->face = aface;
- }
- }
-
- /* If e was representative symedge for v1 or v2, fix that. */
- if (v1_isolated) {
- v1->symedge = NULL;
- }
- else if (v1->symedge == e) {
- v1->symedge = i;
- }
- if (v2_isolated) {
- v2->symedge = NULL;
- }
- else if (v2->symedge == esym) {
- v2->symedge = f;
- }
-
- /* Mark SymEdge as deleted by setting all its pointers to NULL. */
- e->next = e->rot = NULL;
- esym->next = esym->rot = NULL;
- if (!v1_isolated && !v2_isolated && aface != bface) {
- bface->deleted = true;
- if (cdt->outer_face == bface) {
- cdt->outer_face = aface;
- }
- }
-}
-
-static CDT_state *cdt_init(const CDT_input *in)
-{
- int i;
- MemArena *arena = BLI_memarena_new(DLNY_ARENASIZE, __func__);
- CDT_state *cdt = BLI_memarena_calloc(arena, sizeof(CDT_state));
-
- cdt->epsilon = (double)in->epsilon;
- cdt->epsilon_squared = cdt->epsilon * cdt->epsilon;
- cdt->arena = arena;
- cdt->input_vert_tot = in->verts_len;
- cdt->vert_array_len_alloc = 2 * in->verts_len;
- cdt->vert_array = BLI_memarena_alloc(arena,
- cdt->vert_array_len_alloc * sizeof(*cdt->vert_array));
- cdt->listpool = BLI_mempool_create(
- sizeof(LinkNode), 128 + 4 * in->verts_len, 128 + in->verts_len, 0);
-
- for (i = 0; i < in->verts_len; i++) {
- add_cdtvert(cdt, (double)(in->vert_coords[i][0]), (double)(in->vert_coords[i][1]));
- }
- cdt->outer_face = add_cdtface(cdt);
- return cdt;
-}
-
-static void new_cdt_free(CDT_state *cdt)
-{
- BLI_mempool_destroy(cdt->listpool);
- BLI_memarena_free(cdt->arena);
-}
-
-typedef struct SiteInfo {
- CDTVert *v;
- int orig_index;
-} SiteInfo;
-
-static int site_lexicographic_cmp(const void *a, const void *b)
-{
- const SiteInfo *s1 = a;
- const SiteInfo *s2 = b;
- const double *co1 = s1->v->co;
- const double *co2 = s2->v->co;
-
- if (co1[0] < co2[0]) {
- return -1;
- }
- if (co1[0] > co2[0]) {
- return 1;
- }
- if (co1[1] < co2[1]) {
- return -1;
- }
- if (co1[1] > co2[1]) {
- return 1;
- }
- if (s1->orig_index < s2->orig_index) {
- return -1;
- }
- if (s1->orig_index > s2->orig_index) {
- return 1;
- }
- return 0;
-}
-
-BLI_INLINE bool vert_left_of_symedge(CDTVert *v, SymEdge *se)
-{
- return orient2d(v->co, se->vert->co, se->next->vert->co) > 0.0;
-}
-
-BLI_INLINE bool vert_right_of_symedge(CDTVert *v, SymEdge *se)
-{
- return orient2d(v->co, se->next->vert->co, se->vert->co) > 0.0;
-}
-
-/* Is se above basel? */
-BLI_INLINE bool dc_tri_valid(SymEdge *se, SymEdge *basel, SymEdge *basel_sym)
-{
- return orient2d(se->next->vert->co, basel_sym->vert->co, basel->vert->co) > 0.0;
-}
-
-/* Delaunay triangulate sites[start} to sites[end-1].
- * Assume sites are lexicographically sorted by coordinate.
- * Return SymEdge of ccw convex hull at left-most point in *r_le
- * and that of right-most point of cw convex null in *r_re.
- */
-static void dc_tri(
- CDT_state *cdt, SiteInfo *sites, int start, int end, SymEdge **r_le, SymEdge **r_re)
-{
- int n = end - start;
- int n2;
- CDTVert *v1, *v2, *v3;
- CDTEdge *ea, *eb, *ebasel;
- SymEdge *ldo, *ldi, *rdi, *rdo, *basel, *basel_sym, *lcand, *rcand, *t;
- double orient;
- bool valid_lcand, valid_rcand;
-#ifdef DEBUG_CDT
- char label_buf[100];
- int dbg_level = 0;
-
- if (dbg_level > 0) {
- fprintf(stderr, "DC_TRI start=%d end=%d\n", start, end);
- }
-#endif
-
- BLI_assert(r_le != NULL && r_re != NULL);
- if (n <= 1) {
- *r_le = NULL;
- *r_re = NULL;
- return;
- }
- if (n <= 3) {
- v1 = sites[start].v;
- v2 = sites[start + 1].v;
- ea = add_cdtedge(cdt, v1, v2, cdt->outer_face, cdt->outer_face);
- ea->symedges[0].next = &ea->symedges[1];
- ea->symedges[1].next = &ea->symedges[0];
- ea->symedges[0].rot = &ea->symedges[0];
- ea->symedges[1].rot = &ea->symedges[1];
- if (n == 2) {
- *r_le = &ea->symedges[0];
- *r_re = &ea->symedges[1];
- return;
- }
- v3 = sites[start + 2].v;
- eb = add_vert_to_symedge_edge(cdt, v3, &ea->symedges[1]);
- orient = orient2d(v1->co, v2->co, v3->co);
- if (orient > 0.0) {
- add_diagonal(cdt, &eb->symedges[0], &ea->symedges[0]);
- *r_le = &ea->symedges[0];
- *r_re = &eb->symedges[0];
- }
- else if (orient < 0.0) {
- add_diagonal(cdt, &ea->symedges[0], &eb->symedges[0]);
- *r_le = ea->symedges[0].rot;
- *r_re = eb->symedges[0].rot;
- }
- else {
- /* Collinear points. Just return a line. */
- *r_le = &ea->symedges[0];
- *r_re = &eb->symedges[0];
- }
- return;
- }
- /* Here: n >= 4. Divide and conquer. */
- n2 = n / 2;
- BLI_assert(n2 >= 2 && end - (start + n2) >= 2);
-
- /* Delaunay triangulate two halves, L and R. */
- dc_tri(cdt, sites, start, start + n2, &ldo, &ldi);
- dc_tri(cdt, sites, start + n2, end, &rdi, &rdo);
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "\nDC_TRI merge step for start=%d, end=%d\n", start, end);
- dump_se(ldo, "ldo");
- dump_se(ldi, "ldi");
- dump_se(rdi, "rdi");
- dump_se(rdo, "rdo");
- if (dbg_level > 1) {
- sprintf(label_buf, "dc_tri(%d,%d)(%d,%d)", start, start + n2, start + n2, end);
- /* dump_cdt(cdt, label_buf); */
- cdt_draw(cdt, label_buf);
- }
- }
-#endif
-
- /* Find lower common tangent of L and R. */
- for (;;) {
- if (vert_left_of_symedge(rdi->vert, ldi)) {
- ldi = ldi->next;
- }
- else if (vert_right_of_symedge(ldi->vert, rdi)) {
- rdi = sym(rdi)->rot; /* Previous edge to rdi with same right face. */
- }
- else {
- break;
- }
- }
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "common lower tangent is between\n");
- dump_se(rdi, "rdi");
- dump_se(ldi, "ldi");
- }
-#endif
- ebasel = connect_separate_parts(cdt, sym(rdi)->next, ldi);
- basel = &ebasel->symedges[0];
- basel_sym = &ebasel->symedges[1];
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- dump_se(basel, "basel");
- cdt_draw(cdt, "after basel made");
- }
-#endif
- if (ldi->vert == ldo->vert) {
- ldo = basel_sym;
- }
- if (rdi->vert == rdo->vert) {
- rdo = basel;
- }
-
- /* Merge loop. */
- for (;;) {
- /* Locate the first point lcand->next->vert encountered by rising bubble,
- * and delete L edges out of basel->next->vert that fail the circle test. */
- lcand = basel_sym->rot;
- rcand = basel_sym->next;
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "\ntop of merge loop\n");
- dump_se(lcand, "lcand");
- dump_se(rcand, "rcand");
- dump_se(basel, "basel");
- }
-#endif
- if (dc_tri_valid(lcand, basel, basel_sym)) {
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "found valid lcand\n");
- dump_se(lcand, " lcand");
- }
-#endif
- while (incircle(basel_sym->vert->co,
- basel->vert->co,
- lcand->next->vert->co,
- lcand->rot->next->vert->co) > 0.0) {
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "incircle says to remove lcand\n");
- dump_se(lcand, " lcand");
- }
-#endif
- t = lcand->rot;
- delete_edge(cdt, sym(lcand));
- lcand = t;
- }
- }
- /* Symmetrically, locate first R point to be hit and delete R edges. */
- if (dc_tri_valid(rcand, basel, basel_sym)) {
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "found valid rcand\n");
- dump_se(rcand, " rcand");
- }
-#endif
- while (incircle(basel_sym->vert->co,
- basel->vert->co,
- rcand->next->vert->co,
- sym(rcand)->next->next->vert->co) > 0.0) {
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "incircle says to remove rcand\n");
- dump_se(lcand, " rcand");
- }
-#endif
- t = sym(rcand)->next;
- delete_edge(cdt, rcand);
- rcand = t;
- }
- }
- /* If both lcand and rcand are invalid, then basel is the common upper tangent. */
- valid_lcand = dc_tri_valid(lcand, basel, basel_sym);
- valid_rcand = dc_tri_valid(rcand, basel, basel_sym);
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(
- stderr, "after bubbling up, valid_lcand=%d, valid_rcand=%d\n", valid_lcand, valid_rcand);
- dump_se(lcand, "lcand");
- dump_se(rcand, "rcand");
- }
-#endif
- if (!valid_lcand && !valid_rcand) {
- break;
- }
- /* The next cross edge to be connected is to either lcand->next->vert or rcand->next->vert;
- * if both are valid, choose the appropriate one using the incircle test.
- */
- if (!valid_lcand ||
- (valid_rcand &&
- incircle(lcand->next->vert->co, lcand->vert->co, rcand->vert->co, rcand->next->vert->co) >
- 0.0)) {
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "connecting rcand\n");
- dump_se(basel_sym, " se1=basel_sym");
- dump_se(rcand->next, " se2=rcand->next");
- }
-#endif
- ebasel = add_diagonal(cdt, rcand->next, basel_sym);
- }
- else {
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "connecting lcand\n");
- dump_se(sym(lcand), " se1=sym(lcand)");
- dump_se(basel_sym->next, " se2=basel_sym->next");
- }
-#endif
- ebasel = add_diagonal(cdt, basel_sym->next, sym(lcand));
- }
- basel = &ebasel->symedges[0];
- basel_sym = &ebasel->symedges[1];
- BLI_assert(basel_sym->face == cdt->outer_face);
-#ifdef DEBUG_CDT
- if (dbg_level > 2) {
- cdt_draw(cdt, "after adding new crossedge");
- // dump_cdt(cdt, "after adding new crossedge");
- }
-#endif
- }
- *r_le = ldo;
- *r_re = rdo;
- BLI_assert(sym(ldo)->face == cdt->outer_face && rdo->face == cdt->outer_face);
-}
-
-/* Guibas-Stolfi Divide-and_Conquer algorithm. */
-static void dc_triangulate(CDT_state *cdt, SiteInfo *sites, int nsites)
-{
- int i, j, n;
- SymEdge *le, *re;
-
- /* Compress sites in place to eliminated verts that merge to others. */
- i = 0;
- j = 0;
- while (j < nsites) {
- /* Invariante: sites[0..i-1] have non-merged verts from 0..(j-1) in them. */
- sites[i] = sites[j++];
- if (sites[i].v->merge_to_index < 0) {
- i++;
- }
- }
- n = i;
- if (n == 0) {
- return;
- }
- dc_tri(cdt, sites, 0, n, &le, &re);
-}
-
-/**
- * Do a Delaunay Triangulation of the points in cdt->vert_array.
- * This is only a first step in the Constrained Delaunay triangulation,
- * because it doesn't yet deal with the segment constraints.
- * The algorithm used is the Divide & Conquer algorithm from the
- * Guibas-Stolfi "Primitives for the Manipulation of General Subdivision
- * and the Computation of Voronoi Diagrams" paper.
- * The data structure here is similar to but not exactly the same as
- * the quad-edge structure described in that paper.
- * The incircle and ccw tests are done using Shewchuk's exact
- * primitives (see below), so that this routine is robust.
- *
- * As a preprocessing step, we want to merge all vertices that are
- * within cdt->epsilon of each other. This is accomplished by lexicographically
- * sorting the coordinates first (which is needed anyway for the D&C algorithm).
- * The CDTVerts with merge_to_index not equal to -1 are after this regarded
- * as having been merged into the vertex with the corresponding index.
- */
-static void initial_triangulation(CDT_state *cdt)
-{
- int i, j, n;
- SiteInfo *sites;
- double *ico, *jco;
- double xend, yend, xcur;
- double epsilon = cdt->epsilon;
- double epsilon_squared = cdt->epsilon_squared;
-#ifdef SJF_WAY
- CDTEdge *e;
- CDTVert *va, *vb;
-#endif
-#ifdef DEBUG_CDT
- int dbg_level = 0;
-
- if (dbg_level > 0) {
- fprintf(stderr, "\nINITIAL TRIANGULATION\n\n");
- }
-#endif
-
- /* First sort the vertices by lexicographic order of their
- * coordinates, breaking ties by putting earlier original-index
- * vertices first.
- */
- n = cdt->vert_array_len;
- if (n <= 1) {
- return;
- }
- sites = MEM_malloc_arrayN(n, sizeof(SiteInfo), __func__);
- for (i = 0; i < n; i++) {
- sites[i].v = cdt->vert_array[i];
- sites[i].orig_index = i;
- }
- qsort(sites, n, sizeof(SiteInfo), site_lexicographic_cmp);
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "after sorting\n");
- for (i = 0; i < n; i++) {
- fprintf(stderr, "%d: orig index: %d, (%f,%f)\n", i, sites[i].orig_index, F2(sites[i].v->co));
- }
- }
-#endif
-
- /* Now de-duplicate according to user-defined epsilon.
- * We will merge a vertex into an earlier-indexed vertex
- * that is within epsilon (Euclidean distance).
- * Merges may cascade. So we may end up merging two things
- * that are farther than epsilon by transitive merging. Oh well.
- * Assume that merges are rare, so use simple searches in the
- * lexicographic ordering - likely we will soon hit y's with
- * the same x that are farther away than epsilon, and then
- * skipping ahead to the next biggest x, are likely to soon
- * find one of those farther away than epsilon.
- */
- for (i = 0; i < n - 1; i++) {
- ico = sites[i].v->co;
- /* Start j at next place that has both x and y coords within epsilon. */
- xend = ico[0] + epsilon;
- yend = ico[1] + epsilon;
- j = i + 1;
- while (j < n) {
- jco = sites[j].v->co;
- if (jco[0] > xend) {
- break; /* No more j's to process. */
- }
- if (jco[1] > yend) {
- /* Get past any string of v's with the same x and too-big y. */
- xcur = jco[0];
- while (++j < n) {
- if (sites[j].v->co[0] > xcur) {
- break;
- }
- }
- BLI_assert(j == n || sites[j].v->co[0] > xcur);
- if (j == n) {
- break;
- }
- jco = sites[j].v->co;
- if (jco[0] > xend || jco[1] > yend) {
- break;
- }
- }
- /* When here, vertex i and j are within epsilon by box test.
- * The Euclidean distance test is stricter, so need to do it too, now.
- */
- BLI_assert(j < n && jco[0] <= xend && jco[1] <= yend);
- if (len_squared_v2v2_db(ico, jco) <= epsilon_squared) {
- sites[j].v->merge_to_index = (sites[i].v->merge_to_index == -1) ?
- sites[i].orig_index :
- sites[i].v->merge_to_index;
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr,
- "merged orig vert %d to %d\n",
- sites[j].orig_index,
- sites[j].v->merge_to_index);
- }
-#endif
- }
- j++;
- }
- }
-
- /* Now add non-dup vertices into triangulation in lexicographic order. */
-
- dc_triangulate(cdt, sites, n);
- MEM_freeN(sites);
-}
-
-/**
- * Use #LinkNode linked list as stack of #SymEdges, allocating from `cdt->listpool` .
- */
-typedef LinkNode *Stack;
-
-BLI_INLINE void push(Stack *stack, SymEdge *se, CDT_state *cdt)
-{
- BLI_linklist_prepend_pool(stack, se, cdt->listpool);
-}
-
-BLI_INLINE SymEdge *pop(Stack *stack, CDT_state *cdt)
-{
- return (SymEdge *)BLI_linklist_pop_pool(stack, cdt->listpool);
-}
-
-BLI_INLINE bool is_empty(Stack *stack)
-{
- return *stack == NULL;
-}
-
-/**
- * Re-triangulates, assuring constrained delaunay condition,
- * the pseudo-polygon that cycles from se.
- * "pseudo" because a vertex may be repeated.
- * See Anglada paper, "An Improved incremental algorithm
- * for constructing restricted Delaunay triangulations".
- */
-static void re_delaunay_triangulate(CDT_state *cdt, SymEdge *se)
-{
- SymEdge *ss, *first, *cse;
- CDTVert *a, *b, *c, *v;
- CDTEdge *ebc, *eca;
- int count;
-#ifdef DEBUG_CDT
- SymEdge *last;
- const int dbg_level = 0;
-#endif
-
- if (se->face == cdt->outer_face || sym(se)->face == cdt->outer_face) {
- return;
- }
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "retriangulate");
- dump_se_cycle(se, "poly ", 1000);
- }
-#endif
- /* 'se' is a diagonal just added, and it is base of area to retriangulate (face on its left) */
- count = 1;
- for (ss = se->next; ss != se; ss = ss->next) {
- count++;
- }
- if (count <= 3) {
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "nothing to do\n");
- }
-#endif
- return;
- }
- /* First and last are the SymEdges whose verts are first and last off of base,
- * continuing from 'se'. */
- first = se->next->next;
- /* We want to make a triangle with 'se' as base and some other c as 3rd vertex. */
- a = se->vert;
- b = se->next->vert;
- c = first->vert;
- cse = first;
-#ifdef DEBUG_CDT
- last = prev(se);
- if (dbg_level > 1) {
- dump_se(first, "first");
- dump_se(last, "last");
- dump_v(a, "a");
- dump_v(b, "b");
- dump_v(c, "c");
- }
-#endif
- for (ss = first->next; ss != se; ss = ss->next) {
- v = ss->vert;
- if (incircle(a->co, b->co, c->co, v->co) > 0.0) {
- c = v;
- cse = ss;
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- dump_v(c, "new c ");
- }
-#endif
- }
- }
- /* Add diagonals necessary to make abc a triangle. */
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "make triangle abc exist where\n");
- dump_v(a, " a");
- dump_v(b, " b");
- dump_v(c, " c");
- }
-#endif
- ebc = NULL;
- eca = NULL;
- if (!exists_edge(b, c)) {
- ebc = add_diagonal(cdt, se->next, cse);
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "added edge ebc\n");
- dump_se(&ebc->symedges[0], " ebc");
- }
-#endif
- }
- if (!exists_edge(c, a)) {
- eca = add_diagonal(cdt, cse, se);
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "added edge eca\n");
- dump_se(&eca->symedges[0], " eca");
- }
-#endif
- }
- /* Now recurse. */
- if (ebc) {
- re_delaunay_triangulate(cdt, &ebc->symedges[1]);
- }
- if (eca) {
- re_delaunay_triangulate(cdt, &eca->symedges[1]);
- }
-}
-
-static double tri_orient(const SymEdge *t)
-{
- return orient2d(t->vert->co, t->next->vert->co, t->next->next->vert->co);
-}
-
-/**
- * The CrossData struct gives defines either an endpoint or an intermediate point
- * in the path we will take to insert an edge constraint.
- * Each such point will either be
- * (a) a vertex or
- * (b) a fraction lambda (0 < lambda < 1) along some #SymEdge.]
- *
- * In general, lambda=0 indicates case a and lambda != 0 indicates case be.
- * The 'in' edge gives the destination attachment point of a diagonal from the previous crossing,
- * and the 'out' edge gives the origin attachment point of a diagonal to the next crossing.
- * But in some cases, 'in' and 'out' are undefined or not needed, and will be NULL.
- *
- * For case (a), 'vert' will be the vertex, and lambda will be 0, and 'in' will be the #SymEdge
- * from 'vert' that has as face the one that you go through to get to this vertex. If you go
- * exactly along an edge then we set 'in' to NULL, since it won't be needed. The first crossing
- * will have 'in' = NULL. We set 'out' to the #SymEdge that has the face we go though to get to the
- * next crossing, or, if the next crossing is a case (a), then it is the edge that goes to that
- * next vertex. 'out' wlll be NULL for the last one.
- *
- * For case (b), vert will be NULL at first, and later filled in with the created split vertex,
- * and 'in' will be the #SymEdge that we go through, and lambda will be between 0 and 1,
- * the fraction from in's vert to in->next's vert to put the split vertex.
- * 'out' is not needed in this case, since the attachment point will be the sym of the first
- * half of the split edge.
- */
-typedef struct CrossData {
- double lambda;
- CDTVert *vert;
- SymEdge *in;
- SymEdge *out;
-} CrossData;
-
-static bool get_next_crossing_from_vert(CDT_state *cdt,
- CrossData *cd,
- CrossData *cd_next,
- const CDTVert *v2);
-
-/**
- * As part of finding crossings, we found a case where the next crossing goes through vert v.
- * If it came from a previous vert in cd, then cd_out is the edge that leads from that to v.
- * Else cd_out can be NULL, because it won't be used.
- * Set *cd_next to indicate this. We can set 'in' but not 'out'. We can set the 'out' of the
- * current cd.
- */
-static void fill_crossdata_for_through_vert(CDTVert *v,
- SymEdge *cd_out,
- CrossData *cd,
- CrossData *cd_next)
-{
- SymEdge *se;
-#ifdef DEBUG_CDT
- int dbg_level = 0;
-#endif
-
- cd_next->lambda = 0.0;
- cd_next->vert = v;
- cd_next->in = NULL;
- cd_next->out = NULL;
- if (cd->lambda == 0.0) {
- cd->out = cd_out;
- }
- else {
- /* One of the edges in the triangle with edge sym(cd->in) contains v. */
- se = sym(cd->in);
- if (se->vert != v) {
- se = se->next;
- if (se->vert != v) {
- se = se->next;
- }
- }
- BLI_assert(se->vert == v);
- cd_next->in = se;
- }
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- dump_cross_data(cd, "cd through vert, cd");
- dump_cross_data(cd_next, "cd_next through vert, cd");
- }
-#endif
-}
-
-/**
- * As part of finding crossings, we found a case where orient tests say that the next crossing
- * is on the #SymEdge t, while intersecting with the ray from \a curco to \a v2.
- * Find the intersection point and fill in the #CrossData for that point.
- * It may turn out that when doing the intersection, we get an answer that says that
- * this case is better handled as through-vertex case instead, so we may do that.
- * In the latter case, we want to avoid a situation where the current crossing is on an edge
- * and the next will be an endpoint of the same edge. When that happens, we "rewrite history"
- * and turn the current crossing into a vert one, and then extend from there.
- *
- * We cannot fill cd_next's 'out' edge yet, in the case that the next one ends up being a vert
- * case. We need to fill in cd's 'out' edge if it was a vert case.
- */
-static void fill_crossdata_for_intersect(CDT_state *cdt,
- const double *curco,
- const CDTVert *v2,
- SymEdge *t,
- CrossData *cd,
- CrossData *cd_next)
-{
- CDTVert *va, *vb, *vc;
- double lambda, mu, len_ab;
- SymEdge *se_vcva, *se_vcvb;
- int isect;
-#ifdef DEBUG_CDT
- int dbg_level = 0;
-#endif
-
- va = t->vert;
- vb = t->next->vert;
- vc = t->next->next->vert;
- se_vcvb = sym(t->next);
- se_vcva = t->next->next;
- BLI_assert(se_vcva->vert == vc && se_vcva->next->vert == va);
- BLI_assert(se_vcvb->vert == vc && se_vcvb->next->vert == vb);
- UNUSED_VARS_NDEBUG(vc);
- isect = isect_seg_seg_v2_lambda_mu_db(va->co, vb->co, curco, v2->co, &lambda, &mu);
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- double co[2];
- fprintf(stderr, "crossdata for intersect gets lambda=%.17g, mu=%.17g\n", lambda, mu);
- fprintf(stderr,
- "isect=%s\n",
- isect == 2 ? "cross" : (isect == 1 ? "exact" : (isect == 0 ? "none" : "colinear")));
- fprintf(stderr,
- "va=v%d=(%g,%g), vb=v%d=(%g,%g), vc=v%d, curco=(%g,%g), v2=(%g,%g)\n",
- va->index,
- F2(va->co),
- vb->index,
- F2(vb->co),
- vc->index,
- F2(curco),
- F2(v2->co));
- dump_se_short(se_vcva, "vcva=");
- dump_se_short(se_vcvb, " vcvb=");
- interp_v2_v2v2_db(co, va->co, vb->co, lambda);
- fprintf(stderr, "\nco=(%.17g,%.17g)\n", F2(co));
- }
-#endif
- switch (isect) {
- case ISECT_LINE_LINE_CROSS:
- len_ab = len_v2v2_db(va->co, vb->co);
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr,
- "len_ab=%g, near a=%g, near b=%g\n",
- len_ab,
- lambda * len_ab,
- (1.0 - lambda) * len_ab);
- }
-#endif
- if (lambda * len_ab <= cdt->epsilon) {
- fill_crossdata_for_through_vert(va, se_vcva, cd, cd_next);
- }
- else if ((1.0 - lambda) * len_ab <= cdt->epsilon) {
- fill_crossdata_for_through_vert(vb, se_vcvb, cd, cd_next);
- }
- else {
- *cd_next = (CrossData){lambda, NULL, t, NULL};
- if (cd->lambda == 0.0) {
- cd->out = se_vcva;
- }
- }
- break;
- case ISECT_LINE_LINE_EXACT:
- if (lambda == 0.0) {
- fill_crossdata_for_through_vert(va, se_vcva, cd, cd_next);
- }
- else if (lambda == 1.0) {
- fill_crossdata_for_through_vert(vb, se_vcvb, cd, cd_next);
- }
- else {
- *cd_next = (CrossData){lambda, NULL, t, NULL};
- if (cd->lambda == 0.0) {
- cd->out = se_vcva;
- }
- }
- break;
- case ISECT_LINE_LINE_NONE:
- /* It should be very near one end or other of segment. */
- if (lambda <= 0.5) {
- fill_crossdata_for_through_vert(va, se_vcva, cd, cd_next);
- }
- else {
- fill_crossdata_for_through_vert(vb, se_vcvb, cd, cd_next);
- }
- break;
- case ISECT_LINE_LINE_COLINEAR:
- if (len_squared_v2v2_db(va->co, v2->co) <= len_squared_v2v2_db(vb->co, v2->co)) {
- fill_crossdata_for_through_vert(va, se_vcva, cd, cd_next);
- }
- else {
- fill_crossdata_for_through_vert(vb, se_vcvb, cd, cd_next);
- }
- break;
- }
-}
-
-/**
- * As part of finding the crossings of a ray to v2, find the next crossing after 'cd', assuming
- * 'cd' represents a crossing that goes through a vertex.
- *
- * We do a rotational scan around cd's vertex, looking for the triangle where the ray from cd->vert
- * to v2 goes between the two arms from cd->vert, or where it goes along one of the edges.
- */
-static bool get_next_crossing_from_vert(CDT_state *cdt,
- CrossData *cd,
- CrossData *cd_next,
- const CDTVert *v2)
-{
- SymEdge *t, *tstart;
- CDTVert *vcur, *va, *vb;
- double orient1, orient2;
- bool ok;
-#ifdef DEBUG_CDT
- int dbg_level = 0;
-
- if (dbg_level > 0) {
- fprintf(stderr, "\nget_next_crossing_from_vert\n");
- dump_v(cd->vert, " cd->vert");
- }
-#endif
-
- t = tstart = cd->vert->symedge;
- vcur = cd->vert;
- ok = false;
- do {
- /*
- * The ray from vcur to v2 has to go either between two successive
- * edges around vcur or exactly along them. This time through the
- * loop, check to see if the ray goes along vcur-va
- * or between vcur-va and vcur-vb, where va is the end of t
- * and vb is the next vertex (on the next rot edge around vcur, but
- * should also be the next vert of triangle starting with vcur-va.
- */
- if (t->face != cdt->outer_face && tri_orient(t) < 0.0) {
- fprintf(stderr, "BAD TRI orientation %g\n", tri_orient(t)); /* TODO: handle this */
-#ifdef DEBUG_CDT
- dump_se_cycle(t, "top of ccw scan loop", 4);
-#endif
- }
- va = t->next->vert;
- vb = t->next->next->vert;
- orient1 = orient2d(t->vert->co, va->co, v2->co);
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "non-final through vert case\n");
- dump_v(va, " va");
- dump_v(vb, " vb");
- fprintf(stderr, "orient1=%g\n", orient1);
- }
-#endif
- if (orient1 == 0.0 && in_line(vcur->co, va->co, v2->co)) {
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "ray goes through va\n");
- }
-#endif
- fill_crossdata_for_through_vert(va, t, cd, cd_next);
- ok = true;
- break;
- }
- if (t->face != cdt->outer_face) {
- orient2 = orient2d(vcur->co, vb->co, v2->co);
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "orient2=%g\n", orient2);
- }
-#endif
- /* Don't handle orient2 == 0.0 case here: next rotation will get it. */
- if (orient1 > 0.0 && orient2 < 0.0) {
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "segment intersects\n");
- }
-#endif
- t = t->next;
- fill_crossdata_for_intersect(cdt, vcur->co, v2, t, cd, cd_next);
- ok = true;
- break;
- }
- }
- } while ((t = t->rot) != tstart);
-#ifdef DEBUG_CDT
- if (!ok) {
- /* Didn't find the exit! Shouldn't happen. */
- fprintf(stderr, "shouldn't happen: can't find next crossing from vert\n");
- }
-#endif
- return ok;
-}
-
-/**
- * As part of finding the crossings of a ray to 'v2', find the next crossing after 'cd', assuming
- * 'cd' represents a crossing that goes through a an edge, not at either end of that edge.
- *
- * We have the triangle 'vb-va-vc', where va and vb are the split edge and 'vc' is the third vertex
- * on that new side of the edge (should be closer to v2). The next crossing should be through 'vc'
- * or intersecting 'vb-vc' or 'va-vc'.
- */
-static void get_next_crossing_from_edge(CDT_state *cdt,
- CrossData *cd,
- CrossData *cd_next,
- const CDTVert *v2)
-{
- double curco[2];
- double orient;
- CDTVert *va, *vb, *vc;
- SymEdge *se_ac;
-#ifdef DEBUG_CDT
- int dbg_level = 0;
-
- if (dbg_level > 0) {
- fprintf(stderr, "\nget_next_crossing_from_edge\n");
- fprintf(stderr, " lambda=%.17g\n", cd->lambda);
- dump_se_short(cd->in, " cd->in");
- }
-#endif
-
- va = cd->in->vert;
- vb = cd->in->next->vert;
- interp_v2_v2v2_db(curco, va->co, vb->co, cd->lambda);
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, " curco=(%.17g,%.17g)\n", F2(curco));
- }
-#endif
- se_ac = sym(cd->in)->next;
- vc = se_ac->next->vert;
- orient = orient2d(curco, v2->co, vc->co);
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "now searching with third vertex ");
- dump_v(vc, "vc");
- fprintf(stderr, "orient2d(cur, v2, vc) = %g\n", orient);
- }
-#endif
- if (orient < 0.0) {
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "curco--v2 intersects vb--vc\n");
- }
-#endif
- fill_crossdata_for_intersect(cdt, curco, v2, se_ac->next, cd, cd_next);
- }
- else if (orient > 0.0) {
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "curco--v2 intersects va--vc\n");
- }
-#endif
- fill_crossdata_for_intersect(cdt, curco, v2, se_ac, cd, cd_next);
- }
- else {
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "orient==0 case, so going through or to vc\n");
- }
-#endif
- *cd_next = (CrossData){0.0, vc, se_ac->next, NULL};
- }
-}
-
-/**
- * Add a constrained edge between v1 and v2 to cdt structure.
- * This may result in a number of #CDTEdges created, due to intersections
- * and partial overlaps with existing cdt vertices and edges.
- * Each created #CDTEdge will have input_id added to its input_ids list.
- *
- * If \a r_edges is not NULL, the #CDTEdges generated or found that go from
- * v1 to v2 are put into that linked list, in order.
- *
- * Assumes that #BLI_constrained_delaunay_get_output has not been called yet.
- */
-static void add_edge_constraint(
- CDT_state *cdt, CDTVert *v1, CDTVert *v2, int input_id, LinkNode **r_edges)
-{
- SymEdge *t, *se, *tstart, *tnext;
- int i, j, n, visit;
- bool ok;
- CrossData *crossings = NULL;
- CrossData *cd, *cd_prev, *cd_next;
- CDTVert *v;
- CDTEdge *edge;
- LinkNodePair edge_list = {NULL, NULL};
- BLI_array_staticdeclare(crossings, 128);
-#ifdef DEBUG_CDT
- int dbg_level = 0;
-
- if (dbg_level > 0) {
- fprintf(stderr, "\nADD_EDGE_CONSTRAINT\n");
- dump_v(v1, " 1");
- dump_v(v2, " 2");
- }
-#endif
-
- if (r_edges) {
- *r_edges = NULL;
- }
-
- /*
- * Handle two special cases first:
- * 1) The two end vertices are the same (can happen because of merging).
- * 2) There is already an edge between v1 and v2.
- */
- if (v1 == v2) {
- return;
- }
- if ((t = find_symedge_between_verts(v1, v2)) != NULL) {
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "segment already there\n");
- }
-#endif
- add_to_input_ids(&t->edge->input_ids, input_id, cdt);
- if (r_edges != NULL) {
- BLI_linklist_append_pool(&edge_list, t->edge, cdt->listpool);
- *r_edges = edge_list.list;
- }
- return;
- }
-
- /*
- * Fill crossings array with CrossData points for intersection path from v1 to v2.
- *
- * At every point, the crossings array has the path so far, except that
- * the .out field of the last element of it may not be known yet -- if that
- * last element is a vertex, then we won't know the output edge until we
- * find the next crossing.
- *
- * To protect against infinite loops, we keep track of which vertices
- * we have visited by setting their visit_index to a new visit epoch.
- *
- * We check a special case first: where the segment is already there in
- * one hop. Saves a bunch of orient2d tests in that common case.
- */
- visit = ++cdt->visit_count;
- BLI_array_grow_one(crossings);
- crossings[0] = (CrossData){0.0, v1, NULL, NULL};
- while (!((n = BLI_array_len(crossings)) > 0 && crossings[n - 1].vert == v2)) {
- BLI_array_grow_one(crossings);
- cd = &crossings[n - 1];
- cd_next = &crossings[n];
- if (crossings[n - 1].lambda == 0.0) {
- ok = get_next_crossing_from_vert(cdt, cd, cd_next, v2);
- }
- else {
- get_next_crossing_from_edge(cdt, cd, cd_next, v2);
- }
- if (!ok || BLI_array_len(crossings) == 100000) {
- /* Shouldn't happen but if does, just bail out. */
-#ifdef DEBUG_CDT
- fprintf(stderr, "FAILURE adding segment, bailing out\n");
-#endif
- BLI_array_free(crossings);
- return;
- }
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "crossings[%d]: ", n);
- dump_cross_data(&crossings[n], "");
- }
-#endif
- if (crossings[n].lambda == 0.0) {
- if (crossings[n].vert->visit_index == visit) {
- fprintf(stderr, "WHOOPS, REVISIT. Bailing out.\n"); /*TODO: desperation search here. */
- BLI_array_free(crossings);
- return;
- }
- crossings[n].vert->visit_index = visit;
- }
- }
-
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "\ncrossings found\n");
- for (i = 0; i < BLI_array_len(crossings); i++) {
- fprintf(stderr, "%d: ", i);
- dump_cross_data(&crossings[i], "");
- if (crossings[i].lambda == 0.0) {
- if (i == 0 || crossings[i - 1].lambda == 0.0) {
- BLI_assert(crossings[i].in == NULL);
- }
- else {
- BLI_assert(crossings[i].in != NULL && crossings[i].in->vert == crossings[i].vert);
- BLI_assert(crossings[i].in->face == sym(crossings[i - 1].in)->face);
- }
- if (i == BLI_array_len(crossings) - 1) {
- BLI_assert(crossings[i].vert == v2);
- BLI_assert(crossings[i].out == NULL);
- }
- else {
- BLI_assert(crossings[i].out->vert == crossings[i].vert);
- if (crossings[i + 1].lambda == 0.0) {
- BLI_assert(crossings[i].out->next->vert == crossings[i + 1].vert);
- }
- else {
- BLI_assert(crossings[i].out->face == crossings[i + 1].in->face);
- }
- }
- }
- else {
- if (i > 0 && crossings[i - 1].lambda == 0.0) {
- BLI_assert(crossings[i].in->face == crossings[i - 1].out->face);
- }
- BLI_assert(crossings[i].out == NULL);
- }
- }
- }
-#endif
-
- /*
- * Postprocess crossings.
- * Some crossings may have an intersection crossing followed
- * by a vertex crossing that is on the same edge that was just
- * intersected. We prefer to go directly from the previous
- * crossing directly to the vertex. This may chain backwards.
- *
- * This loop marks certain crossings as "deleted", by setting
- * their lambdas to -1.0.
- */
- for (i = 2; i < BLI_array_len(crossings); i++) {
- cd = &crossings[i];
- if (cd->lambda == 0.0) {
- v = cd->vert;
- for (j = i - 1; j > 0; j--) {
- cd_prev = &crossings[j];
- if ((cd_prev->lambda == 0.0 && cd_prev->vert != v) ||
- (cd_prev->lambda != 0.0 && cd_prev->in->vert != v && cd_prev->in->next->vert != v)) {
- break;
- }
- cd_prev->lambda = -1.0; /* Mark cd_prev as 'deleted'. */
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "deleted crossing %d\n", j);
- }
-#endif
- }
- if (j < i - 1) {
- /* Some crossings were deleted. Fix the in and out edges across gap. */
- cd_prev = &crossings[j];
- if (cd_prev->lambda == 0.0) {
- se = find_symedge_between_verts(cd_prev->vert, v);
- if (se == NULL) {
-#ifdef DEBUG_CDT
- fprintf(stderr, "FAILURE(a) in delete crossings, bailing out.\n");
-#endif
- BLI_array_free(crossings);
- return;
- }
- cd_prev->out = se;
- cd->in = NULL;
- }
- else {
- se = find_symedge_with_face(v, sym(cd_prev->in)->face);
- if (se == NULL) {
-#ifdef DEBUG_CDT
- fprintf(stderr, "FAILURE(b) in delete crossings, bailing out.\n");
-#endif
- BLI_array_free(crossings);
- return;
- }
- cd->in = se;
- }
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "after deleting crossings:\n");
- fprintf(stderr, "cross[%d]: ", j);
- dump_cross_data(cd_prev, "");
- fprintf(stderr, "cross[%d]: ", i);
- dump_cross_data(cd, "");
- }
-#endif
- }
- }
- }
-
- /*
- * Insert all intersection points on constrained edges.
- */
- for (i = 0; i < BLI_array_len(crossings); i++) {
- cd = &crossings[i];
- if (cd->lambda != 0.0 && cd->lambda != -1.0 && is_constrained_edge(cd->in->edge)) {
- edge = split_edge(cdt, cd->in, cd->lambda);
- cd->vert = edge->symedges[0].vert;
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "insert vert for crossing %d: ", i);
- dump_v(cd->vert, "inserted");
- }
-#endif
- }
- }
-
- /*
- * Remove any crossed, non-intersected edges.
- */
- for (i = 0; i < BLI_array_len(crossings); i++) {
- cd = &crossings[i];
- if (cd->lambda != 0.0 && cd->lambda != -1.0 && !is_constrained_edge(cd->in->edge)) {
- delete_edge(cdt, cd->in);
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "delete edge for crossing %d\n", i);
- }
-#endif
- }
- }
-
- /*
- * Insert segments for v1->v2.
- */
- tstart = crossings[0].out;
- for (i = 1; i < BLI_array_len(crossings); i++) {
- cd = &crossings[i];
- if (cd->lambda == -1.0) {
- continue; /* This crossing was deleted. */
- }
- t = tnext = NULL;
- if (cd->lambda != 0.0) {
- if (is_constrained_edge(cd->in->edge)) {
- t = cd->vert->symedge;
- tnext = sym(t)->next;
- }
- }
- else if (cd->lambda == 0.0) {
- t = cd->in;
- tnext = cd->out;
- if (t == NULL) {
- /* Previous non-deleted crossing must also have been a vert, and segment should exist. */
- for (j = i - 1; j >= 0; j--) {
- cd_prev = &crossings[j];
- if (cd_prev->lambda != -1.0) {
- break;
- }
- }
- BLI_assert(cd_prev->lambda == 0.0);
- BLI_assert(cd_prev->out->next->vert == cd->vert);
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "edge to crossing %d already there\n", i);
- }
-#endif
- edge = cd_prev->out->edge;
- add_to_input_ids(&edge->input_ids, input_id, cdt);
- }
- }
- if (t != NULL) {
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "edge to crossing %d: insert diagonal between\n", i);
- dump_se(tstart, " ");
- dump_se(t, " ");
- dump_se_cycle(tstart, "tstart", 100);
- dump_se_cycle(t, "t", 100);
- }
-#endif
- if (tstart->next->vert == t->vert) {
- edge = tstart->edge;
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "already there (b)\n");
- }
-#endif
- }
- else {
- edge = add_diagonal(cdt, tstart, t);
- }
- add_to_input_ids(&edge->input_ids, input_id, cdt);
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "added\n");
- }
-#endif
- if (r_edges != NULL) {
- BLI_linklist_append_pool(&edge_list, edge, cdt->listpool);
- }
- /* Now retriangulate upper and lower gaps. */
- re_delaunay_triangulate(cdt, &edge->symedges[0]);
- re_delaunay_triangulate(cdt, &edge->symedges[1]);
- }
- if (i < BLI_array_len(crossings) - 1) {
- if (tnext != NULL) {
- tstart = tnext;
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "now tstart = ");
- dump_se(tstart, "");
- }
-#endif
- }
- }
- }
-
- if (r_edges) {
- *r_edges = edge_list.list;
- }
- BLI_array_free(crossings);
-}
-
-/**
- * Add face_id to the input_ids lists of all #CDTFace's on the interior of the input face with that
- * id. face_symedge is on edge of the boundary of the input face, with assumption that interior is
- * on the left of that SymEdge.
- *
- * The algorithm is: starting from the #CDTFace for face_symedge, add the face_id and then
- * process all adjacent faces where the adjacency isn't across an edge that was a constraint added
- * for the boundary of the input face.
- * fedge_start..fedge_end is the inclusive range of edge input ids that are for the given face.
- *
- * Note: if the input face is not CCW oriented, we'll be labeling the outside, not the inside.
- * Note 2: if the boundary has self-crossings, this method will arbitrarily pick one of the
- * contiguous set of faces enclosed by parts of the boundary, leaving the other such untagged. This
- * may be a feature instead of a bug if the first contiguous section is most of the face and the
- * others are tiny self-crossing triangles at some parts of the boundary. On the other hand, if
- * decide we want to handle these in full generality, then will need a more complicated algorithm
- * (using "inside" tests and a parity rule) to decide on the interior.
- */
-static void add_face_ids(
- CDT_state *cdt, SymEdge *face_symedge, int face_id, int fedge_start, int fedge_end)
-{
- Stack stack;
- SymEdge *se, *se_start, *se_sym;
- CDTFace *face, *face_other;
- int visit;
-
- /* Can't loop forever since eventually would visit every face. */
- cdt->visit_count++;
- visit = cdt->visit_count;
- stack = NULL;
- push(&stack, face_symedge, cdt);
- while (!is_empty(&stack)) {
- se = pop(&stack, cdt);
- face = se->face;
- if (face->visit_index == visit) {
- continue;
- }
- face->visit_index = visit;
- add_to_input_ids(&face->input_ids, face_id, cdt);
- se_start = se;
- for (se = se->next; se != se_start; se = se->next) {
- if (!id_range_in_list(se->edge->input_ids, fedge_start, fedge_end)) {
- se_sym = sym(se);
- face_other = se_sym->face;
- if (face_other->visit_index != visit) {
- push(&stack, se_sym, cdt);
- }
- }
- }
- }
-}
-
-/* Delete_edge but try not to mess up outer face.
- * Also faces have symedges now, so make sure not
- * to mess those up either. */
-static void dissolve_symedge(CDT_state *cdt, SymEdge *se)
-{
- SymEdge *symse = sym(se);
- if (symse->face == cdt->outer_face) {
- se = sym(se);
- symse = sym(se);
- }
- if (cdt->outer_face->symedge == se || cdt->outer_face->symedge == symse) {
- /* Advancing by 2 to get past possible 'sym(se)'. */
- if (se->next->next == se) {
- cdt->outer_face->symedge = NULL;
- }
- else {
- cdt->outer_face->symedge = se->next->next;
- }
- }
- else {
- if (se->face->symedge == se) {
- se->face->symedge = se->next;
- }
- if (symse->face->symedge == symse) {
- symse->face->symedge = symse->next;
- }
- }
- delete_edge(cdt, se);
-}
-
-/* Slow way to get face and start vertex index within face for edge id e. */
-static bool get_face_edge_id_indices(const CDT_input *in, int e, int *r_f, int *r_fi)
-{
- int f;
- int id;
-
- id = in->edges_len;
- if (e < id) {
- return false;
- }
- for (f = 0; f < in->faces_len; f++) {
- if (e < id + in->faces_len_table[f]) {
- *r_f = f;
- *r_fi = e - id;
- return true;
- }
- id += in->faces_len_table[f];
- }
- return false;
-}
-
-/* Is pt_co when snapped to segment seg1 seg2 all of:
- * a) strictly within that segment
- * b) within epsilon from original pt_co
- * c) pt_co is not within epsilon of either seg1 or seg2.
- * Return true if so, and return in *r_lambda the fraction of the way from seg1 to seg2 of the
- * snapped point.
- */
-static bool check_vert_near_segment(const double *pt_co,
- const double *seg1,
- const double *seg2,
- double epsilon_squared,
- double *r_lambda)
-{
- double lambda, snap_co[2];
-
- lambda = closest_to_line_v2_db(snap_co, pt_co, seg1, seg2);
- *r_lambda = lambda;
- if (lambda <= 0.0 || lambda >= 1.0) {
- return false;
- }
- if (len_squared_v2v2_db(pt_co, snap_co) > epsilon_squared) {
- return false;
- }
- if (len_squared_v2v2_db(pt_co, seg1) <= epsilon_squared ||
- len_squared_v2v2_db(pt_co, seg2) <= epsilon_squared) {
- return false;
- }
- return true;
-}
-
-typedef struct EdgeVertLambda {
- int e_id;
- int v_id;
- double lambda;
-} EdgeVertLambda;
-
-/* For sorting first by edge id, then by lambda, then by vert id. */
-static int evl_cmp(const void *a, const void *b)
-{
- const EdgeVertLambda *area = a;
- const EdgeVertLambda *sb = b;
-
- if (area->e_id < sb->e_id) {
- return -1;
- }
- if (area->e_id > sb->e_id) {
- return 1;
- }
- if (area->lambda < sb->lambda) {
- return -1;
- }
- if (area->lambda > sb->lambda) {
- return 1;
- }
- if (area->v_id < sb->v_id) {
- return -1;
- }
- if (area->v_id > sb->v_id) {
- return 1;
- }
- return 0;
-}
-
-/**
- * If epsilon > 0, and input doesn't have skip_modify_input == true,
- * check input to see if any constraint edge ends (including face edges) come
- * within epsilon of another edge.
- * For all such cases, we want to split the constraint edge at the point nearest to near vertex
- * and move the vertex coordinates to be on that edge.
- * But exclude cases where they come within epsilon of either end because those will be handled
- * by vertex merging in the main triangulation algorithm.
- *
- * If any such splits are found, make a new CDT_input reflecting this change, and provide an
- * edge map to map from edge ids in the new input space to edge ids in the old input space.
- *
- * TODO: replace naive O(n^2) algorithm with kdopbvh-based one.
- */
-static const CDT_input *modify_input_for_near_edge_ends(const CDT_input *input, int **r_edge_map)
-{
- CDT_input *new_input = NULL;
- int e, eprev, e1, e2, f, fi, flen, start, i, j;
- int i_new, i_old, i_evl;
- int v11, v12, v21, v22;
- double co11[2], co12[2], co21[2], co22[2];
- double lambda;
- double eps = (double)input->epsilon;
- double eps_sq = eps * eps;
- int tot_edge_constraints, edges_len, tot_face_edges;
- int new_tot_face_edges, new_tot_con_edges;
- int delta_con_edges, delta_face_edges, cur_e_cnt;
- int *edge_map;
- int evl_len;
- EdgeVertLambda *edge_vert_lambda = NULL;
- BLI_array_staticdeclare(edge_vert_lambda, 128);
-#ifdef DEBUG_CDT
- EdgeVertLambda *evl;
- int dbg_level = 0;
-
- if (dbg_level > 0) {
- fprintf(stderr, "\nMODIFY INPUT\n\n");
- }
-#endif
-
- *r_edge_map = NULL;
- if (input->epsilon == 0.0 || input->skip_input_modify ||
- (input->edges_len == 0 && input->faces_len == 0)) {
- return input;
- }
-
- /* Edge constraints are union of the explicitly provided edges and the implicit edges
- * that are part of the provided faces. We index constraints by have the first input->edges_len
- * ints standing for the explicit edge with the same index, and the rest being face edges in
- * the order that the faces appear and then edges within those faces, with indices offset by
- * input->edges_len.
- * Calculate tot_edge_constraints to be the sum of the two kinds of edges.
- * We first have to count the number of face edges.
- * That is the same as the number of vertices in the faces table, which
- * we can find by adding the last length to the last start.
- */
- edges_len = input->edges_len;
- tot_edge_constraints = edges_len;
- if (input->faces_len > 0) {
- tot_face_edges = input->faces_start_table[input->faces_len - 1] +
- input->faces_len_table[input->faces_len - 1];
- }
- else {
- tot_face_edges = 0;
- }
- tot_edge_constraints = edges_len + tot_face_edges;
-
- for (e1 = 0; e1 < tot_edge_constraints - 1; e1++) {
- if (e1 < edges_len) {
- v11 = input->edges[e1][0];
- v12 = input->edges[e1][1];
- }
- else {
- if (!get_face_edge_id_indices(input, e1, &f, &fi)) {
- /* Must be bad input. Will be caught later so don't need to signal here. */
- continue;
- }
- start = input->faces_start_table[f];
- flen = input->faces_len_table[f];
- v11 = input->faces[start + fi];
- v12 = input->faces[(fi == flen - 1) ? start : start + fi + 1];
- }
- for (e2 = e1 + 1; e2 < tot_edge_constraints; e2++) {
- if (e2 < edges_len) {
- v21 = input->edges[e2][0];
- v22 = input->edges[e2][1];
- }
- else {
- if (!get_face_edge_id_indices(input, e2, &f, &fi)) {
- continue;
- }
- start = input->faces_start_table[f];
- flen = input->faces_len_table[f];
- v21 = input->faces[start + fi];
- v22 = input->faces[(fi == flen - 1) ? start : start + fi + 1];
- }
- copy_v2db_v2fl(co11, input->vert_coords[v11]);
- copy_v2db_v2fl(co12, input->vert_coords[v12]);
- copy_v2db_v2fl(co21, input->vert_coords[v21]);
- copy_v2db_v2fl(co22, input->vert_coords[v22]);
- if (check_vert_near_segment(co11, co21, co22, eps_sq, &lambda)) {
-
- BLI_array_append(edge_vert_lambda, ((EdgeVertLambda){e2, v11, lambda}));
- }
- if (check_vert_near_segment(co12, co21, co22, eps_sq, &lambda)) {
- BLI_array_append(edge_vert_lambda, ((EdgeVertLambda){e2, v12, lambda}));
- }
- if (check_vert_near_segment(co21, co11, co12, eps_sq, &lambda)) {
- BLI_array_append(edge_vert_lambda, ((EdgeVertLambda){e1, v21, lambda}));
- }
- if (check_vert_near_segment(co22, co11, co12, eps_sq, &lambda)) {
- BLI_array_append(edge_vert_lambda, ((EdgeVertLambda){e1, v22, lambda}));
- }
- }
- }
-
- evl_len = BLI_array_len(edge_vert_lambda);
- if (evl_len > 0) {
- /* Sort to bring splits for each edge together,
- * and for each edge, to be in order of lambda. */
- qsort(edge_vert_lambda, evl_len, sizeof(EdgeVertLambda), evl_cmp);
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "\nafter sorting\n");
- for (i = 0; i < evl_len; i++) {
- evl = &edge_vert_lambda[i];
- fprintf(stderr, "e%d, v%d, %g\n", evl->e_id, evl->v_id, evl->lambda);
- }
- }
-#endif
-
- /* Remove dups in edge_vert_lambda, where dup means that the edge is the
- * same, and the verts are either the same or will be merged by epsilon-nearness.
- */
- i = 0;
- j = 0;
- /* In loop, copy from position j to position i. */
- for (j = 0; j < evl_len;) {
- int k;
- if (i != j) {
- memmove(&edge_vert_lambda[i], &edge_vert_lambda[j], sizeof(EdgeVertLambda));
- }
- for (k = j + 1; k < evl_len; k++) {
- int vj = edge_vert_lambda[j].v_id;
- int vk = edge_vert_lambda[k].v_id;
- if (vj != vk) {
- if (len_squared_v2v2(input->vert_coords[vj], input->vert_coords[vk]) > (float)eps_sq) {
- break;
- }
- }
- }
- j = k;
- i++;
- }
-
- if (i != evl_len) {
- evl_len = i;
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "\nduplicates eliminated\n");
- for (i = 0; i < evl_len; i++) {
- evl = &edge_vert_lambda[i];
- fprintf(stderr, "e%d, v%d, %g\n", evl->e_id, evl->v_id, evl->lambda);
- }
- }
-#endif
- }
- /* Find delta in number of constraint edges and face edges.
- * This may be overestimates of true number, due to duplicates. */
- delta_con_edges = 0;
- delta_face_edges = 0;
- cur_e_cnt = 0;
- eprev = -1;
- for (i = 0; i < evl_len; i++) {
- e = edge_vert_lambda[i].e_id;
- if (i > 0 && e > eprev) {
- /* New edge group. Previous group had cur_e_cnt split vertices.
- * That is the delta in the number of edges needed in input since
- * there will be cur_e_cnt + 1 edges replacing one edge.
- */
- if (eprev < edges_len) {
- delta_con_edges += cur_e_cnt;
- }
- else {
- delta_face_edges += cur_e_cnt;
- }
- cur_e_cnt = 1;
- ;
- }
- else {
- cur_e_cnt++;
- }
- eprev = e;
- }
- if (eprev < edges_len) {
- delta_con_edges += cur_e_cnt;
- }
- else {
- delta_face_edges += cur_e_cnt;
- }
- new_tot_con_edges = input->edges_len + delta_con_edges;
- if (input->faces_len > 0) {
- new_tot_face_edges = input->faces_start_table[input->faces_len - 1] +
- input->faces_len_table[input->faces_len - 1] + delta_face_edges;
- }
- else {
- new_tot_face_edges = 0;
- }
-
- /* Allocate new CDT_input, now we know sizes needed (perhaps overestimated a bit).
- * Caller will be responsible for freeing it and its arrays.
- */
- new_input = MEM_callocN(sizeof(CDT_input), __func__);
- new_input->epsilon = input->epsilon;
- new_input->verts_len = input->verts_len;
- new_input->vert_coords = (float(*)[2])MEM_malloc_arrayN(
- new_input->verts_len, sizeof(float[2]), __func__);
- /* We don't do it now, but may decide to change coords of snapped verts. */
- memmove(new_input->vert_coords,
- input->vert_coords,
- sizeof(float[2]) * (size_t)new_input->verts_len);
-
- if (edges_len > 0) {
- new_input->edges_len = new_tot_con_edges;
- new_input->edges = (int(*)[2])MEM_malloc_arrayN(new_tot_con_edges, sizeof(int[2]), __func__);
- }
-
- if (input->faces_len > 0) {
- new_input->faces_len = input->faces_len;
- new_input->faces_start_table = (int *)MEM_malloc_arrayN(
- new_input->faces_len, sizeof(int), __func__);
- new_input->faces_len_table = (int *)MEM_malloc_arrayN(
- new_input->faces_len, sizeof(int), __func__);
- new_input->faces = (int *)MEM_malloc_arrayN(new_tot_face_edges, sizeof(int), __func__);
- }
-
- edge_map = (int *)MEM_malloc_arrayN(
- new_tot_con_edges + new_tot_face_edges, sizeof(int), __func__);
- *r_edge_map = edge_map;
-
- i_new = i_old = i_evl = 0;
- e = edge_vert_lambda[0].e_id;
- /* First do new constraint edges. */
- for (i_old = 0; i_old < edges_len; i_old++) {
- if (i_old < e) {
- /* Edge for i_old not split; copy it into new_input. */
- new_input->edges[i_new][0] = input->edges[i_old][0];
- new_input->edges[i_new][1] = input->edges[i_old][1];
- edge_map[i_new] = i_old;
- i_new++;
- }
- else {
- /* Edge for i_old is split. */
- BLI_assert(i_old == e);
- new_input->edges[i_new][0] = input->edges[i_old][0];
- new_input->edges[i_new][1] = edge_vert_lambda[i_evl].v_id;
- edge_map[i_new] = i_old;
- i_new++;
- i_evl++;
- while (i_evl < evl_len && e == edge_vert_lambda[i_evl].e_id) {
- new_input->edges[i_new][0] = new_input->edges[i_new - 1][1];
- new_input->edges[i_new][1] = edge_vert_lambda[i_evl].v_id;
- edge_map[i_new] = i_old;
- i_new++;
- i_evl++;
- }
- new_input->edges[i_new][0] = new_input->edges[i_new - 1][1];
- new_input->edges[i_new][1] = input->edges[i_old][1];
- edge_map[i_new] = i_old;
- i_new++;
- if (i_evl < evl_len) {
- e = edge_vert_lambda[i_evl].e_id;
- }
- else {
- e = INT_MAX;
- }
- }
- }
- BLI_assert(i_new <= new_tot_con_edges);
- new_input->edges_len = i_new;
-
- /* Now do face constraints. */
- if (input->faces_len > 0) {
- f = 0;
- i_new = 0; /* Now will index cur place in new_input->faces. */
- while (i_old < tot_edge_constraints) {
- flen = input->faces_len_table[f];
- BLI_assert(i_old - edges_len == input->faces_start_table[f]);
- new_input->faces_start_table[f] = i_new;
- if (i_old + flen - 1 < e) {
- /* Face f is not split. */
- for (j = 0; j < flen; j++) {
- new_input->faces[i_new] = input->faces[i_old - edges_len + j];
- edge_map[i_new + new_input->edges_len] = i_old + j;
- i_new++;
- }
- i_old += flen;
- new_input->faces_len_table[f] = flen;
- f++;
- }
- else {
- /* Face f has at least one split edge. */
- int i_new_start = i_new;
- for (j = 0; j < flen; j++) {
- if (i_old + j < e) {
- /* jth edge of f is not split. */
- new_input->faces[i_new] = input->faces[i_old - edges_len + j];
- edge_map[i_new + new_input->edges_len] = i_old + j;
- i_new++;
- }
- else {
- /* jth edge of f is split. */
- BLI_assert(i_old + j == e);
- new_input->faces[i_new] = input->faces[i_old - edges_len + j];
- edge_map[i_new + new_input->edges_len] = i_old + j;
- i_new++;
- while (i_evl < evl_len && e == edge_vert_lambda[i_evl].e_id) {
- new_input->faces[i_new] = edge_vert_lambda[i_evl].v_id;
- edge_map[i_new + new_input->edges_len] = i_old + j;
- i_new++;
- i_evl++;
- }
- if (i_evl < evl_len) {
- e = edge_vert_lambda[i_evl].e_id;
- }
- else {
- e = INT_MAX;
- }
- }
- }
- new_input->faces_len_table[f] = i_new - i_new_start;
- i_old += flen;
- f++;
- }
- }
- }
-
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "\nnew constraint edges\n");
- for (i = 0; i < new_input->edges_len; i++) {
- fprintf(stderr, " e%d: (%d,%d)\n", i, new_input->edges[i][0], new_input->edges[i][1]);
- }
- fprintf(stderr, "\nnew faces\n");
- for (f = 0; f < new_input->faces_len; f++) {
- flen = new_input->faces_len_table[f];
- start = new_input->faces_start_table[f];
- fprintf(stderr, " f%d: start=%d, len=%d\n ", f, start, flen);
- for (i = start; i < start + flen; i++) {
- fprintf(stderr, "%d ", new_input->faces[i]);
- }
- fprintf(stderr, "\n");
- }
- fprintf(stderr, "\nedge map (new->old)\n");
- for (i = 0; i < new_tot_con_edges + new_tot_face_edges; i++) {
- fprintf(stderr, " %d->%d\n", i, edge_map[i]);
- }
- }
-#endif
- }
-
- BLI_array_free(edge_vert_lambda);
- if (new_input != NULL) {
- return (const CDT_input *)new_input;
- }
- return input;
-}
-
-static void free_modified_input(CDT_input *input)
-{
- MEM_freeN(input->vert_coords);
- if (input->edges != NULL) {
- MEM_freeN(input->edges);
- }
- if (input->faces != NULL) {
- MEM_freeN(input->faces);
- MEM_freeN(input->faces_len_table);
- MEM_freeN(input->faces_start_table);
- }
- MEM_freeN(input);
-}
-
-/* Return true if we can merge se's vert into se->next's vert
- * without making the area of any new triangle formed by doing
- * that into a zero or negative area triangle.*/
-static bool can_collapse(const SymEdge *se)
-{
- SymEdge *loop_se;
- const double *co = se->next->vert->co;
-
- for (loop_se = se->rot; loop_se != se && loop_se->rot != se; loop_se = loop_se->rot) {
- if (orient2d(co, loop_se->next->vert->co, loop_se->rot->next->vert->co) <= 0.0) {
- return false;
- }
- }
- return true;
-}
-
-/*
- * Merge one end of e onto the other, fixing up surrounding faces.
- *
- * General situation looks something like:
- *
- * c-----e
- * / \ / \
- * / \ / \
- * a------b-----f
- * \ / \ /
- * \ / \ /
- * d-----g
- *
- * where ab is the tiny edge. We want to merge a and b and delete edge ab.
- * We don't want to change the coordinates of input vertices [We could revisit this
- * in the future, as API def doesn't prohibit this, but callers will appreciate if they
- * don't change.]
- * Sometimes the collapse shouldn't happen because the triangles formed by the changed
- * edges may end up with zero or negative area (see can_collapse, above).
- * So don't choose a collapse direction that is not allowed or one that has an original vertex
- * as origin and a non-original vertex as destination.
- * If both collapse directions are allowed by that rule, pick the one with the lower original
- * index.
- *
- * After merging, the faces abc and adb disappear (if they are not the outer face).
- * Suppose we merge b onto a.
- * Then edges cb and db are deleted. Face cbe becomes cae and face bdg becomes adg.
- * Any other faces attached to b now have a in their place.
- * We can do this by rotating edges round b, replacing their vert references with a.
- * Similar statements can be made about what happens when a merges into b;
- * in code below we'll swap a and b to make above lettering work for a b->a merge.
- * Return the vert at the collapsed edge, if a collapse happens.
- */
-static CDTVert *collapse_tiny_edge(CDT_state *cdt, CDTEdge *e)
-{
- CDTVert *va, *vb;
- SymEdge *ab_se, *ba_se, *bd_se, *bc_se, *ad_se, *ac_se;
- SymEdge *bg_se, *be_se, *se, *gb_se, *ca_se;
- bool can_collapse_a_to_b, can_collapse_b_to_a;
-#ifdef DEBUG_CDT
- int dbg_level = 0;
-#endif
-
- ab_se = &e->symedges[0];
- ba_se = &e->symedges[1];
- va = ab_se->vert;
- vb = ba_se->vert;
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "\ncollapse_tiny_edge\n");
- dump_se(&e->symedges[0], "tiny edge");
- fprintf(stderr, "a = [%d], b = [%d]\n", va->index, vb->index);
- validate_cdt(cdt, true, false, true);
- }
-#endif
- can_collapse_a_to_b = can_collapse(ab_se);
- can_collapse_b_to_a = can_collapse(ba_se);
- /* Now swap a and b if necessary and possible, so that from this point on we are collapsing b to
- * a. */
- if (va->index > vb->index || !can_collapse_b_to_a) {
- if (can_collapse_a_to_b && !(is_original_vert(va, cdt) && !is_original_vert(vb, cdt))) {
- SWAP(CDTVert *, va, vb);
- ab_se = &e->symedges[1];
- ba_se = &e->symedges[0];
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "swapped a and b\n");
- }
-#endif
- }
- else {
- /* Neither collapse direction is OK. */
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "neither collapse direction ok\n");
- }
-#endif
- return NULL;
- }
- }
- bc_se = ab_se->next;
- bd_se = ba_se->rot;
- if (bd_se == ba_se) {
- /* Shouldn't happen. Wire edge in outer face. */
- fprintf(stderr, "unexpected wire edge\n");
- return NULL;
- }
- vb->merge_to_index = va->merge_to_index == -1 ? va->index : va->merge_to_index;
- vb->symedge = NULL;
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr,
- "vb = v[%d] merges to va = v[%d], vb->merge_to_index=%d\n",
- vb->index,
- va->index,
- vb->merge_to_index);
- }
-#endif
- /* First fix the vertex of intermediate triangles, like bgf. */
- for (se = bd_se->rot; se != bc_se; se = se->rot) {
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- dump_se(se, "intermediate tri edge, setting vert to va");
- }
-#endif
- se->vert = va;
- }
- ad_se = sym(sym(bd_se)->rot);
- ca_se = bc_se->next;
- ac_se = sym(ca_se);
- if (bd_se->rot != bc_se) {
- bg_se = bd_se->rot;
- be_se = sym(bc_se)->next;
- gb_se = sym(bg_se);
- }
- else {
- bg_se = NULL;
- be_se = NULL;
- }
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "delete bd, inputs to ad\n");
- dump_se(bd_se, " bd");
- dump_se(ad_se, " ad");
- fprintf(stderr, "delete bc, inputs to ac\n");
- dump_se(bc_se, " bc");
- dump_se(ac_se, " ac");
- fprintf(stderr, "delete ab\n");
- dump_se(ab_se, " ab");
- if (bg_se != NULL) {
- fprintf(stderr, "fix up bg, be\n");
- dump_se(bg_se, " bg");
- dump_se(be_se, " be");
- }
- }
-#endif
- add_list_to_input_ids(&ad_se->edge->input_ids, bd_se->edge->input_ids, cdt);
- delete_edge(cdt, bd_se);
- add_list_to_input_ids(&ac_se->edge->input_ids, bc_se->edge->input_ids, cdt);
- delete_edge(cdt, sym(bc_se));
- /* At this point we have this:
- *
- * c-----e
- * / / \
- * / / \
- * a------b-----f
- * \ \ /
- * \ \ /
- * d-----g
- *
- * Or, if there is not bg_se and be_se, like this:
- *
- * c
- * /
- * /
- * a------b
- * \
- * \
- * d
- *
- * (But we've already changed the vert field for bg, bf, ..., be to be va.)
- */
- if (bg_se != NULL) {
- gb_se->next = ad_se;
- ad_se->rot = bg_se;
- ca_se->next = be_se;
- be_se->rot = ac_se;
- bg_se->vert = va;
- be_se->vert = va;
- }
- else {
- ca_se->next = ad_se;
- ad_se->rot = ac_se;
- }
- /* Don't use delete_edge as it changes too much. */
- ab_se->next = ab_se->rot = NULL;
- ba_se->next = ba_se->rot = NULL;
- if (va->symedge == ab_se) {
- va->symedge = ac_se;
- }
- return va;
-}
-
-/*
- * Check to see if e is tiny (length <= epsilon) and queue it if so.
- */
-static void maybe_enqueue_small_feature(CDT_state *cdt, CDTEdge *e, LinkNodePair *tiny_edge_queue)
-{
- SymEdge *se, *sesym;
-#ifdef DEBUG_CDT
- int dbg_level = 0;
-
- if (dbg_level > 0) {
- fprintf(stderr, "\nmaybe_enqueue_small_features\n");
- dump_se(&e->symedges[0], " se0");
- }
-#endif
-
- if (is_deleted_edge(e) || e->in_queue) {
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "returning because of e conditions\n");
- }
-#endif
- return;
- }
- se = &e->symedges[0];
- sesym = &e->symedges[1];
- if (len_squared_v2v2_db(se->vert->co, sesym->vert->co) <= cdt->epsilon_squared) {
- BLI_linklist_append_pool(tiny_edge_queue, e, cdt->listpool);
- e->in_queue = true;
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "Queue tiny edge\n");
- }
-#endif
- }
-}
-
-/* Consider all edges in rot ring around v for possible enqueing as small features .*/
-static void maybe_enqueue_small_features(CDT_state *cdt, CDTVert *v, LinkNodePair *tiny_edge_queue)
-{
- SymEdge *se, *se_start;
-
- se = se_start = v->symedge;
- if (!se_start) {
- return;
- }
- do {
- maybe_enqueue_small_feature(cdt, se->edge, tiny_edge_queue);
- } while ((se = se->rot) != se_start);
-}
-
-/* Collapse small edges (length <= epsilon) until no more exist.
- */
-static void remove_small_features(CDT_state *cdt)
-{
- double epsilon = cdt->epsilon;
- LinkNodePair tiny_edge_queue = {NULL, NULL};
- BLI_mempool *pool = cdt->listpool;
- LinkNode *ln;
- CDTEdge *e;
- CDTVert *v_change;
-#ifdef DEBUG_CDT
- int dbg_level = 0;
-
- if (dbg_level > 0) {
- fprintf(stderr, "\nREMOVE_SMALL_FEATURES, epsilon=%g\n", epsilon);
- }
-#endif
-
- if (epsilon == 0.0) {
- return;
- }
-
- for (ln = cdt->edges; ln; ln = ln->next) {
- e = (CDTEdge *)ln->link;
- maybe_enqueue_small_feature(cdt, e, &tiny_edge_queue);
- }
-
- while (tiny_edge_queue.list != NULL) {
- e = (CDTEdge *)BLI_linklist_pop_pool(&tiny_edge_queue.list, pool);
- if (tiny_edge_queue.list == NULL) {
- tiny_edge_queue.last_node = NULL;
- }
- e->in_queue = false;
- if (is_deleted_edge(e)) {
- continue;
- }
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "collapse tiny edge\n");
- dump_se(&e->symedges[0], "");
- }
-#endif
- v_change = collapse_tiny_edge(cdt, e);
- if (v_change) {
- maybe_enqueue_small_features(cdt, v_change, &tiny_edge_queue);
- }
- }
-}
-
-/* Remove all non-constraint edges. */
-static void remove_non_constraint_edges(CDT_state *cdt)
-{
- LinkNode *ln;
- CDTEdge *e;
- SymEdge *se;
-
- for (ln = cdt->edges; ln; ln = ln->next) {
- e = (CDTEdge *)ln->link;
- se = &e->symedges[0];
- if (!is_deleted_edge(e) && !is_constrained_edge(e)) {
- dissolve_symedge(cdt, se);
- }
- }
-}
-
-/*
- * Remove the non-constraint edges, but leave enough of them so that all of the
- * faces that would be bmesh faces (that is, the faces that have some input representative)
- * are valid: they can't have holes, they can't have repeated vertices, and they can't have
- * repeated edges.
- *
- * Not essential, but to make the result look more aesthetically nice,
- * remove the edges in order of decreasing length, so that it is more likely that the
- * final remaining support edges are short, and therefore likely to make a fairly
- * direct path from an outer face to an inner hole face.
- */
-
-/* For sorting edges by decreasing length (squared). */
-struct EdgeToSort {
- double len_squared;
- CDTEdge *e;
-};
-
-static int edge_to_sort_cmp(const void *a, const void *b)
-{
- const struct EdgeToSort *e1 = a;
- const struct EdgeToSort *e2 = b;
-
- if (e1->len_squared > e2->len_squared) {
- return -1;
- }
- if (e1->len_squared < e2->len_squared) {
- return 1;
- }
- return 0;
-}
-
-static void remove_non_constraint_edges_leave_valid_bmesh(CDT_state *cdt)
-{
- LinkNode *ln;
- CDTEdge *e;
- SymEdge *se, *se2;
- CDTFace *fleft, *fright;
- bool dissolve;
- size_t nedges;
- int i, ndissolvable;
- const double *co1, *co2;
- struct EdgeToSort *sorted_edges;
-
- nedges = 0;
- for (ln = cdt->edges; ln; ln = ln->next) {
- nedges++;
- }
- if (nedges == 0) {
- return;
- }
- sorted_edges = BLI_memarena_alloc(cdt->arena, nedges * sizeof(*sorted_edges));
- i = 0;
- for (ln = cdt->edges; ln; ln = ln->next) {
- e = (CDTEdge *)ln->link;
- if (!is_deleted_edge(e) && !is_constrained_edge(e)) {
- sorted_edges[i].e = e;
- co1 = e->symedges[0].vert->co;
- co2 = e->symedges[1].vert->co;
- sorted_edges[i].len_squared = len_squared_v2v2_db(co1, co2);
- i++;
- }
- }
- ndissolvable = i;
- qsort(sorted_edges, ndissolvable, sizeof(*sorted_edges), edge_to_sort_cmp);
- for (i = 0; i < ndissolvable; i++) {
- e = sorted_edges[i].e;
- se = &e->symedges[0];
- dissolve = true;
- if (true /*!edge_touches_frame(e)*/) {
- fleft = se->face;
- fright = sym(se)->face;
- if (fleft != cdt->outer_face && fright != cdt->outer_face &&
- (fleft->input_ids != NULL || fright->input_ids != NULL)) {
- /* Is there another symedge with same left and right faces?
- * Or is there a vertex not part of e touching the same left and right faces? */
- for (se2 = se->next; dissolve && se2 != se; se2 = se2->next) {
- if (sym(se2)->face == fright ||
- (se2->vert != se->next->vert && vert_touches_face(se2->vert, fright))) {
- dissolve = false;
- }
- }
- }
- }
- if (dissolve) {
- dissolve_symedge(cdt, se);
- }
- }
-}
-
-static void remove_outer_edges_until_constraints(CDT_state *cdt)
-{
- LinkNode *fstack = NULL;
- SymEdge *se, *se_start;
- CDTFace *f, *fsym;
- int visit = ++cdt->visit_count;
-#ifdef DEBUG_CDT
- int dbg_level = 0;
-
- if (dbg_level > 0) {
- fprintf(stderr, "remove_outer_edges_until_constraints\n");
- }
-#endif
-
- cdt->outer_face->visit_index = visit;
- /* Walk around outer face, adding faces on other side of dissolvable edges to stack. */
- se_start = se = cdt->outer_face->symedge;
- do {
- if (!is_constrained_edge(se->edge)) {
- fsym = sym(se)->face;
- if (fsym->visit_index != visit) {
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "pushing f=%p from symedge ", fsym);
- dump_se(se, "an outer edge");
- }
-#endif
- BLI_linklist_prepend_pool(&fstack, fsym, cdt->listpool);
- }
- }
- } while ((se = se->next) != se_start);
-
- while (fstack != NULL) {
- LinkNode *to_dissolve = NULL;
- bool dissolvable;
- f = (CDTFace *)BLI_linklist_pop_pool(&fstack, cdt->listpool);
- if (f->visit_index == visit) {
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "skipping f=%p, already visited\n", f);
- }
-#endif
- continue;
- }
- BLI_assert(f != cdt->outer_face);
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "top of loop, f=%p\n", f);
- dump_se_cycle(f->symedge, "visit", 10000);
- if (dbg_level > 1) {
- dump_cdt(cdt, "cdt at top of loop");
- cdt_draw(cdt, "top of dissolve loop");
- }
- }
-#endif
- f->visit_index = visit;
- se_start = se = f->symedge;
- do {
- dissolvable = !is_constrained_edge(se->edge);
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- dump_se(se, "edge in f");
- fprintf(stderr, " dissolvable=%d\n", dissolvable);
- }
-#endif
- if (dissolvable) {
- fsym = sym(se)->face;
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- dump_se_cycle(fsym->symedge, "fsym", 10000);
- fprintf(stderr, " visited=%d\n", fsym->visit_index == visit);
- }
-#endif
- if (fsym->visit_index != visit) {
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "pushing face %p\n", fsym);
- dump_se_cycle(fsym->symedge, "pushed", 10000);
- }
-#endif
- BLI_linklist_prepend_pool(&fstack, fsym, cdt->listpool);
- }
- else {
- BLI_linklist_prepend_pool(&to_dissolve, se, cdt->listpool);
- }
- }
- se = se->next;
- } while (se != se_start);
- while (to_dissolve != NULL) {
- se = (SymEdge *)BLI_linklist_pop_pool(&to_dissolve, cdt->listpool);
- if (se->next != NULL) {
- dissolve_symedge(cdt, se);
- }
- }
- }
-}
-
-/**
- * Remove edges and merge faces to get desired output, as per options.
- * \note the cdt cannot be further changed after this.
- */
-static void prepare_cdt_for_output(CDT_state *cdt, const CDT_output_type output_type)
-{
- CDTFace *f;
- CDTEdge *e;
- LinkNode *ln;
-
- cdt->output_prepared = true;
- if (cdt->edges == NULL) {
- return;
- }
-
- /* Make sure all non-deleted faces have a symedge. */
- for (ln = cdt->edges; ln; ln = ln->next) {
- e = (CDTEdge *)ln->link;
- if (!is_deleted_edge(e)) {
- if (e->symedges[0].face->symedge == NULL) {
- e->symedges[0].face->symedge = &e->symedges[0];
- }
- if (e->symedges[1].face->symedge == NULL) {
- e->symedges[1].face->symedge = &e->symedges[1];
- }
- }
- }
-#ifdef DEBUG_CDT
- /* All non-deleted faces should have a symedge now. */
- for (ln = cdt->faces; ln; ln = ln->next) {
- f = (CDTFace *)ln->link;
- if (!f->deleted) {
- BLI_assert(f->symedge != NULL);
- }
- }
-#else
- UNUSED_VARS(f);
-#endif
-
- if (output_type == CDT_CONSTRAINTS) {
- remove_non_constraint_edges(cdt);
- }
- else if (output_type == CDT_CONSTRAINTS_VALID_BMESH) {
- remove_non_constraint_edges_leave_valid_bmesh(cdt);
- }
- else if (output_type == CDT_INSIDE) {
- remove_outer_edges_until_constraints(cdt);
- }
-}
-
-static CDT_result *cdt_get_output(CDT_state *cdt,
- const CDT_input *input,
- const CDT_output_type output_type)
-{
- int i, j, nv, ne, nf, faces_len_total;
- int orig_map_size, orig_map_index;
- int *vert_to_output_map;
- CDT_result *result;
- CDTVert *v;
- LinkNode *lne, *lnf, *ln;
- SymEdge *se, *se_start;
- CDTEdge *e;
- CDTFace *f;
-#ifdef DEBUG_CDT
- int dbg_level = 0;
-
- if (dbg_level > 0) {
- fprintf(stderr, "\nCDT_GET_OUTPUT\n\n");
- }
-#endif
-
- prepare_cdt_for_output(cdt, output_type);
-
- result = (CDT_result *)MEM_callocN(sizeof(*result), __func__);
- if (cdt->vert_array_len == 0) {
- return result;
- }
-
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- dump_cdt(cdt, "cdt to output");
- }
-#endif
-
- /* All verts without a merge_to_index will be output.
- * vert_to_output_map[i] will hold the output vertex index
- * corresponding to the vert in position i in cdt->vert_array.
- * Since merging picked the leftmost-lowermost representative,
- * that is not necessarily the same as the vertex with the lowest original
- * index (i.e., index in cdt->vert_array), so we need two passes:
- * one to get the non-merged-to vertices in vert_to_output_map,
- * and a second to put in the merge targets for merged-to vertices.
- */
- vert_to_output_map = BLI_memarena_alloc(cdt->arena, (size_t)cdt->vert_array_len * sizeof(int *));
- nv = 0;
- for (i = 0; i < cdt->vert_array_len; i++) {
- v = cdt->vert_array[i];
- if (v->merge_to_index == -1) {
- vert_to_output_map[i] = nv;
- nv++;
- }
- }
- if (nv <= 0) {
- return result;
- }
- if (nv < cdt->vert_array_len) {
- for (i = 0; i < input->verts_len; i++) {
- v = cdt->vert_array[i];
- if (v->merge_to_index != -1) {
- add_to_input_ids(&cdt->vert_array[v->merge_to_index]->input_ids, i, cdt);
- vert_to_output_map[i] = vert_to_output_map[v->merge_to_index];
- }
- }
- }
-
- result->verts_len = nv;
- result->vert_coords = MEM_malloc_arrayN(nv, sizeof(result->vert_coords[0]), __func__);
-
- /* Make the vertex "orig" map arrays, mapping output verts to lists of input ones. */
- orig_map_size = 0;
- for (i = 0; i < cdt->vert_array_len; i++) {
- if (cdt->vert_array[i]->merge_to_index == -1) {
- orig_map_size += 1 + BLI_linklist_count(cdt->vert_array[i]->input_ids);
- }
- }
- result->verts_orig_len_table = MEM_malloc_arrayN(nv, sizeof(int), __func__);
- result->verts_orig_start_table = MEM_malloc_arrayN(nv, sizeof(int), __func__);
- result->verts_orig = MEM_malloc_arrayN(orig_map_size, sizeof(int), __func__);
-
- orig_map_index = 0;
- i = 0;
- for (j = 0; j < cdt->vert_array_len; j++) {
- v = cdt->vert_array[j];
- if (v->merge_to_index == -1) {
- result->vert_coords[i][0] = (float)v->co[0];
- result->vert_coords[i][1] = (float)v->co[1];
- result->verts_orig_start_table[i] = orig_map_index;
- if (j < input->verts_len) {
- result->verts_orig[orig_map_index++] = j;
- }
- for (ln = v->input_ids; ln; ln = ln->next) {
- result->verts_orig[orig_map_index++] = POINTER_AS_INT(ln->link);
- }
- result->verts_orig_len_table[i] = orig_map_index - result->verts_orig_start_table[i];
- i++;
- }
- }
-
- ne = 0;
- orig_map_size = 0;
- for (ln = cdt->edges; ln; ln = ln->next) {
- e = (CDTEdge *)ln->link;
- if (!is_deleted_edge(e)) {
- ne++;
- if (e->input_ids) {
- orig_map_size += BLI_linklist_count(e->input_ids);
- }
- }
- }
- if (ne != 0) {
- result->edges_len = ne;
- result->face_edge_offset = cdt->face_edge_offset;
- result->edges = MEM_malloc_arrayN(ne, sizeof(result->edges[0]), __func__);
- result->edges_orig_len_table = MEM_malloc_arrayN(ne, sizeof(int), __func__);
- result->edges_orig_start_table = MEM_malloc_arrayN(ne, sizeof(int), __func__);
- if (orig_map_size > 0) {
- result->edges_orig = MEM_malloc_arrayN(orig_map_size, sizeof(int), __func__);
- }
- orig_map_index = 0;
- i = 0;
- for (lne = cdt->edges; lne; lne = lne->next) {
- e = (CDTEdge *)lne->link;
- if (!is_deleted_edge(e)) {
- result->edges[i][0] = vert_to_output_map[e->symedges[0].vert->index];
- result->edges[i][1] = vert_to_output_map[e->symedges[1].vert->index];
- result->edges_orig_start_table[i] = orig_map_index;
- for (ln = e->input_ids; ln; ln = ln->next) {
- result->edges_orig[orig_map_index++] = POINTER_AS_INT(ln->link);
- }
- result->edges_orig_len_table[i] = orig_map_index - result->edges_orig_start_table[i];
- i++;
- }
- }
- }
-
- nf = 0;
- faces_len_total = 0;
- orig_map_size = 0;
- for (ln = cdt->faces; ln; ln = ln->next) {
- f = (CDTFace *)ln->link;
- if (!f->deleted && f != cdt->outer_face) {
- nf++;
- se = se_start = f->symedge;
- BLI_assert(se != NULL);
- do {
- faces_len_total++;
- se = se->next;
- } while (se != se_start);
- if (f->input_ids) {
- orig_map_size += BLI_linklist_count(f->input_ids);
- }
- }
- }
-
- if (nf != 0) {
- result->faces_len = nf;
- result->faces_len_table = MEM_malloc_arrayN(nf, sizeof(int), __func__);
- result->faces_start_table = MEM_malloc_arrayN(nf, sizeof(int), __func__);
- result->faces = MEM_malloc_arrayN(faces_len_total, sizeof(int), __func__);
- result->faces_orig_len_table = MEM_malloc_arrayN(nf, sizeof(int), __func__);
- result->faces_orig_start_table = MEM_malloc_arrayN(nf, sizeof(int), __func__);
- if (orig_map_size > 0) {
- result->faces_orig = MEM_malloc_arrayN(orig_map_size, sizeof(int), __func__);
- }
- orig_map_index = 0;
- i = 0;
- j = 0;
- for (lnf = cdt->faces; lnf; lnf = lnf->next) {
- f = (CDTFace *)lnf->link;
- if (!f->deleted && f != cdt->outer_face) {
- result->faces_start_table[i] = j;
- se = se_start = f->symedge;
- do {
- result->faces[j++] = vert_to_output_map[se->vert->index];
- se = se->next;
- } while (se != se_start);
- result->faces_len_table[i] = j - result->faces_start_table[i];
- result->faces_orig_start_table[i] = orig_map_index;
- for (ln = f->input_ids; ln; ln = ln->next) {
- result->faces_orig[orig_map_index++] = POINTER_AS_INT(ln->link);
- }
- result->faces_orig_len_table[i] = orig_map_index - result->faces_orig_start_table[i];
- i++;
- }
- }
- }
- return result;
-}
-
-/**
- * Calculate the Constrained Delaunay Triangulation of the 2d elements given in \a input.
- *
- * A Delaunay triangulation of a set of vertices is a triangulation where no triangle in the
- * triangulation has a circumcircle that strictly contains another vertex. Delaunay triangulations
- * are avoid long skinny triangles: they maximize the minimum angle of all triangles in the
- * triangulation.
- *
- * A Constrained Delaunay Triangulation adds the requirement that user-provided line segments must
- * appear as edges in the output (perhaps divided into several sub-segments). It is not required
- * that the input edges be non-intersecting: this routine will calculate the intersections. This
- * means that besides triangulating, this routine is also useful for general and robust 2d edge and
- * face intersection.
- *
- * This routine also takes an epsilon parameter in the \a input. Input vertices closer than epsilon
- * will be merged, and we collapse tiny edges (less than epsilon length).
- *
- * The current initial Deluanay triangulation algorithm is the Guibas-Stolfi Divide and Conquer
- * algorithm (see "Primitives for the Manipulation of General Subdivisions and the Computation of
- * Voronoi Diagrams"). and uses Shewchuk's exact predicates to issues where numeric errors cause
- * inconsistent geometric judgments. This is followed by inserting edge constraints (including the
- * edges implied by faces) using the algorithms discussed in "Fully Dynamic Constrained Delaunay
- * Triangulations" by Kallmann, Bieri, and Thalmann.
- *
- * \param input: points to a CDT_input struct which contains the vertices, edges, and faces to be
- * triangulated. \param output_type: specifies which edges to remove after doing the triangulation.
- * \return A pointer to an allocated CDT_result struct, which describes the triangulation in terms
- * of vertices, edges, and faces, and also has tables to map output elements back to input
- * elements. The caller must use BLI_delaunay_2d_cdt_free() on the result when done with it.
- *
- * See the header file BLI_delaunay_2d.h for details of the CDT_input and CDT_result structs and
- * the CDT_output_type enum.
- */
-CDT_result *BLI_delaunay_2d_cdt_calc(const CDT_input *input, const CDT_output_type output_type)
-{
- int nv = input->verts_len;
- int ne = input->edges_len;
- int nf = input->faces_len;
- int i, iv1, iv2, f, fedge_start, fedge_end, ei;
- CDT_state *cdt;
- CDTVert *v1, *v2;
- CDTEdge *face_edge;
- SymEdge *face_symedge;
- LinkNode *edge_list;
- CDT_result *result;
- const CDT_input *input_orig;
- int *new_edge_map;
- static bool called_exactinit = false;
-#ifdef DEBUG_CDT
- int dbg_level = 0;
-#endif
-
- /* The exact orientation and incircle primitives need a one-time initialization of certain
- * constants. */
- if (!called_exactinit) {
- exactinit();
- called_exactinit = true;
- }
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr,
- "\n\nCDT CALC, nv=%d, ne=%d, nf=%d, eps=%g\n",
- input->verts_len,
- input->edges_len,
- input->faces_len,
- input->epsilon);
- }
- if (dbg_level == -1) {
- write_cdt_input_to_file(input);
- }
-#endif
-
- if ((nv > 0 && input->vert_coords == NULL) || (ne > 0 && input->edges == NULL) ||
- (nf > 0 && (input->faces == NULL || input->faces_start_table == NULL ||
- input->faces_len_table == NULL))) {
-#ifdef DEBUG_CDT
- fprintf(stderr, "invalid input: unexpected NULL array(s)\n");
-#endif
- return NULL;
- }
-
- input_orig = input;
- input = modify_input_for_near_edge_ends(input, &new_edge_map);
- if (input != input_orig) {
- nv = input->verts_len;
- ne = input->edges_len;
- nf = input->faces_len;
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "input modified for near ends; now ne=%d\n", ne);
- }
-#endif
- }
- cdt = cdt_init(input);
- initial_triangulation(cdt);
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- validate_cdt(cdt, true, false, false);
- if (dbg_level > 1) {
- cdt_draw(cdt, "after initial triangulation");
- }
- }
-#endif
-
- for (i = 0; i < ne; i++) {
- iv1 = input->edges[i][0];
- iv2 = input->edges[i][1];
- if (iv1 < 0 || iv1 >= nv || iv2 < 0 || iv2 >= nv) {
-#ifdef DEBUG_CDT
- fprintf(stderr, "edge indices for e%d not valid: v1=%d, v2=%d, nv=%d\n", i, iv1, iv2, nv);
-#endif
- continue;
- }
- v1 = cdt->vert_array[iv1];
- v2 = cdt->vert_array[iv2];
- if (v1->merge_to_index != -1) {
- v1 = cdt->vert_array[v1->merge_to_index];
- }
- if (v2->merge_to_index != -1) {
- v2 = cdt->vert_array[v2->merge_to_index];
- }
- if (new_edge_map) {
- ei = new_edge_map[i];
- }
- else {
- ei = i;
- }
- add_edge_constraint(cdt, v1, v2, ei, NULL);
-#ifdef DEBUG_CDT
- if (dbg_level > 3) {
- char namebuf[60];
- sprintf(namebuf, "after edge constraint %d = (%d,%d)\n", i, iv1, iv2);
- cdt_draw(cdt, namebuf);
- // dump_cdt(cdt, namebuf);
- validate_cdt(cdt, true, true, false);
- }
-#endif
- }
-
- cdt->face_edge_offset = ne;
- for (f = 0; f < nf; f++) {
- int flen = input->faces_len_table[f];
- int fstart = input->faces_start_table[f];
- if (flen <= 2) {
-#ifdef DEBUG_CDT
- fprintf(stderr, "face %d has length %d; ignored\n", f, flen);
-#endif
- continue;
- }
- for (i = 0; i < flen; i++) {
- int face_edge_id = cdt->face_edge_offset + fstart + i;
- if (new_edge_map) {
- face_edge_id = new_edge_map[face_edge_id];
- }
- iv1 = input->faces[fstart + i];
- iv2 = input->faces[fstart + ((i + 1) % flen)];
- if (iv1 < 0 || iv1 >= nv || iv2 < 0 || iv2 >= nv) {
-#ifdef DEBUG_CDT
- fprintf(stderr, "face indices not valid: f=%d, iv1=%d, iv2=%d, nv=%d\n", f, iv1, iv2, nv);
-#endif
- continue;
- }
- v1 = cdt->vert_array[iv1];
- v2 = cdt->vert_array[iv2];
- if (v1->merge_to_index != -1) {
- v1 = cdt->vert_array[v1->merge_to_index];
- }
- if (v2->merge_to_index != -1) {
- v2 = cdt->vert_array[v2->merge_to_index];
- }
- add_edge_constraint(cdt, v1, v2, face_edge_id, &edge_list);
-#ifdef DEBUG_CDT
- if (dbg_level > 2) {
- fprintf(stderr, "edges for edge %d:\n", i);
- for (LinkNode *ln = edge_list; ln; ln = ln->next) {
- CDTEdge *cdt_e = (CDTEdge *)ln->link;
- fprintf(stderr,
- " (%.2f,%.2f)->(%.2f,%.2f)\n",
- F2(cdt_e->symedges[0].vert->co),
- F2(cdt_e->symedges[1].vert->co));
- }
- }
- if (dbg_level > 2) {
- cdt_draw(cdt, "after a face edge");
- if (dbg_level > 3) {
- dump_cdt(cdt, "after a face edge");
- }
- validate_cdt(cdt, true, true, false);
- }
-#endif
- if (i == 0) {
- face_edge = (CDTEdge *)edge_list->link;
- face_symedge = &face_edge->symedges[0];
- if (face_symedge->vert != v1) {
- face_symedge = &face_edge->symedges[1];
- BLI_assert(face_symedge->vert == v1);
- }
- }
- BLI_linklist_free_pool(edge_list, NULL, cdt->listpool);
- }
- fedge_start = cdt->face_edge_offset + fstart;
- fedge_end = fedge_start + flen - 1;
- add_face_ids(cdt, face_symedge, f, fedge_start, fedge_end);
- }
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- validate_cdt(cdt, true, true, false);
- }
- if (dbg_level > 1) {
- cdt_draw(cdt, "after adding edges and faces");
- if (dbg_level > 2) {
- dump_cdt(cdt, "after adding edges and faces");
- }
- }
-#endif
-
- if (cdt->epsilon > 0.0) {
- remove_small_features(cdt);
-#ifdef DEBUG_CDT
- if (dbg_level > 2) {
- cdt_draw(cdt, "after remove small features\n");
- if (dbg_level > 3) {
- dump_cdt(cdt, "after remove small features\n");
- }
- }
-#endif
- }
-
- result = cdt_get_output(cdt, input, output_type);
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- cdt_draw(cdt, "final");
- }
-#endif
-
- if (input != input_orig) {
- free_modified_input((CDT_input *)input);
- }
- new_cdt_free(cdt);
- return result;
-}
-
-void BLI_delaunay_2d_cdt_free(CDT_result *result)
-{
- if (result == NULL) {
- return;
- }
- if (result->vert_coords) {
- MEM_freeN(result->vert_coords);
- }
- if (result->edges) {
- MEM_freeN(result->edges);
- }
- if (result->faces) {
- MEM_freeN(result->faces);
- }
- if (result->faces_start_table) {
- MEM_freeN(result->faces_start_table);
- }
- if (result->faces_len_table) {
- MEM_freeN(result->faces_len_table);
- }
- if (result->verts_orig) {
- MEM_freeN(result->verts_orig);
- }
- if (result->verts_orig_start_table) {
- MEM_freeN(result->verts_orig_start_table);
- }
- if (result->verts_orig_len_table) {
- MEM_freeN(result->verts_orig_len_table);
- }
- if (result->edges_orig) {
- MEM_freeN(result->edges_orig);
- }
- if (result->edges_orig_start_table) {
- MEM_freeN(result->edges_orig_start_table);
- }
- if (result->edges_orig_len_table) {
- MEM_freeN(result->edges_orig_len_table);
- }
- if (result->faces_orig) {
- MEM_freeN(result->faces_orig);
- }
- if (result->faces_orig_start_table) {
- MEM_freeN(result->faces_orig_start_table);
- }
- if (result->faces_orig_len_table) {
- MEM_freeN(result->faces_orig_len_table);
- }
- MEM_freeN(result);
-}
-
-#ifdef DEBUG_CDT
-
-ATTU static const char *vertname(const CDTVert *v)
-{
- static char vertnamebuf[20];
-
- sprintf(vertnamebuf, "[%d]", v->index);
- return vertnamebuf;
-}
-
-ATTU static const char *sename(const SymEdge *se)
-{
- static char senamebuf[20];
-
- sprintf(senamebuf, "{%x}", (POINTER_AS_UINT(se)) & 0xFFFF);
- return senamebuf;
-}
-
-static void dump_v(const CDTVert *v, const char *lab)
-{
- fprintf(stderr, "%s%s(%.10f,%.10f)\n", lab, vertname(v), F2(v->co));
-}
-
-static void dump_se(const SymEdge *se, const char *lab)
-{
- if (se->next) {
- fprintf(stderr,
- "%s%s((%.10f,%.10f)->(%.10f,%.10f))",
- lab,
- vertname(se->vert),
- F2(se->vert->co),
- F2(se->next->vert->co));
- fprintf(stderr, "%s\n", vertname(se->next->vert));
- }
- else {
- fprintf(stderr, "%s%s((%.10f,%.10f)->NULL)\n", lab, vertname(se->vert), F2(se->vert->co));
- }
-}
-
-static void dump_se_short(const SymEdge *se, const char *lab)
-{
- if (se == NULL) {
- fprintf(stderr, "%sNULL", lab);
- }
- else {
- fprintf(stderr, "%s%s", lab, vertname(se->vert));
- fprintf(stderr, "%s", se->next == NULL ? "[NULL]" : vertname(se->next->vert));
- }
-}
-
-static void dump_se_cycle(const SymEdge *se, const char *lab, const int limit)
-{
- int count = 0;
- const SymEdge *s = se;
- fprintf(stderr, "%s:\n", lab);
- do {
- dump_se(s, " ");
- s = s->next;
- count++;
- } while (s != se && count < limit);
- if (count == limit) {
- fprintf(stderr, " limit hit without cycle!\n");
- }
-}
-
-static void dump_id_list(const LinkNode *id_list, const char *lab)
-{
- const LinkNode *ln;
- if (!id_list) {
- return;
- }
- fprintf(stderr, "%s", lab);
- for (ln = id_list; ln; ln = ln->next) {
- fprintf(stderr, "%d%c", POINTER_AS_INT(ln->link), ln->next ? ' ' : '\n');
- }
-}
-
-static void dump_cross_data(struct CrossData *cd, const char *lab)
-{
- fprintf(stderr, "%s", lab);
- if (cd->lambda == 0.0) {
- fprintf(stderr, "v%d", cd->vert->index);
- }
- else {
- fprintf(stderr, "lambda=%.17g", cd->lambda);
- }
- dump_se_short(cd->in, " in=");
- dump_se_short(cd->out, " out=");
- fprintf(stderr, "\n");
-}
-
-/* If filter_fn != NULL, only dump vert v its edges when filter_fn(cdt, v, filter_data) is true. */
-# define PL(p) (POINTER_AS_UINT(p) & 0xFFFF)
-static void dump_cdt_filtered(const CDT_state *cdt,
- bool (*filter_fn)(const CDT_state *, int, void *),
- void *filter_data,
- const char *lab)
-{
- LinkNode *ln;
- CDTVert *v, *vother;
- CDTEdge *e;
- CDTFace *f;
- SymEdge *se;
- int i, cnt;
-
- fprintf(stderr, "\nCDT %s\n", lab);
- fprintf(stderr, "\nVERTS\n");
- for (i = 0; i < cdt->vert_array_len; i++) {
- if (filter_fn && !filter_fn(cdt, i, filter_data)) {
- continue;
- }
- v = cdt->vert_array[i];
- fprintf(stderr, "%s %x: (%f,%f) symedge=%x", vertname(v), PL(v), F2(v->co), PL(v->symedge));
- if (v->merge_to_index == -1) {
- fprintf(stderr, "\n");
- }
- else {
- fprintf(stderr, " merge to %s\n", vertname(cdt->vert_array[v->merge_to_index]));
- continue;
- }
- dump_id_list(v->input_ids, " ");
- se = v->symedge;
- cnt = 0;
- if (se) {
- fprintf(stderr, " edges out:\n");
- do {
- if (se->next == NULL) {
- fprintf(stderr, " [NULL next/rot symedge, se=%x\n", PL(se));
- break;
- }
- if (se->next->next == NULL) {
- fprintf(stderr, " [NULL next-next/rot symedge, se=%x\n", PL(se));
- break;
- }
- vother = sym(se)->vert;
- fprintf(stderr, " %s (e=%x, se=%x)\n", vertname(vother), PL(se->edge), PL(se));
- se = se->rot;
- cnt++;
- } while (se != v->symedge && cnt < 25);
- fprintf(stderr, "\n");
- }
- }
- if (filter_fn) {
- return;
- }
- fprintf(stderr, "\nEDGES\n");
- for (ln = cdt->edges; ln; ln = ln->next) {
- e = (CDTEdge *)ln->link;
- if (e->symedges[0].next == NULL) {
- continue;
- }
- fprintf(stderr, "%x:\n", PL(e));
- for (i = 0; i < 2; i++) {
- se = &e->symedges[i];
- fprintf(stderr,
- " se[%d] @%x: next=%x, rot=%x, vert=%x [%s] (%.2f,%.2f), edge=%x, face=%x\n",
- i,
- PL(se),
- PL(se->next),
- PL(se->rot),
- PL(se->vert),
- vertname(se->vert),
- F2(se->vert->co),
- PL(se->edge),
- PL(se->face));
- }
- dump_id_list(e->input_ids, " ");
- }
- fprintf(stderr, "\nFACES\n");
- for (ln = cdt->faces; ln; ln = ln->next) {
- f = (CDTFace *)ln->link;
- if (f->deleted) {
- continue;
- }
- if (f == cdt->outer_face) {
- fprintf(stderr, "%x: outer", PL(f));
- }
- fprintf(stderr, " symedge=%x\n", PL(f->symedge));
- dump_id_list(f->input_ids, " ");
- }
- fprintf(stderr, "\nOTHER\n");
- fprintf(stderr, "outer_face=%x\n", PL(cdt->outer_face));
- fprintf(
- stderr, "minx=%f, maxx=%f, miny=%f, maxy=%f\n", cdt->minx, cdt->maxx, cdt->miny, cdt->maxy);
- fprintf(stderr, "margin=%f\n", cdt->margin);
-}
-# undef PL
-
-static void dump_cdt(const CDT_state *cdt, const char *lab)
-{
- dump_cdt_filtered(cdt, NULL, NULL, lab);
-}
-
-typedef struct ReachableFilterData {
- int vstart_index;
- int maxdist;
-} ReachableFilterData;
-
-/* Stupid algorithm will repeat itself. Don't use for large n. */
-static bool reachable_filter(const CDT_state *cdt, int v_index, void *filter_data)
-{
- CDTVert *v;
- SymEdge *se;
- ReachableFilterData *rfd_in = (ReachableFilterData *)filter_data;
- ReachableFilterData rfd_next;
-
- if (v_index == rfd_in->vstart_index) {
- return true;
- }
- if (rfd_in->maxdist <= 0 || v_index < 0 || v_index >= cdt->vert_array_len) {
- return false;
- }
- else {
- v = cdt->vert_array[v_index];
- se = v->symedge;
- if (se != NULL) {
- rfd_next.vstart_index = rfd_in->vstart_index;
- rfd_next.maxdist = rfd_in->maxdist - 1;
- do {
- if (reachable_filter(cdt, se->next->vert->index, &rfd_next)) {
- return true;
- }
- se = se->rot;
- } while (se != v->symedge);
- }
- }
- return false;
-}
-
-static void set_min_max(CDT_state *cdt)
-{
- int i;
- double minx, maxx, miny, maxy;
- double *co;
-
- minx = miny = DBL_MAX;
- maxx = maxy = -DBL_MAX;
- for (i = 0; i < cdt->vert_array_len; i++) {
- co = cdt->vert_array[i]->co;
- if (co[0] < minx) {
- minx = co[0];
- }
- if (co[0] > maxx) {
- maxx = co[0];
- }
- if (co[1] < miny) {
- miny = co[1];
- }
- if (co[1] > maxy) {
- maxy = co[1];
- }
- }
- if (minx != DBL_MAX) {
- cdt->minx = minx;
- cdt->miny = miny;
- cdt->maxx = maxx;
- cdt->maxy = maxy;
- }
-}
-
-static void dump_cdt_vert_neighborhood(CDT_state *cdt, int v, int maxdist, const char *lab)
-{
- ReachableFilterData rfd;
- rfd.vstart_index = v;
- rfd.maxdist = maxdist;
- dump_cdt_filtered(cdt, reachable_filter, &rfd, lab);
-}
-
-/*
- * Make an html file with svg in it to display the argument cdt.
- * Mouse-overs will reveal the coordinates of vertices and edges.
- * Constraint edges are drawn thicker than non-constraint edges.
- * The first call creates DRAWFILE; subsequent calls append to it.
- */
-# define DRAWFILE "/tmp/debug_draw.html"
-# define MAX_DRAW_WIDTH 2000
-# define MAX_DRAW_HEIGHT 1400
-# define THIN_LINE 1
-# define THICK_LINE 4
-# define VERT_RADIUS 3
-# define DRAW_VERT_LABELS 1
-# define DRAW_EDGE_LABELS 0
-
-static void cdt_draw_region(
- CDT_state *cdt, const char *lab, double minx, double miny, double maxx, double maxy)
-{
- static bool append = false;
- FILE *f = fopen(DRAWFILE, append ? "a" : "w");
- int view_width, view_height;
- double width, height, aspect, scale;
- LinkNode *ln;
- CDTVert *v, *u;
- CDTEdge *e;
- int i, strokew;
-
- width = maxx - minx;
- height = maxy - miny;
- aspect = height / width;
- view_width = MAX_DRAW_WIDTH;
- view_height = (int)(view_width * aspect);
- if (view_height > MAX_DRAW_HEIGHT) {
- view_height = MAX_DRAW_HEIGHT;
- view_width = (int)(view_height / aspect);
- }
- scale = view_width / width;
-
-# define SX(x) ((x - minx) * scale)
-# define SY(y) ((maxy - y) * scale)
-
- if (!f) {
- printf("couldn't open file %s\n", DRAWFILE);
- return;
- }
- fprintf(f, "<div>%s</div>\n<div>\n", lab);
- fprintf(f,
- "<svg version=\"1.1\" "
- "xmlns=\"http://www.w3.org/2000/svg\" "
- "xmlns:xlink=\"http://www.w3.org/1999/xlink\" "
- "xml:space=\"preserve\"\n");
- fprintf(f, "width=\"%d\" height=\"%d\">/n", view_width, view_height);
-
- for (ln = cdt->edges; ln; ln = ln->next) {
- e = (CDTEdge *)ln->link;
- if (is_deleted_edge(e)) {
- continue;
- }
- u = e->symedges[0].vert;
- v = e->symedges[1].vert;
- strokew = is_constrained_edge(e) ? THICK_LINE : THIN_LINE;
- fprintf(f,
- "<line fill=\"none\" stroke=\"black\" stroke-width=\"%d\" "
- "x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\">\n",
- strokew,
- SX(u->co[0]),
- SY(u->co[1]),
- SX(v->co[0]),
- SY(v->co[1]));
- fprintf(f, " <title>%s", vertname(u));
- fprintf(f, "%s</title>\n", vertname(v));
- fprintf(f, "</line>\n");
-# if DRAW_EDGE_LABELS
- fprintf(f,
- "<text x=\"%f\" y=\"%f\" font-size=\"small\">",
- SX(0.5 * (u->co[0] + v->co[0])),
- SY(0.5 * (u->co[1] + v->co[1])));
- fprintf(f, "%s", vertname(u));
- fprintf(f, "%s", vertname(v));
- fprintf(f, "%s", sename(&e->symedges[0]));
- fprintf(f, "%s</text>\n", sename(&e->symedges[1]));
-# endif
- }
- i = 0;
- for (; i < cdt->vert_array_len; i++) {
- v = cdt->vert_array[i];
- if (v->merge_to_index != -1) {
- continue;
- }
- fprintf(f,
- "<circle fill=\"black\" cx=\"%f\" cy=\"%f\" r=\"%d\">\n",
- SX(v->co[0]),
- SY(v->co[1]),
- VERT_RADIUS);
- fprintf(f, " <title>%s(%.10f,%.10f)</title>\n", vertname(v), v->co[0], v->co[1]);
- fprintf(f, "</circle>\n");
-# if DRAW_VERT_LABELS
- fprintf(f,
- "<text x=\"%f\" y=\"%f\" font-size=\"small\">%s</text>\n",
- SX(v->co[0]) + VERT_RADIUS,
- SY(v->co[1]) - VERT_RADIUS,
- vertname(v));
-# endif
- }
-
- fprintf(f, "</svg>\n</div>\n");
- fclose(f);
- append = true;
-# undef SX
-# undef SY
-}
-
-static void cdt_draw(CDT_state *cdt, const char *lab)
-{
- double draw_margin, minx, maxx, miny, maxy;
-
- set_min_max(cdt);
- draw_margin = (cdt->maxx - cdt->minx + cdt->maxy - cdt->miny + 1) * 0.05;
- minx = cdt->minx - draw_margin;
- maxx = cdt->maxx + draw_margin;
- miny = cdt->miny - draw_margin;
- maxy = cdt->maxy + draw_margin;
- cdt_draw_region(cdt, lab, minx, miny, maxx, maxy);
-}
-
-static void cdt_draw_vertex_region(CDT_state *cdt, int v, double dist, const char *lab)
-{
- const double *co = cdt->vert_array[v]->co;
- cdt_draw_region(cdt, lab, co[0] - dist, co[1] - dist, co[0] + dist, co[1] + dist);
-}
-
-static void cdt_draw_edge_region(CDT_state *cdt, int v1, int v2, double dist, const char *lab)
-{
- const double *co1 = cdt->vert_array[v1]->co;
- const double *co2 = cdt->vert_array[v2]->co;
- double minx, miny, maxx, maxy;
-
- minx = min_dd(co1[0], co2[0]);
- miny = min_dd(co1[1], co2[1]);
- maxx = max_dd(co1[0], co2[0]);
- maxy = max_dd(co1[1], co2[1]);
- cdt_draw_region(cdt, lab, minx - dist, miny - dist, maxx + dist, maxy + dist);
-}
-
-# define CDTFILE "/tmp/cdtinput.txt"
-static void write_cdt_input_to_file(const CDT_input *inp)
-{
- int i, j;
- FILE *f = fopen(CDTFILE, "w");
-
- fprintf(f, "%d %d %d\n", inp->verts_len, inp->edges_len, inp->faces_len);
- for (i = 0; i < inp->verts_len; i++) {
- fprintf(f, "%.17f %.17f\n", inp->vert_coords[i][0], inp->vert_coords[i][1]);
- }
- for (i = 0; i < inp->edges_len; i++) {
- fprintf(f, "%d %d\n", inp->edges[i][0], inp->edges[i][1]);
- }
- for (i = 0; i < inp->faces_len; i++) {
- for (j = 0; j < inp->faces_len_table[i]; j++) {
- fprintf(f, "%d ", inp->faces[j + inp->faces_start_table[i]]);
- }
- fprintf(f, "\n");
- }
- fclose(f);
-}
-
-# ifndef NDEBUG /* Only used in assert. */
-/*
- * Is a visible from b: i.e., ab crosses no edge of cdt?
- * If constrained is true, consider only constrained edges as possible crossed edges.
- * In any case, don't count an edge ab itself.
- * Note: this is an expensive test if there are a lot of edges.
- */
-static bool is_visible(const CDTVert *a, const CDTVert *b, bool constrained, const CDT_state *cdt)
-{
- const LinkNode *ln;
- const CDTEdge *e;
- const SymEdge *se, *senext;
- double lambda, mu;
- int ikind;
-
- for (ln = cdt->edges; ln; ln = ln->next) {
- e = (const CDTEdge *)ln->link;
- if (is_deleted_edge(e) || is_border_edge(e, cdt)) {
- continue;
- }
- if (constrained && !is_constrained_edge(e)) {
- continue;
- }
- se = (const SymEdge *)&e->symedges[0];
- senext = se->next;
- if ((a == se->vert || a == senext->vert) || b == se->vert || b == se->next->vert) {
- continue;
- }
- ikind = isect_seg_seg_v2_lambda_mu_db(
- a->co, b->co, se->vert->co, senext->vert->co, &lambda, &mu);
- if (ikind != ISECT_LINE_LINE_NONE) {
- if (ikind == ISECT_LINE_LINE_COLINEAR) {
- /* TODO: special test here for overlap. */
- continue;
- }
- /* Allow an intersection very near or at ends, to allow for numerical error. */
- if (lambda > FLT_EPSILON && (1.0 - lambda) > FLT_EPSILON && mu > FLT_EPSILON &&
- (1.0 - mu) > FLT_EPSILON) {
- return false;
- }
- }
- }
- return true;
-}
-# endif
-
-# ifndef NDEBUG /* Only used in assert. */
-/*
- * Check that edge ab satisfies constrained delaunay condition:
- * That is, for all non-constraint, non-border edges ab,
- * (1) ab is visible in the constraint graph; and
- * (2) there is a circle through a and b such that any vertex v connected by an edge to a or b
- * is not inside that circle.
- * The argument 'se' specifies ab by: a is se's vert and b is se->next's vert.
- * Return true if check is OK.
- */
-static bool is_delaunay_edge(const SymEdge *se)
-{
- int i;
- CDTVert *a, *b, *c;
- const SymEdge *sesym, *curse, *ss;
- bool ok[2];
-
- if (!is_constrained_edge(se->edge)) {
- return true;
- }
- sesym = sym(se);
- a = se->vert;
- b = se->next->vert;
- /* Try both the triangles adjacent to se's edge for circle. */
- for (i = 0; i < 2; i++) {
- ok[i] = true;
- curse = (i == 0) ? se : sesym;
- a = curse->vert;
- b = curse->next->vert;
- c = curse->next->next->vert;
- for (ss = curse->rot; ss != curse; ss = ss->rot) {
- ok[i] |= incircle(a->co, b->co, c->co, ss->next->vert->co) <= 0.0;
- }
- }
- return ok[0] || ok[1];
-}
-# endif
-
-# ifndef NDEBUG
-static bool plausible_non_null_ptr(void *p)
-{
- return p > (void *)0x1000;
-}
-# endif
-
-static void validate_cdt(CDT_state *cdt,
- bool check_all_tris,
- bool check_delaunay,
- bool check_visibility)
-{
- LinkNode *ln;
- int totedges, totfaces, totverts;
- CDTEdge *e;
- SymEdge *se, *sesym, *s;
- CDTVert *v, *v1, *v2, *v3;
- CDTFace *f;
- int i, limit;
- bool isborder;
-
- if (cdt->output_prepared) {
- return;
- }
- if (cdt->edges == NULL || cdt->edges->next == NULL) {
- return;
- }
-
- BLI_assert(cdt != NULL);
- totedges = 0;
- for (ln = cdt->edges; ln; ln = ln->next) {
- e = (CDTEdge *)ln->link;
- se = &e->symedges[0];
- sesym = &e->symedges[1];
- if (is_deleted_edge(e)) {
- BLI_assert(se->rot == NULL && sesym->next == NULL && sesym->rot == NULL);
- continue;
- }
- totedges++;
- isborder = is_border_edge(e, cdt);
- BLI_assert(se->vert != sesym->vert);
- BLI_assert(se->edge == sesym->edge && se->edge == e);
- BLI_assert(sym(se) == sesym && sym(sesym) == se);
- for (i = 0; i < 2; i++) {
- se = &e->symedges[i];
- v = se->vert;
- f = se->face;
- BLI_assert(plausible_non_null_ptr(v));
- if (f != NULL) {
- BLI_assert(plausible_non_null_ptr(f));
- }
- BLI_assert(plausible_non_null_ptr(se->next));
- BLI_assert(plausible_non_null_ptr(se->rot));
- if (check_all_tris && se->face != cdt->outer_face) {
- limit = 3;
- }
- else {
- limit = 10000;
- }
- BLI_assert(reachable(se->next, se, limit));
- if (limit == 3) {
- v1 = se->vert;
- v2 = se->next->vert;
- v3 = se->next->next->vert;
- /* The triangle should be positively oriented, but because
- * the insertion of intersection vertices doesn't use exact
- * arithmetic, this may not be true, so allow a little slop. */
- BLI_assert(orient2d(v1->co, v2->co, v3->co) >= -FLT_EPSILON);
- BLI_assert(orient2d(v2->co, v3->co, v1->co) >= -FLT_EPSILON);
- BLI_assert(orient2d(v3->co, v1->co, v2->co) >= -FLT_EPSILON);
- }
- UNUSED_VARS_NDEBUG(limit);
- BLI_assert(se->next->next != se);
- s = se;
- do {
- BLI_assert(prev(s)->next == s);
- BLI_assert(s->rot == sym(prev(s)));
- s = s->next;
- } while (s != se);
- }
- if (check_visibility) {
- BLI_assert(isborder || is_visible(se->vert, se->next->vert, false, cdt));
- }
- if (!isborder && check_delaunay) {
- BLI_assert(is_delaunay_edge(se));
- }
- }
- totverts = 0;
- for (i = 0; i < cdt->vert_array_len; i++) {
- v = cdt->vert_array[i];
- BLI_assert(plausible_non_null_ptr(v));
- if (v->merge_to_index != -1) {
- BLI_assert(v->merge_to_index >= 0 && v->merge_to_index < cdt->vert_array_len);
- continue;
- }
- totverts++;
- BLI_assert(cdt->vert_array_len <= 1 || v->symedge->vert == v);
- }
- totfaces = 0;
- for (ln = cdt->faces; ln; ln = ln->next) {
- f = (CDTFace *)ln->link;
- BLI_assert(plausible_non_null_ptr(f));
- if (f->deleted) {
- continue;
- }
- totfaces++;
- if (f == cdt->outer_face) {
- continue;
- }
- }
- /* Euler's formula for planar graphs. */
- if (check_all_tris && totfaces > 1) {
- BLI_assert(totverts - totedges + totfaces == 2);
- }
-}
-#endif
-
-/* Jonathan Shewchuk's adaptive predicates, trimmed to those needed here.
- * Permission obtained by private communication from Jonathan to include this code in Blender.
- */
-
-/*
- * Routines for Arbitrary Precision Floating-point Arithmetic
- * and Fast Robust Geometric Predicates
- * (predicates.c)
- *
- * May 18, 1996
- *
- * Placed in the public domain by
- * Jonathan Richard Shewchuk
- * School of Computer Science
- * Carnegie Mellon University
- * 5000 Forbes Avenue
- * Pittsburgh, Pennsylvania 15213-3891
- * jrs@cs.cmu.edu
- *
- * This file contains C implementation of algorithms for exact addition
- * and multiplication of floating-point numbers, and predicates for
- * robustly performing the orientation and incircle tests used in
- * computational geometry. The algorithms and underlying theory are
- * described in Jonathan Richard Shewchuk. "Adaptive Precision Floating-
- * Point Arithmetic and Fast Robust Geometric Predicates." Technical
- * Report CMU-CS-96-140, School of Computer Science, Carnegie Mellon
- * University, Pittsburgh, Pennsylvania, May 1996. (Submitted to
- * Discrete & Computational Geometry.)
- *
- * This file, the paper listed above, and other information are available
- * from the Web page http://www.cs.cmu.edu/~quake/robust.html .
- *
- * Using this code:
- *
- * First, read the short or long version of the paper (from the Web page
- * above).
- *
- * Be sure to call exactinit() once, before calling any of the arithmetic
- * functions or geometric predicates. Also be sure to turn on the
- * optimizer when compiling this file.
- *
- * On some machines, the exact arithmetic routines might be defeated by the
- * use of internal extended precision floating-point registers. Sometimes
- * this problem can be fixed by defining certain values to be volatile,
- * thus forcing them to be stored to memory and rounded off. This isn't
- * a great solution, though, as it slows the arithmetic down.
- *
- * To try this out, write "#define INEXACT volatile" below. Normally,
- * however, INEXACT should be defined to be nothing. ("#define INEXACT".)
- */
-
-#define INEXACT /* Nothing */
-/* #define INEXACT volatile */
-
-/* Which of the following two methods of finding the absolute values is
- * fastest is compiler-dependent. A few compilers can inline and optimize
- * the fabs() call; but most will incur the overhead of a function call,
- * which is disastrously slow. A faster way on IEEE machines might be to
- * mask the appropriate bit, but that's difficult to do in C.
- */
-
-#define Absolute(a) ((a) >= 0.0 ? (a) : -(a))
-/* #define Absolute(a) fabs(a) */
-
-/* Many of the operations are broken up into two pieces, a main part that
- * performs an approximate operation, and a "tail" that computes the
- * roundoff error of that operation.
- *
- * The operations Fast_Two_Sum(), Fast_Two_Diff(), Two_Sum(), Two_Diff(),
- * Split(), and Two_Product() are all implemented as described in the
- * reference. Each of these macros requires certain variables to be
- * defined in the calling routine. The variables `bvirt', `c', `abig',
- * `_i', `_j', `_k', `_l', `_m', and `_n' are declared `INEXACT' because
- * they store the result of an operation that may incur roundoff error.
- * The input parameter `x' (or the highest numbered `x_' parameter) must
- * also be declared `INEXACT'.
- */
-
-#define Fast_Two_Sum_Tail(a, b, x, y) \
- bvirt = x - a; \
- y = b - bvirt
-
-#define Fast_Two_Sum(a, b, x, y) \
- x = (double)(a + b); \
- Fast_Two_Sum_Tail(a, b, x, y)
-
-#define Fast_Two_Diff_Tail(a, b, x, y) \
- bvirt = a - x; \
- y = bvirt - b
-
-#define Fast_Two_Diff(a, b, x, y) \
- x = (double)(a - b); \
- Fast_Two_Diff_Tail(a, b, x, y)
-
-#define Two_Sum_Tail(a, b, x, y) \
- bvirt = (double)(x - a); \
- avirt = x - bvirt; \
- bround = b - bvirt; \
- around = a - avirt; \
- y = around + bround
-
-#define Two_Sum(a, b, x, y) \
- x = (double)(a + b); \
- Two_Sum_Tail(a, b, x, y)
-
-#define Two_Diff_Tail(a, b, x, y) \
- bvirt = (double)(a - x); \
- avirt = x + bvirt; \
- bround = bvirt - b; \
- around = a - avirt; \
- y = around + bround
-
-#define Two_Diff(a, b, x, y) \
- x = (double)(a - b); \
- Two_Diff_Tail(a, b, x, y)
-
-#define Split(a, ahi, alo) \
- c = (double)(splitter * a); \
- abig = (double)(c - a); \
- ahi = c - abig; \
- alo = a - ahi
-
-#define Two_Product_Tail(a, b, x, y) \
- Split(a, ahi, alo); \
- Split(b, bhi, blo); \
- err1 = x - (ahi * bhi); \
- err2 = err1 - (alo * bhi); \
- err3 = err2 - (ahi * blo); \
- y = (alo * blo) - err3
-
-#define Two_Product(a, b, x, y) \
- x = (double)(a * b); \
- Two_Product_Tail(a, b, x, y)
-
-#define Two_Product_Presplit(a, b, bhi, blo, x, y) \
- x = (double)(a * b); \
- Split(a, ahi, alo); \
- err1 = x - (ahi * bhi); \
- err2 = err1 - (alo * bhi); \
- err3 = err2 - (ahi * blo); \
- y = (alo * blo) - err3
-
-#define Square_Tail(a, x, y) \
- Split(a, ahi, alo); \
- err1 = x - (ahi * ahi); \
- err3 = err1 - ((ahi + ahi) * alo); \
- y = (alo * alo) - err3
-
-#define Square(a, x, y) \
- x = (double)(a * a); \
- Square_Tail(a, x, y)
-
-#define Two_One_Sum(a1, a0, b, x2, x1, x0) \
- Two_Sum(a0, b, _i, x0); \
- Two_Sum(a1, _i, x2, x1)
-
-#define Two_One_Diff(a1, a0, b, x2, x1, x0) \
- Two_Diff(a0, b, _i, x0); \
- Two_Sum(a1, _i, x2, x1)
-
-#define Two_Two_Sum(a1, a0, b1, b0, x3, x2, x1, x0) \
- Two_One_Sum(a1, a0, b0, _j, _0, x0); \
- Two_One_Sum(_j, _0, b1, x3, x2, x1)
-
-#define Two_Two_Diff(a1, a0, b1, b0, x3, x2, x1, x0) \
- Two_One_Diff(a1, a0, b0, _j, _0, x0); \
- Two_One_Diff(_j, _0, b1, x3, x2, x1)
-
-static double splitter; /* = 2^ceiling(p / 2) + 1. Used to split floats in half. */
-static double m_epsilon; /* = 2^(-p). Used to estimate roundoff errors. */
-/* A set of coefficients used to calculate maximum roundoff errors. */
-static double resulterrbound;
-static double ccwerrboundA, ccwerrboundB, ccwerrboundC;
-static double o3derrboundA, o3derrboundB, o3derrboundC;
-static double iccerrboundA, iccerrboundB, iccerrboundC;
-static double isperrboundA, isperrboundB, isperrboundC;
-
-/* exactinit() Initialize the variables used for exact arithmetic.
- *
- * `epsilon' is the largest power of two such that 1.0 + epsilon = 1.0 in
- * floating-point arithmetic. `epsilon' bounds the relative roundoff
- * error. It is used for floating-point error analysis.
- *
- * `splitter' is used to split floating-point numbers into two
- * half-length significands for exact multiplication.
- *
- * I imagine that a highly optimizing compiler might be too smart for its
- * own good, and somehow cause this routine to fail, if it pretends that
- * floating-point arithmetic is too much like real arithmetic.
- *
- * Don't change this routine unless you fully understand it.
- */
-
-static void exactinit(void)
-{
- double half;
- double check, lastcheck;
- int every_other;
-
- every_other = 1;
- half = 0.5;
- m_epsilon = 1.0;
- splitter = 1.0;
- check = 1.0;
- /* Repeatedly divide `epsilon' by two until it is too small to add to
- * one without causing roundoff. (Also check if the sum is equal to
- * the previous sum, for machines that round up instead of using exact
- * rounding. Not that this library will work on such machines anyway.
- */
- do {
- lastcheck = check;
- m_epsilon *= half;
- if (every_other) {
- splitter *= 2.0;
- }
- every_other = !every_other;
- check = 1.0 + m_epsilon;
- } while ((check != 1.0) && (check != lastcheck));
- splitter += 1.0;
-
- /* Error bounds for orientation and incircle tests. */
- resulterrbound = (3.0 + 8.0 * m_epsilon) * m_epsilon;
- ccwerrboundA = (3.0 + 16.0 * m_epsilon) * m_epsilon;
- ccwerrboundB = (2.0 + 12.0 * m_epsilon) * m_epsilon;
- ccwerrboundC = (9.0 + 64.0 * m_epsilon) * m_epsilon * m_epsilon;
- o3derrboundA = (7.0 + 56.0 * m_epsilon) * m_epsilon;
- o3derrboundB = (3.0 + 28.0 * m_epsilon) * m_epsilon;
- o3derrboundC = (26.0 + 288.0 * m_epsilon) * m_epsilon * m_epsilon;
- iccerrboundA = (10.0 + 96.0 * m_epsilon) * m_epsilon;
- iccerrboundB = (4.0 + 48.0 * m_epsilon) * m_epsilon;
- iccerrboundC = (44.0 + 576.0 * m_epsilon) * m_epsilon * m_epsilon;
- isperrboundA = (16.0 + 224.0 * m_epsilon) * m_epsilon;
- isperrboundB = (5.0 + 72.0 * m_epsilon) * m_epsilon;
- isperrboundC = (71.0 + 1408.0 * m_epsilon) * m_epsilon * m_epsilon;
-}
-
-/* fast_expansion_sum_zeroelim() Sum two expansions, eliminating zero
- * components from the output expansion.
- *
- * Sets h = e + f. See the long version of my paper for details.
- *
- * If round-to-even is used (as with IEEE 754), maintains the strongly
- * non-overlapping property. (That is, if e is strongly non-overlapping, h
- * will be also.) Does NOT maintain the non-overlapping or non-adjacent
- * properties.
- */
-
-static int fast_expansion_sum_zeroelim(
- int elen, const double *e, int flen, const double *f, double *h) /* h cannot be e or f. */
-{
- double Q;
- INEXACT double Qnew;
- INEXACT double hh;
- INEXACT double bvirt;
- double avirt, bround, around;
- int eindex, findex, hindex;
- double enow, fnow;
-
- enow = e[0];
- fnow = f[0];
- eindex = findex = 0;
- if ((fnow > enow) == (fnow > -enow)) {
- Q = enow;
- enow = e[++eindex];
- }
- else {
- Q = fnow;
- fnow = f[++findex];
- }
- hindex = 0;
- if ((eindex < elen) && (findex < flen)) {
- if ((fnow > enow) == (fnow > -enow)) {
- Fast_Two_Sum(enow, Q, Qnew, hh);
- enow = e[++eindex];
- }
- else {
- Fast_Two_Sum(fnow, Q, Qnew, hh);
- fnow = f[++findex];
- }
- Q = Qnew;
- if (hh != 0.0) {
- h[hindex++] = hh;
- }
- while ((eindex < elen) && (findex < flen)) {
- if ((fnow > enow) == (fnow > -enow)) {
- Two_Sum(Q, enow, Qnew, hh);
- enow = e[++eindex];
- }
- else {
- Two_Sum(Q, fnow, Qnew, hh);
- fnow = f[++findex];
- }
- Q = Qnew;
- if (hh != 0.0) {
- h[hindex++] = hh;
- }
- }
- }
- while (eindex < elen) {
- Two_Sum(Q, enow, Qnew, hh);
- enow = e[++eindex];
- Q = Qnew;
- if (hh != 0.0) {
- h[hindex++] = hh;
- }
- }
- while (findex < flen) {
- Two_Sum(Q, fnow, Qnew, hh);
- fnow = f[++findex];
- Q = Qnew;
- if (hh != 0.0) {
- h[hindex++] = hh;
- }
- }
- if ((Q != 0.0) || (hindex == 0)) {
- h[hindex++] = Q;
- }
- return hindex;
-}
-
-/* scale_expansion_zeroelim() Multiply an expansion by a scalar,
- * eliminating zero components from the
- * output expansion.
- *
- * Sets h = be. See either version of my paper for details.
- *
- * Maintains the nonoverlapping property. If round-to-even is used (as
- * with IEEE 754), maintains the strongly nonoverlapping and nonadjacent
- * properties as well. (That is, if e has one of these properties, so
- * will h.)
- */
-
-static int scale_expansion_zeroelim(int elen,
- const double *e,
- double b,
- double *h) /* e and h cannot be the same. */
-{
- INEXACT double Q, sum;
- double hh;
- INEXACT double product1;
- double product0;
- int eindex, hindex;
- double enow;
- INEXACT double bvirt;
- double avirt, bround, around;
- INEXACT double c;
- INEXACT double abig;
- double ahi, alo, bhi, blo;
- double err1, err2, err3;
-
- Split(b, bhi, blo);
- Two_Product_Presplit(e[0], b, bhi, blo, Q, hh);
- hindex = 0;
- if (hh != 0) {
- h[hindex++] = hh;
- }
- for (eindex = 1; eindex < elen; eindex++) {
- enow = e[eindex];
- Two_Product_Presplit(enow, b, bhi, blo, product1, product0);
- Two_Sum(Q, product0, sum, hh);
- if (hh != 0) {
- h[hindex++] = hh;
- }
- Fast_Two_Sum(product1, sum, Q, hh);
- if (hh != 0) {
- h[hindex++] = hh;
- }
- }
- if ((Q != 0.0) || (hindex == 0)) {
- h[hindex++] = Q;
- }
- return hindex;
-}
-
-/* estimate() Produce a one-word estimate of an expansion's value.
- *
- * See either version of my paper for details.
- */
-
-static double estimate(int elen, const double *e)
-{
- double Q;
- int eindex;
-
- Q = e[0];
- for (eindex = 1; eindex < elen; eindex++) {
- Q += e[eindex];
- }
- return Q;
-}
-
-/* orient2d() Adaptive exact 2D orientation test. Robust.
- *
- * Return a positive value if the points pa, pb, and pc occur
- * in counterclockwise order; a negative value if they occur
- * in clockwise order; and zero if they are collinear. The
- * result is also a rough approximation of twice the signed
- * area of the triangle defined by the three points.
- *
- * This uses exact arithmetic to ensure a correct answer. The
- * result returned is the determinant of a matrix.
- * This determinant is computed adaptively, in the sense that exact
- * arithmetic is used only to the degree it is needed to ensure that the
- * returned value has the correct sign. Hence, orient2d() is usually quite
- * fast, but will run more slowly when the input points are collinear or
- * nearly so.
- */
-
-static double orient2dadapt(const double *pa, const double *pb, const double *pc, double detsum)
-{
- INEXACT double acx, acy, bcx, bcy;
- double acxtail, acytail, bcxtail, bcytail;
- INEXACT double detleft, detright;
- double detlefttail, detrighttail;
- double det, errbound;
- double B[4], C1[8], C2[12], D[16];
- INEXACT double B3;
- int C1length, C2length, Dlength;
- double u[4];
- INEXACT double u3;
- INEXACT double s1, t1;
- double s0, t0;
-
- INEXACT double bvirt;
- double avirt, bround, around;
- INEXACT double c;
- INEXACT double abig;
- double ahi, alo, bhi, blo;
- double err1, err2, err3;
- INEXACT double _i, _j;
- double _0;
-
- acx = (double)(pa[0] - pc[0]);
- bcx = (double)(pb[0] - pc[0]);
- acy = (double)(pa[1] - pc[1]);
- bcy = (double)(pb[1] - pc[1]);
-
- Two_Product(acx, bcy, detleft, detlefttail);
- Two_Product(acy, bcx, detright, detrighttail);
-
- Two_Two_Diff(detleft, detlefttail, detright, detrighttail, B3, B[2], B[1], B[0]);
- B[3] = B3;
-
- det = estimate(4, B);
- errbound = ccwerrboundB * detsum;
- if ((det >= errbound) || (-det >= errbound)) {
- return det;
- }
-
- Two_Diff_Tail(pa[0], pc[0], acx, acxtail);
- Two_Diff_Tail(pb[0], pc[0], bcx, bcxtail);
- Two_Diff_Tail(pa[1], pc[1], acy, acytail);
- Two_Diff_Tail(pb[1], pc[1], bcy, bcytail);
-
- if ((acxtail == 0.0) && (acytail == 0.0) && (bcxtail == 0.0) && (bcytail == 0.0)) {
- return det;
- }
-
- errbound = ccwerrboundC * detsum + resulterrbound * Absolute(det);
- det += (acx * bcytail + bcy * acxtail) - (acy * bcxtail + bcx * acytail);
- if ((det >= errbound) || (-det >= errbound)) {
- return det;
- }
-
- Two_Product(acxtail, bcy, s1, s0);
- Two_Product(acytail, bcx, t1, t0);
- Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]);
- u[3] = u3;
- C1length = fast_expansion_sum_zeroelim(4, B, 4, u, C1);
-
- Two_Product(acx, bcytail, s1, s0);
- Two_Product(acy, bcxtail, t1, t0);
- Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]);
- u[3] = u3;
- C2length = fast_expansion_sum_zeroelim(C1length, C1, 4, u, C2);
-
- Two_Product(acxtail, bcytail, s1, s0);
- Two_Product(acytail, bcxtail, t1, t0);
- Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]);
- u[3] = u3;
- Dlength = fast_expansion_sum_zeroelim(C2length, C2, 4, u, D);
-
- return (D[Dlength - 1]);
-}
-
-static double orient2d(const double *pa, const double *pb, const double *pc)
-{
- double detleft, detright, det;
- double detsum, errbound;
-
- detleft = (pa[0] - pc[0]) * (pb[1] - pc[1]);
- detright = (pa[1] - pc[1]) * (pb[0] - pc[0]);
- det = detleft - detright;
-
- if (detleft > 0.0) {
- if (detright <= 0.0) {
- return det;
- }
- detsum = detleft + detright;
- }
- else if (detleft < 0.0) {
- if (detright >= 0.0) {
- return det;
- }
- detsum = -detleft - detright;
- }
- else {
- return det;
- }
-
- errbound = ccwerrboundA * detsum;
- if ((det >= errbound) || (-det >= errbound)) {
- return det;
- }
-
- return orient2dadapt(pa, pb, pc, detsum);
-}
-
-/* incircle() Adaptive exact 2D incircle test. Robust.
- *
- * Return a positive value if the point pd lies inside the
- * circle passing through pa, pb, and pc; a negative value if
- * it lies outside; and zero if the four points are cocircular.
- * The points pa, pb, and pc must be in counterclockwise
- * order, or the sign of the result will be reversed.
- *
- * This uses exact arithmetic to ensure a correct answer.
- * The result returned is the determinant of a matrix.
- * This determinant is computed adaptively, in the sense that exact
- * arithmetic is used only to the degree it is needed to ensure that the
- * returned value has the correct sign. Hence, incircle() is usually quite
- * fast, but will run more slowly when the input points are cocircular or
- * nearly so.
- *
- * This function is allowed to be long for two reasons. Firstly, it was taken
- * from an external source and only slightly adapted, and keeping its original
- * form will make integration of upstream changes easier. Secondly, it is very
- * sensitive to floating point errors, and refactoring may break it in subtle
- * and hard to detect ways.
- * NOLINTNEXTLINE: readability-function-size */
-static double incircleadapt(
- const double *pa, const double *pb, const double *pc, const double *pd, double permanent)
-{
- INEXACT double adx, bdx, cdx, ady, bdy, cdy;
- double det, errbound;
-
- INEXACT double bdxcdy1, cdxbdy1, cdxady1, adxcdy1, adxbdy1, bdxady1;
- double bdxcdy0, cdxbdy0, cdxady0, adxcdy0, adxbdy0, bdxady0;
- double bc[4], ca[4], ab[4];
- INEXACT double bc3, ca3, ab3;
- double axbc[8], axxbc[16], aybc[8], ayybc[16], adet[32];
- int axbclen, axxbclen, aybclen, ayybclen, alen;
- double bxca[8], bxxca[16], byca[8], byyca[16], bdet[32];
- int bxcalen, bxxcalen, bycalen, byycalen, blen;
- double cxab[8], cxxab[16], cyab[8], cyyab[16], cdet[32];
- int cxablen, cxxablen, cyablen, cyyablen, clen;
- double abdet[64];
- int ablen;
- double fin1[1152], fin2[1152];
- double *finnow, *finother, *finswap;
- int finlength;
-
- double adxtail, bdxtail, cdxtail, adytail, bdytail, cdytail;
- INEXACT double adxadx1, adyady1, bdxbdx1, bdybdy1, cdxcdx1, cdycdy1;
- double adxadx0, adyady0, bdxbdx0, bdybdy0, cdxcdx0, cdycdy0;
- double aa[4], bb[4], cc[4];
- INEXACT double aa3, bb3, cc3;
- INEXACT double ti1, tj1;
- double ti0, tj0;
- double u[4], v[4];
- INEXACT double u3, v3;
- double temp8[8], temp16a[16], temp16b[16], temp16c[16];
- double temp32a[32], temp32b[32], temp48[48], temp64[64];
- int temp8len, temp16alen, temp16blen, temp16clen;
- int temp32alen, temp32blen, temp48len, temp64len;
- double axtbb[8], axtcc[8], aytbb[8], aytcc[8];
- int axtbblen, axtcclen, aytbblen, aytcclen;
- double bxtaa[8], bxtcc[8], bytaa[8], bytcc[8];
- int bxtaalen, bxtcclen, bytaalen, bytcclen;
- double cxtaa[8], cxtbb[8], cytaa[8], cytbb[8];
- int cxtaalen, cxtbblen, cytaalen, cytbblen;
- double axtbc[8], aytbc[8], bxtca[8], bytca[8], cxtab[8], cytab[8];
- int axtbclen, aytbclen, bxtcalen, bytcalen, cxtablen, cytablen;
- double axtbct[16], aytbct[16], bxtcat[16], bytcat[16], cxtabt[16], cytabt[16];
- int axtbctlen, aytbctlen, bxtcatlen, bytcatlen, cxtabtlen, cytabtlen;
- double axtbctt[8], aytbctt[8], bxtcatt[8];
- double bytcatt[8], cxtabtt[8], cytabtt[8];
- int axtbcttlen, aytbcttlen, bxtcattlen, bytcattlen, cxtabttlen, cytabttlen;
- double abt[8], bct[8], cat[8];
- int abtlen, bctlen, catlen;
- double abtt[4], bctt[4], catt[4];
- int abttlen, bcttlen, cattlen;
- INEXACT double abtt3, bctt3, catt3;
- double negate;
-
- INEXACT double bvirt;
- double avirt, bround, around;
- INEXACT double c;
- INEXACT double abig;
- double ahi, alo, bhi, blo;
- double err1, err2, err3;
- INEXACT double _i, _j;
- double _0;
-
- adx = (double)(pa[0] - pd[0]);
- bdx = (double)(pb[0] - pd[0]);
- cdx = (double)(pc[0] - pd[0]);
- ady = (double)(pa[1] - pd[1]);
- bdy = (double)(pb[1] - pd[1]);
- cdy = (double)(pc[1] - pd[1]);
-
- Two_Product(bdx, cdy, bdxcdy1, bdxcdy0);
- Two_Product(cdx, bdy, cdxbdy1, cdxbdy0);
- Two_Two_Diff(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0, bc3, bc[2], bc[1], bc[0]);
- bc[3] = bc3;
- axbclen = scale_expansion_zeroelim(4, bc, adx, axbc);
- axxbclen = scale_expansion_zeroelim(axbclen, axbc, adx, axxbc);
- aybclen = scale_expansion_zeroelim(4, bc, ady, aybc);
- ayybclen = scale_expansion_zeroelim(aybclen, aybc, ady, ayybc);
- alen = fast_expansion_sum_zeroelim(axxbclen, axxbc, ayybclen, ayybc, adet);
-
- Two_Product(cdx, ady, cdxady1, cdxady0);
- Two_Product(adx, cdy, adxcdy1, adxcdy0);
- Two_Two_Diff(cdxady1, cdxady0, adxcdy1, adxcdy0, ca3, ca[2], ca[1], ca[0]);
- ca[3] = ca3;
- bxcalen = scale_expansion_zeroelim(4, ca, bdx, bxca);
- bxxcalen = scale_expansion_zeroelim(bxcalen, bxca, bdx, bxxca);
- bycalen = scale_expansion_zeroelim(4, ca, bdy, byca);
- byycalen = scale_expansion_zeroelim(bycalen, byca, bdy, byyca);
- blen = fast_expansion_sum_zeroelim(bxxcalen, bxxca, byycalen, byyca, bdet);
-
- Two_Product(adx, bdy, adxbdy1, adxbdy0);
- Two_Product(bdx, ady, bdxady1, bdxady0);
- Two_Two_Diff(adxbdy1, adxbdy0, bdxady1, bdxady0, ab3, ab[2], ab[1], ab[0]);
- ab[3] = ab3;
- cxablen = scale_expansion_zeroelim(4, ab, cdx, cxab);
- cxxablen = scale_expansion_zeroelim(cxablen, cxab, cdx, cxxab);
- cyablen = scale_expansion_zeroelim(4, ab, cdy, cyab);
- cyyablen = scale_expansion_zeroelim(cyablen, cyab, cdy, cyyab);
- clen = fast_expansion_sum_zeroelim(cxxablen, cxxab, cyyablen, cyyab, cdet);
-
- ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
- finlength = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, fin1);
-
- det = estimate(finlength, fin1);
- errbound = iccerrboundB * permanent;
- if ((det >= errbound) || (-det >= errbound)) {
- return det;
- }
-
- Two_Diff_Tail(pa[0], pd[0], adx, adxtail);
- Two_Diff_Tail(pa[1], pd[1], ady, adytail);
- Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail);
- Two_Diff_Tail(pb[1], pd[1], bdy, bdytail);
- Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail);
- Two_Diff_Tail(pc[1], pd[1], cdy, cdytail);
- if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0) && (adytail == 0.0) &&
- (bdytail == 0.0) && (cdytail == 0.0)) {
- return det;
- }
-
- errbound = iccerrboundC * permanent + resulterrbound * Absolute(det);
- det += ((adx * adx + ady * ady) *
- ((bdx * cdytail + cdy * bdxtail) - (bdy * cdxtail + cdx * bdytail)) +
- 2.0 * (adx * adxtail + ady * adytail) * (bdx * cdy - bdy * cdx)) +
- ((bdx * bdx + bdy * bdy) *
- ((cdx * adytail + ady * cdxtail) - (cdy * adxtail + adx * cdytail)) +
- 2.0 * (bdx * bdxtail + bdy * bdytail) * (cdx * ady - cdy * adx)) +
- ((cdx * cdx + cdy * cdy) *
- ((adx * bdytail + bdy * adxtail) - (ady * bdxtail + bdx * adytail)) +
- 2.0 * (cdx * cdxtail + cdy * cdytail) * (adx * bdy - ady * bdx));
- if ((det >= errbound) || (-det >= errbound)) {
- return det;
- }
-
- finnow = fin1;
- finother = fin2;
-
- if ((bdxtail != 0.0) || (bdytail != 0.0) || (cdxtail != 0.0) || (cdytail != 0.0)) {
- Square(adx, adxadx1, adxadx0);
- Square(ady, adyady1, adyady0);
- Two_Two_Sum(adxadx1, adxadx0, adyady1, adyady0, aa3, aa[2], aa[1], aa[0]);
- aa[3] = aa3;
- }
- if ((cdxtail != 0.0) || (cdytail != 0.0) || (adxtail != 0.0) || (adytail != 0.0)) {
- Square(bdx, bdxbdx1, bdxbdx0);
- Square(bdy, bdybdy1, bdybdy0);
- Two_Two_Sum(bdxbdx1, bdxbdx0, bdybdy1, bdybdy0, bb3, bb[2], bb[1], bb[0]);
- bb[3] = bb3;
- }
- if ((adxtail != 0.0) || (adytail != 0.0) || (bdxtail != 0.0) || (bdytail != 0.0)) {
- Square(cdx, cdxcdx1, cdxcdx0);
- Square(cdy, cdycdy1, cdycdy0);
- Two_Two_Sum(cdxcdx1, cdxcdx0, cdycdy1, cdycdy0, cc3, cc[2], cc[1], cc[0]);
- cc[3] = cc3;
- }
-
- if (adxtail != 0.0) {
- axtbclen = scale_expansion_zeroelim(4, bc, adxtail, axtbc);
- temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, 2.0 * adx, temp16a);
-
- axtcclen = scale_expansion_zeroelim(4, cc, adxtail, axtcc);
- temp16blen = scale_expansion_zeroelim(axtcclen, axtcc, bdy, temp16b);
-
- axtbblen = scale_expansion_zeroelim(4, bb, adxtail, axtbb);
- temp16clen = scale_expansion_zeroelim(axtbblen, axtbb, -cdy, temp16c);
-
- temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a);
- temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- }
- if (adytail != 0.0) {
- aytbclen = scale_expansion_zeroelim(4, bc, adytail, aytbc);
- temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, 2.0 * ady, temp16a);
-
- aytbblen = scale_expansion_zeroelim(4, bb, adytail, aytbb);
- temp16blen = scale_expansion_zeroelim(aytbblen, aytbb, cdx, temp16b);
-
- aytcclen = scale_expansion_zeroelim(4, cc, adytail, aytcc);
- temp16clen = scale_expansion_zeroelim(aytcclen, aytcc, -bdx, temp16c);
-
- temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a);
- temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- }
- if (bdxtail != 0.0) {
- bxtcalen = scale_expansion_zeroelim(4, ca, bdxtail, bxtca);
- temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, 2.0 * bdx, temp16a);
-
- bxtaalen = scale_expansion_zeroelim(4, aa, bdxtail, bxtaa);
- temp16blen = scale_expansion_zeroelim(bxtaalen, bxtaa, cdy, temp16b);
-
- bxtcclen = scale_expansion_zeroelim(4, cc, bdxtail, bxtcc);
- temp16clen = scale_expansion_zeroelim(bxtcclen, bxtcc, -ady, temp16c);
-
- temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a);
- temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- }
- if (bdytail != 0.0) {
- bytcalen = scale_expansion_zeroelim(4, ca, bdytail, bytca);
- temp16alen = scale_expansion_zeroelim(bytcalen, bytca, 2.0 * bdy, temp16a);
-
- bytcclen = scale_expansion_zeroelim(4, cc, bdytail, bytcc);
- temp16blen = scale_expansion_zeroelim(bytcclen, bytcc, adx, temp16b);
-
- bytaalen = scale_expansion_zeroelim(4, aa, bdytail, bytaa);
- temp16clen = scale_expansion_zeroelim(bytaalen, bytaa, -cdx, temp16c);
-
- temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a);
- temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- }
- if (cdxtail != 0.0) {
- cxtablen = scale_expansion_zeroelim(4, ab, cdxtail, cxtab);
- temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, 2.0 * cdx, temp16a);
-
- cxtbblen = scale_expansion_zeroelim(4, bb, cdxtail, cxtbb);
- temp16blen = scale_expansion_zeroelim(cxtbblen, cxtbb, ady, temp16b);
-
- cxtaalen = scale_expansion_zeroelim(4, aa, cdxtail, cxtaa);
- temp16clen = scale_expansion_zeroelim(cxtaalen, cxtaa, -bdy, temp16c);
-
- temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a);
- temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- }
- if (cdytail != 0.0) {
- cytablen = scale_expansion_zeroelim(4, ab, cdytail, cytab);
- temp16alen = scale_expansion_zeroelim(cytablen, cytab, 2.0 * cdy, temp16a);
-
- cytaalen = scale_expansion_zeroelim(4, aa, cdytail, cytaa);
- temp16blen = scale_expansion_zeroelim(cytaalen, cytaa, bdx, temp16b);
-
- cytbblen = scale_expansion_zeroelim(4, bb, cdytail, cytbb);
- temp16clen = scale_expansion_zeroelim(cytbblen, cytbb, -adx, temp16c);
-
- temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a);
- temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- }
-
- if ((adxtail != 0.0) || (adytail != 0.0)) {
- if ((bdxtail != 0.0) || (bdytail != 0.0) || (cdxtail != 0.0) || (cdytail != 0.0)) {
- Two_Product(bdxtail, cdy, ti1, ti0);
- Two_Product(bdx, cdytail, tj1, tj0);
- Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]);
- u[3] = u3;
- negate = -bdy;
- Two_Product(cdxtail, negate, ti1, ti0);
- negate = -bdytail;
- Two_Product(cdx, negate, tj1, tj0);
- Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]);
- v[3] = v3;
- bctlen = fast_expansion_sum_zeroelim(4, u, 4, v, bct);
-
- Two_Product(bdxtail, cdytail, ti1, ti0);
- Two_Product(cdxtail, bdytail, tj1, tj0);
- Two_Two_Diff(ti1, ti0, tj1, tj0, bctt3, bctt[2], bctt[1], bctt[0]);
- bctt[3] = bctt3;
- bcttlen = 4;
- }
- else {
- bct[0] = 0.0;
- bctlen = 1;
- bctt[0] = 0.0;
- bcttlen = 1;
- }
-
- if (adxtail != 0.0) {
- temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, adxtail, temp16a);
- axtbctlen = scale_expansion_zeroelim(bctlen, bct, adxtail, axtbct);
- temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, 2.0 * adx, temp32a);
- temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- if (bdytail != 0.0) {
- temp8len = scale_expansion_zeroelim(4, cc, adxtail, temp8);
- temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail, temp16a);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- }
- if (cdytail != 0.0) {
- temp8len = scale_expansion_zeroelim(4, bb, -adxtail, temp8);
- temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail, temp16a);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- }
-
- temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, adxtail, temp32a);
- axtbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adxtail, axtbctt);
- temp16alen = scale_expansion_zeroelim(axtbcttlen, axtbctt, 2.0 * adx, temp16a);
- temp16blen = scale_expansion_zeroelim(axtbcttlen, axtbctt, adxtail, temp16b);
- temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b);
- temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- }
- if (adytail != 0.0) {
- temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, adytail, temp16a);
- aytbctlen = scale_expansion_zeroelim(bctlen, bct, adytail, aytbct);
- temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, 2.0 * ady, temp32a);
- temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
-
- temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, adytail, temp32a);
- aytbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adytail, aytbctt);
- temp16alen = scale_expansion_zeroelim(aytbcttlen, aytbctt, 2.0 * ady, temp16a);
- temp16blen = scale_expansion_zeroelim(aytbcttlen, aytbctt, adytail, temp16b);
- temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b);
- temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- }
- }
- if ((bdxtail != 0.0) || (bdytail != 0.0)) {
- if ((cdxtail != 0.0) || (cdytail != 0.0) || (adxtail != 0.0) || (adytail != 0.0)) {
- Two_Product(cdxtail, ady, ti1, ti0);
- Two_Product(cdx, adytail, tj1, tj0);
- Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]);
- u[3] = u3;
- negate = -cdy;
- Two_Product(adxtail, negate, ti1, ti0);
- negate = -cdytail;
- Two_Product(adx, negate, tj1, tj0);
- Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]);
- v[3] = v3;
- catlen = fast_expansion_sum_zeroelim(4, u, 4, v, cat);
-
- Two_Product(cdxtail, adytail, ti1, ti0);
- Two_Product(adxtail, cdytail, tj1, tj0);
- Two_Two_Diff(ti1, ti0, tj1, tj0, catt3, catt[2], catt[1], catt[0]);
- catt[3] = catt3;
- cattlen = 4;
- }
- else {
- cat[0] = 0.0;
- catlen = 1;
- catt[0] = 0.0;
- cattlen = 1;
- }
-
- if (bdxtail != 0.0) {
- temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, bdxtail, temp16a);
- bxtcatlen = scale_expansion_zeroelim(catlen, cat, bdxtail, bxtcat);
- temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, 2.0 * bdx, temp32a);
- temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- if (cdytail != 0.0) {
- temp8len = scale_expansion_zeroelim(4, aa, bdxtail, temp8);
- temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail, temp16a);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- }
- if (adytail != 0.0) {
- temp8len = scale_expansion_zeroelim(4, cc, -bdxtail, temp8);
- temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail, temp16a);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- }
-
- temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, bdxtail, temp32a);
- bxtcattlen = scale_expansion_zeroelim(cattlen, catt, bdxtail, bxtcatt);
- temp16alen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, 2.0 * bdx, temp16a);
- temp16blen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, bdxtail, temp16b);
- temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b);
- temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- }
- if (bdytail != 0.0) {
- temp16alen = scale_expansion_zeroelim(bytcalen, bytca, bdytail, temp16a);
- bytcatlen = scale_expansion_zeroelim(catlen, cat, bdytail, bytcat);
- temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, 2.0 * bdy, temp32a);
- temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
-
- temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, bdytail, temp32a);
- bytcattlen = scale_expansion_zeroelim(cattlen, catt, bdytail, bytcatt);
- temp16alen = scale_expansion_zeroelim(bytcattlen, bytcatt, 2.0 * bdy, temp16a);
- temp16blen = scale_expansion_zeroelim(bytcattlen, bytcatt, bdytail, temp16b);
- temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b);
- temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- }
- }
- if ((cdxtail != 0.0) || (cdytail != 0.0)) {
- if ((adxtail != 0.0) || (adytail != 0.0) || (bdxtail != 0.0) || (bdytail != 0.0)) {
- Two_Product(adxtail, bdy, ti1, ti0);
- Two_Product(adx, bdytail, tj1, tj0);
- Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]);
- u[3] = u3;
- negate = -ady;
- Two_Product(bdxtail, negate, ti1, ti0);
- negate = -adytail;
- Two_Product(bdx, negate, tj1, tj0);
- Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]);
- v[3] = v3;
- abtlen = fast_expansion_sum_zeroelim(4, u, 4, v, abt);
-
- Two_Product(adxtail, bdytail, ti1, ti0);
- Two_Product(bdxtail, adytail, tj1, tj0);
- Two_Two_Diff(ti1, ti0, tj1, tj0, abtt3, abtt[2], abtt[1], abtt[0]);
- abtt[3] = abtt3;
- abttlen = 4;
- }
- else {
- abt[0] = 0.0;
- abtlen = 1;
- abtt[0] = 0.0;
- abttlen = 1;
- }
-
- if (cdxtail != 0.0) {
- temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, cdxtail, temp16a);
- cxtabtlen = scale_expansion_zeroelim(abtlen, abt, cdxtail, cxtabt);
- temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, 2.0 * cdx, temp32a);
- temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- if (adytail != 0.0) {
- temp8len = scale_expansion_zeroelim(4, bb, cdxtail, temp8);
- temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail, temp16a);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- }
- if (bdytail != 0.0) {
- temp8len = scale_expansion_zeroelim(4, aa, -cdxtail, temp8);
- temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail, temp16a);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- }
-
- temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, cdxtail, temp32a);
- cxtabttlen = scale_expansion_zeroelim(abttlen, abtt, cdxtail, cxtabtt);
- temp16alen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, 2.0 * cdx, temp16a);
- temp16blen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, cdxtail, temp16b);
- temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b);
- temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- }
- if (cdytail != 0.0) {
- temp16alen = scale_expansion_zeroelim(cytablen, cytab, cdytail, temp16a);
- cytabtlen = scale_expansion_zeroelim(abtlen, abt, cdytail, cytabt);
- temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, 2.0 * cdy, temp32a);
- temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
-
- temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, cdytail, temp32a);
- cytabttlen = scale_expansion_zeroelim(abttlen, abtt, cdytail, cytabtt);
- temp16alen = scale_expansion_zeroelim(cytabttlen, cytabtt, 2.0 * cdy, temp16a);
- temp16blen = scale_expansion_zeroelim(cytabttlen, cytabtt, cdytail, temp16b);
- temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b);
- temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- }
- }
-
- return finnow[finlength - 1];
-}
-
-static double incircle(const double *pa, const double *pb, const double *pc, const double *pd)
-{
- double adx, bdx, cdx, ady, bdy, cdy;
- double bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady;
- double alift, blift, clift;
- double det;
- double permanent, errbound;
-
- adx = pa[0] - pd[0];
- bdx = pb[0] - pd[0];
- cdx = pc[0] - pd[0];
- ady = pa[1] - pd[1];
- bdy = pb[1] - pd[1];
- cdy = pc[1] - pd[1];
-
- bdxcdy = bdx * cdy;
- cdxbdy = cdx * bdy;
- alift = adx * adx + ady * ady;
-
- cdxady = cdx * ady;
- adxcdy = adx * cdy;
- blift = bdx * bdx + bdy * bdy;
-
- adxbdy = adx * bdy;
- bdxady = bdx * ady;
- clift = cdx * cdx + cdy * cdy;
-
- det = alift * (bdxcdy - cdxbdy) + blift * (cdxady - adxcdy) + clift * (adxbdy - bdxady);
-
- permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * alift +
- (Absolute(cdxady) + Absolute(adxcdy)) * blift +
- (Absolute(adxbdy) + Absolute(bdxady)) * clift;
- errbound = iccerrboundA * permanent;
- if ((det > errbound) || (-det > errbound)) {
- return det;
- }
-
- return incircleadapt(pa, pb, pc, pd, permanent);
-}
diff --git a/source/blender/blenlib/intern/delaunay_2d.cc b/source/blender/blenlib/intern/delaunay_2d.cc
new file mode 100644
index 00000000000..7b0f6a658ce
--- /dev/null
+++ b/source/blender/blenlib/intern/delaunay_2d.cc
@@ -0,0 +1,2500 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup bli
+ */
+
+#include <algorithm>
+#include <fstream>
+#include <iostream>
+#include <sstream>
+
+#include "BLI_array.hh"
+#include "BLI_double2.hh"
+#include "BLI_linklist.h"
+#include "BLI_math_boolean.hh"
+#include "BLI_math_mpq.hh"
+#include "BLI_mpq2.hh"
+#include "BLI_vector.hh"
+
+#include "BLI_delaunay_2d.h"
+
+namespace blender::meshintersect {
+
+/* Throughout this file, template argument T will be an
+ * arithmetic-like type, like float, double, or mpq_class. */
+
+template<typename T> T math_abs(const T v)
+{
+ return (v < 0) ? -v : v;
+}
+
+#ifdef WITH_GMP
+template<> mpq_class math_abs<mpq_class>(const mpq_class v)
+{
+ return abs(v);
+}
+#endif
+
+template<> double math_abs<double>(const double v)
+{
+ return fabs(v);
+}
+
+template<typename T> double math_to_double(const T UNUSED(v))
+{
+ BLI_assert(false); /* Need implementation for other type. */
+ return 0.0;
+}
+
+#ifdef WITH_GMP
+template<> double math_to_double<mpq_class>(const mpq_class v)
+{
+ return v.get_d();
+}
+#endif
+
+template<> double math_to_double<double>(const double v)
+{
+ return v;
+}
+
+/**
+ * Define a templated 2D arrangement of vertices, edges, and faces.
+ * The #SymEdge data structure is the basis for a structure that allows
+ * easy traversal to neighboring (by topology) geometric elements.
+ * Each of #CDTVert, #CDTEdge, and #CDTFace have an input_id linked list,
+ * whose nodes contain integers that keep track of which input verts, edges,
+ * and faces, respectively, that the element was derived from.
+ *
+ * While this could be cleaned up some, it is usable by other routines in Blender
+ * that need to keep track of a 2D arrangement, with topology.
+ */
+template<typename Arith_t> struct CDTVert;
+template<typename Arith_t> struct CDTEdge;
+template<typename Arith_t> struct CDTFace;
+
+template<typename Arith_t> struct SymEdge {
+ /** Next #SymEdge in face, doing CCW traversal of face. */
+ SymEdge<Arith_t> *next{nullptr};
+ /** Next #SymEdge CCW around vert. */
+ SymEdge<Arith_t> *rot{nullptr};
+ /** Vert at origin. */
+ CDTVert<Arith_t> *vert{nullptr};
+ /** Un-directed edge this is for. */
+ CDTEdge<Arith_t> *edge{nullptr};
+ /** Face on left side. */
+ CDTFace<Arith_t> *face{nullptr};
+
+ SymEdge() = default;
+};
+
+/**
+ * Return other #SymEdge for same #CDTEdge as \a se.
+ */
+template<typename T> inline SymEdge<T> *sym(const SymEdge<T> *se)
+{
+ return se->next->rot;
+}
+
+/** Return #SymEdge whose next is \a se. */
+template<typename T> inline SymEdge<T> *prev(const SymEdge<T> *se)
+{
+ return se->rot->next->rot;
+}
+
+template<typename Arith_t> struct CDTVert {
+ /** Coordinate. */
+ vec2<Arith_t> co;
+ /** Some edge attached to it. */
+ SymEdge<Arith_t> *symedge{nullptr};
+ /** List of corresponding vertex input ids. */
+ LinkNode *input_ids{nullptr};
+ /** Index into array that #CDTArrangement keeps. */
+ int index{-1};
+ /** Index of a CDTVert that this has merged to. -1 if no merge. */
+ int merge_to_index{-1};
+ /** Used by algorithms operating on CDT structures. */
+ int visit_index{0};
+
+ CDTVert() = default;
+ explicit CDTVert(const vec2<Arith_t> &pt);
+};
+
+template<typename Arith_t> struct CDTEdge {
+ /** List of input edge ids that this is part of. */
+ LinkNode *input_ids{nullptr};
+ /** The directed edges for this edge. */
+ SymEdge<Arith_t> symedges[2]{SymEdge<Arith_t>(), SymEdge<Arith_t>()};
+
+ CDTEdge() = default;
+};
+
+template<typename Arith_t> struct CDTFace {
+ /** A symedge in face; only used during output, so only valid then. */
+ SymEdge<Arith_t> *symedge{nullptr};
+ /** List of input face ids that this is part of. */
+ LinkNode *input_ids{nullptr};
+ /** Used by algorithms operating on CDT structures. */
+ int visit_index{0};
+ /** Marks this face no longer used. */
+ bool deleted{false};
+
+ CDTFace() = default;
+};
+
+template<typename Arith_t> struct CDTArrangement {
+ /* The arrangement owns the memory pointed to by the pointers in these vectors.
+ * They are pointers instead of actual structures because these vectors may be resized and
+ * other elements refer to the elements by pointer. */
+
+ /** The verts. Some may be merged to others (see their merge_to_index). */
+ Vector<CDTVert<Arith_t> *> verts;
+ /** The edges. Some may be deleted (SymEdge next and rot pointers are null). */
+ Vector<CDTEdge<Arith_t> *> edges;
+ /** The faces. Some may be deleted (see their delete member). */
+ Vector<CDTFace<Arith_t> *> faces;
+ /** Which CDTFace is the outer face. */
+ CDTFace<Arith_t> *outer_face{nullptr};
+
+ CDTArrangement() = default;
+ ~CDTArrangement();
+
+ /** Hint to how much space to reserve in the Vectors of the arrangement,
+ * based on these counts of input elements. */
+ void reserve(int num_verts, int num_edges, int num_faces);
+
+ /**
+ * Add a new vertex to the arrangement, with the given 2D coordinate.
+ * It will not be connected to anything yet.
+ */
+ CDTVert<Arith_t> *add_vert(const vec2<Arith_t> &pt);
+
+ /**
+ * Add an edge from v1 to v2. The edge will have a left face and a right face,
+ * specified by \a fleft and \a fright. The edge will not be connected to anything yet.
+ * If the vertices do not yet have a #SymEdge pointer,
+ * their pointer is set to the #SymEdge in this new edge.
+ */
+ CDTEdge<Arith_t> *add_edge(CDTVert<Arith_t> *v1,
+ CDTVert<Arith_t> *v2,
+ CDTFace<Arith_t> *fleft,
+ CDTFace<Arith_t> *fright);
+
+ /**
+ * Add a new face. It is disconnected until an add_edge makes it the
+ * left or right face of an edge.
+ */
+ CDTFace<Arith_t> *add_face();
+
+ /** Make a new edge from v to se->vert, splicing it in. */
+ CDTEdge<Arith_t> *add_vert_to_symedge_edge(CDTVert<Arith_t> *v, SymEdge<Arith_t> *se);
+
+ /**
+ * Assuming s1 and s2 are both #SymEdge's in a face with > 3 sides and one is not the next of the
+ * other, Add an edge from `s1->v` to `s2->v`, splitting the face in two. The original face will
+ * be the one that s1 has as left face, and a new face will be added and made s2 and its
+ * next-cycle's left face.
+ */
+ CDTEdge<Arith_t> *add_diagonal(SymEdge<Arith_t> *s1, SymEdge<Arith_t> *s2);
+
+ /**
+ * Connect the verts of se1 and se2, assuming that currently those two #SymEdge's are on the
+ * outer boundary (have face == outer_face) of two components that are isolated from each other.
+ */
+ CDTEdge<Arith_t> *connect_separate_parts(SymEdge<Arith_t> *se1, SymEdge<Arith_t> *se2);
+
+ /**
+ * Split se at fraction lambda, and return the new #CDTEdge that is the new second half.
+ * Copy the edge input_ids into the new one.
+ */
+ CDTEdge<Arith_t> *split_edge(SymEdge<Arith_t> *se, Arith_t lambda);
+
+ /**
+ * Delete an edge. The new combined face on either side of the deleted edge will be the one that
+ * was e's face. There will now be an unused face, which will be marked deleted, and an unused
+ * #CDTEdge, marked by setting the next and rot pointers of its #SymEdge's to #nullptr.
+ */
+ void delete_edge(SymEdge<Arith_t> *se);
+
+ /**
+ * If the vertex with index i in the vert array has not been merge, return it.
+ * Else return the one that it has merged to.
+ */
+ CDTVert<Arith_t> *get_vert_resolve_merge(int i)
+ {
+ CDTVert<Arith_t> *v = this->verts[i];
+ if (v->merge_to_index != -1) {
+ v = this->verts[v->merge_to_index];
+ }
+ return v;
+ }
+};
+
+template<typename T> class CDT_state {
+ public:
+ CDTArrangement<T> cdt;
+ /** How many verts were in input (will be first in vert_array). */
+ int input_vert_tot;
+ /** Used for visiting things without having to initialized their visit fields. */
+ int visit_count;
+ /**
+ * Edge ids for face start with this, and each face gets this much index space
+ * to encode positions within the face.
+ */
+ int face_edge_offset;
+ /** How close before coords considered equal. */
+ T epsilon;
+
+ explicit CDT_state(int num_input_verts, int num_input_edges, int num_input_faces, T epsilon);
+ ~CDT_state()
+ {
+ }
+};
+
+template<typename T> CDTArrangement<T>::~CDTArrangement()
+{
+ for (int i : this->verts.index_range()) {
+ CDTVert<T> *v = this->verts[i];
+ BLI_linklist_free(v->input_ids, nullptr);
+ delete v;
+ this->verts[i] = nullptr;
+ }
+ for (int i : this->edges.index_range()) {
+ CDTEdge<T> *e = this->edges[i];
+ BLI_linklist_free(e->input_ids, nullptr);
+ delete e;
+ this->edges[i] = nullptr;
+ }
+ for (int i : this->faces.index_range()) {
+ CDTFace<T> *f = this->faces[i];
+ BLI_linklist_free(f->input_ids, nullptr);
+ delete f;
+ this->faces[i] = nullptr;
+ }
+}
+
+#define DEBUG_CDT
+#ifdef DEBUG_CDT
+/* Some functions to aid in debugging. */
+template<typename T> std::string vertname(const CDTVert<T> *v)
+{
+ std::stringstream ss;
+ ss << "[" << v->index << "]";
+ return ss.str();
+}
+
+/* Abbreviated pointer value is easier to read in dumps. */
+static std::string trunc_ptr(const void *p)
+{
+ constexpr int TRUNC_PTR_MASK = 0xFFFF;
+ std::stringstream ss;
+ ss << std::hex << (POINTER_AS_INT(p) & TRUNC_PTR_MASK);
+ return ss.str();
+}
+
+template<typename T> std::string sename(const SymEdge<T> *se)
+{
+ std::stringstream ss;
+ ss << "{" << trunc_ptr(se) << "}";
+ return ss.str();
+}
+
+template<typename T> std::ostream &operator<<(std::ostream &os, const SymEdge<T> &se)
+{
+ if (se.next) {
+ os << vertname(se.vert) << "(" << se.vert->co << "->" << se.next->vert->co << ")"
+ << vertname(se.next->vert);
+ }
+ else {
+ os << vertname(se.vert) << "(" << se.vert->co << "->NULL)";
+ }
+ return os;
+}
+
+template<typename T> std::ostream &operator<<(std::ostream &os, const SymEdge<T> *se)
+{
+ os << *se;
+ return os;
+}
+
+template<typename T> std::string short_se_dump(const SymEdge<T> *se)
+{
+ if (se == nullptr) {
+ return std::string("NULL");
+ }
+ return vertname(se->vert) +
+ (se->next == nullptr ? std::string("[NULL]") : vertname(se->next->vert));
+}
+
+template<typename T> std::ostream &operator<<(std::ostream &os, const CDT_state<T> &cdt_state)
+{
+ os << "\nCDT\n\nVERTS\n";
+ for (const CDTVert<T> *v : cdt_state.cdt.verts) {
+ os << vertname(v) << " " << trunc_ptr(v) << ": " << v->co
+ << " symedge=" << trunc_ptr(v->symedge);
+ if (v->merge_to_index == -1) {
+ os << "\n";
+ }
+ else {
+ os << " merge to " << vertname(cdt_state.cdt.verts[v->merge_to_index]) << "\n";
+ }
+ const SymEdge<T> *se = v->symedge;
+ int cnt = 0;
+ constexpr int print_count_limit = 25;
+ if (se) {
+ os << " edges out:\n";
+ do {
+ if (se->next == NULL) {
+ os << " [NULL] next/rot symedge, se=" << trunc_ptr(se) << "\n";
+ break;
+ }
+ if (se->next->next == NULL) {
+ os << " [NULL] next-next/rot symedge, se=" << trunc_ptr(se) << "\n";
+ break;
+ }
+ const CDTVert<T> *vother = sym(se)->vert;
+ os << " " << vertname(vother) << "(e=" << trunc_ptr(se->edge)
+ << ", se=" << trunc_ptr(se) << ")\n";
+ se = se->rot;
+ cnt++;
+ } while (se != v->symedge && cnt < print_count_limit);
+ os << "\n";
+ }
+ }
+ os << "\nEDGES\n";
+ for (const CDTEdge<T> *e : cdt_state.cdt.edges) {
+ if (e->symedges[0].next == nullptr) {
+ continue;
+ }
+ os << trunc_ptr(&e) << ":\n";
+ for (int i = 0; i < 2; ++i) {
+ const SymEdge<T> *se = &e->symedges[i];
+ os << " se[" << i << "] @" << trunc_ptr(se) << " next=" << trunc_ptr(se->next)
+ << ", rot=" << trunc_ptr(se->rot) << ", vert=" << trunc_ptr(se->vert) << " "
+ << vertname(se->vert) << " " << se->vert->co << ", edge=" << trunc_ptr(se->edge)
+ << ", face=" << trunc_ptr(se->face) << "\n";
+ }
+ }
+ os << "\nFACES\n";
+ os << "outer_face=" << trunc_ptr(cdt_state.cdt.outer_face) << "\n";
+ /* Only after prepare_output do faces have non-null symedges. */
+ if (cdt_state.cdt.outer_face->symedge != nullptr) {
+ for (const CDTFace<T> *f : cdt_state.cdt.faces) {
+ if (!f->deleted) {
+ os << trunc_ptr(f) << " symedge=" << trunc_ptr(f->symedge) << "\n";
+ }
+ }
+ }
+ return os;
+}
+
+template<typename T> void cdt_draw(const std::string &label, const CDTArrangement<T> &cdt)
+{
+ static bool append = false; /* Will be set to true after first call. */
+
+/* Would like to use #BKE_tempdir_base() here, but that brings in dependence on kernel library.
+ * This is just for developer debugging anyway, and should never be called in production Blender.
+ */
+# ifdef _WIN32
+ const char *drawfile = "./debug_draw.html";
+# else
+ const char *drawfile = "/tmp/debug_draw.html";
+# endif
+ constexpr int max_draw_width = 1800;
+ constexpr int max_draw_height = 1600;
+ constexpr double margin_expand = 0.05;
+ constexpr int thin_line = 1;
+ constexpr int thick_line = 4;
+ constexpr int vert_radius = 3;
+ constexpr bool draw_vert_labels = true;
+ constexpr bool draw_edge_labels = false;
+
+ if (cdt.verts.size() == 0) {
+ return;
+ }
+ vec2<double> vmin(DBL_MAX, DBL_MAX);
+ vec2<double> vmax(-DBL_MAX, -DBL_MAX);
+ for (const CDTVert<T> *v : cdt.verts) {
+ for (int i = 0; i < 2; ++i) {
+ double dvi = math_to_double(v->co[i]);
+ if (dvi < vmin[i]) {
+ vmin[i] = dvi;
+ }
+ if (dvi > vmax[i]) {
+ vmax[i] = dvi;
+ }
+ }
+ }
+ double draw_margin = ((vmax.x - vmin.x) + (vmax.y - vmin.y)) * margin_expand;
+ double minx = vmin.x - draw_margin;
+ double maxx = vmax.x + draw_margin;
+ double miny = vmin.y - draw_margin;
+ double maxy = vmax.y + draw_margin;
+
+ double width = maxx - minx;
+ double height = maxy - miny;
+ double aspect = height / width;
+ int view_width = max_draw_width;
+ int view_height = static_cast<int>(view_width * aspect);
+ if (view_height > max_draw_height) {
+ view_height = max_draw_height;
+ view_width = static_cast<int>(view_height / aspect);
+ }
+ double scale = view_width / width;
+
+# define SX(x) ((math_to_double(x) - minx) * scale)
+# define SY(y) ((maxy - math_to_double(y)) * scale)
+
+ std::ofstream f;
+ if (append) {
+ f.open(drawfile, std::ios_base::app);
+ }
+ else {
+ f.open(drawfile);
+ }
+ if (!f) {
+ std::cout << "Could not open file " << drawfile << "\n";
+ return;
+ }
+
+ f << "<div>" << label << "</div>\n<div>\n"
+ << "<svg version=\"1.1\" "
+ "xmlns=\"http://www.w3.org/2000/svg\" "
+ "xmlns:xlink=\"http://www.w3.org/1999/xlink\" "
+ "xml:space=\"preserve\"\n"
+ << "width=\"" << view_width << "\" height=\"" << view_height << "\">n";
+
+ for (const CDTEdge<T> *e : cdt.edges) {
+ if (e->symedges[0].next == nullptr) {
+ continue;
+ }
+ const CDTVert<T> *u = e->symedges[0].vert;
+ const CDTVert<T> *v = e->symedges[1].vert;
+ const vec2<T> &uco = u->co;
+ const vec2<T> &vco = v->co;
+ int strokew = e->input_ids == nullptr ? thin_line : thick_line;
+ f << "<line fill=\"none\" stroke=\"black\" stroke-width=\"" << strokew << "\" x1=\""
+ << SX(uco[0]) << "\" y1=\"" << SY(uco[1]) << "\" x2=\"" << SX(vco[0]) << "\" y2=\""
+ << SY(vco[1]) << "\">\n";
+ f << " <title>" << vertname(u) << vertname(v) << "</title>\n";
+ f << "</line>\n";
+ if (draw_edge_labels) {
+ f << "<text x=\"" << SX((uco[0] + vco[0]) / 2) << "\" y=\"" << SY((uco[1] + vco[1]) / 2)
+ << "\" font-size=\"small\">";
+ f << vertname(u) << vertname(v) << sename(&e->symedges[0]) << sename(&e->symedges[1])
+ << "</text>\n";
+ }
+ }
+
+ int i = 0;
+ for (const CDTVert<T> *v : cdt.verts) {
+ f << "<circle fill=\"black\" cx=\"" << SX(v->co[0]) << "\" cy=\"" << SY(v->co[1]) << "\" r=\""
+ << vert_radius << "\">\n";
+ f << " <title>[" << i << "]" << v->co << "</title>\n";
+ f << "</circle>\n";
+ if (draw_vert_labels) {
+ f << "<text x=\"" << SX(v->co[0]) + vert_radius << "\" y=\"" << SY(v->co[1]) - vert_radius
+ << "\" font-size=\"small\">[" << i << "]</text>\n";
+ }
+ ++i;
+ }
+
+ append = true;
+# undef SX
+# undef SY
+}
+#endif
+
+/**
+ * Return true if `a -- b -- c` are in that order, assuming they are on a straight line according
+ * to #orient2d and we know the order is either `abc` or `bac`.
+ * This means `ab . ac` and `bc . ac` must both be non-negative.
+ */
+template<typename T> bool in_line(const vec2<T> &a, const vec2<T> &b, const vec2<T> &c)
+{
+ vec2<T> ab = b - a;
+ vec2<T> bc = c - b;
+ vec2<T> ac = c - a;
+ if (vec2<T>::dot(ab, ac) < 0) {
+ return false;
+ }
+ return vec2<T>::dot(bc, ac) >= 0;
+}
+
+template<typename T> CDTVert<T>::CDTVert(const vec2<T> &pt)
+{
+ this->co = pt;
+ this->input_ids = nullptr;
+ this->symedge = nullptr;
+ this->index = -1;
+ this->merge_to_index = -1;
+ this->visit_index = 0;
+}
+
+template<typename T> CDTVert<T> *CDTArrangement<T>::add_vert(const vec2<T> &pt)
+{
+ CDTVert<T> *v = new CDTVert<T>(pt);
+ int index = this->verts.append_and_get_index(v);
+ v->index = index;
+ return v;
+}
+
+template<typename T>
+CDTEdge<T> *CDTArrangement<T>::add_edge(CDTVert<T> *v1,
+ CDTVert<T> *v2,
+ CDTFace<T> *fleft,
+ CDTFace<T> *fright)
+{
+ CDTEdge<T> *e = new CDTEdge<T>();
+ this->edges.append(e);
+ SymEdge<T> *se = &e->symedges[0];
+ SymEdge<T> *sesym = &e->symedges[1];
+ se->edge = sesym->edge = e;
+ se->face = fleft;
+ sesym->face = fright;
+ se->vert = v1;
+ if (v1->symedge == nullptr) {
+ v1->symedge = se;
+ }
+ sesym->vert = v2;
+ if (v2->symedge == nullptr) {
+ v2->symedge = sesym;
+ }
+ se->next = sesym->next = se->rot = sesym->rot = nullptr;
+ return e;
+}
+
+template<typename T> CDTFace<T> *CDTArrangement<T>::add_face()
+{
+ CDTFace<T> *f = new CDTFace<T>();
+ this->faces.append(f);
+ return f;
+}
+
+template<typename T> void CDTArrangement<T>::reserve(int num_verts, int num_edges, int num_faces)
+{
+ /* These reserves are just guesses; OK if they aren't exactly right since vectors will resize. */
+ this->verts.reserve(2 * num_verts);
+ this->edges.reserve(3 * num_verts + 2 * num_edges + 3 * 2 * num_faces);
+ this->faces.reserve(2 * num_verts + 2 * num_edges + 2 * num_faces);
+}
+
+template<typename T>
+CDT_state<T>::CDT_state(int num_input_verts, int num_input_edges, int num_input_faces, T epsilon)
+{
+ this->input_vert_tot = num_input_verts;
+ this->cdt.reserve(num_input_verts, num_input_edges, num_input_faces);
+ this->cdt.outer_face = this->cdt.add_face();
+ this->epsilon = epsilon;
+ this->visit_count = 0;
+}
+
+static bool id_in_list(const LinkNode *id_list, int id)
+{
+ const LinkNode *ln;
+
+ for (ln = id_list; ln != nullptr; ln = ln->next) {
+ if (POINTER_AS_INT(ln->link) == id) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/* Is any id in (range_start, range_start+1, ... , range_end) in id_list? */
+static bool id_range_in_list(const LinkNode *id_list, int range_start, int range_end)
+{
+ const LinkNode *ln;
+ int id;
+
+ for (ln = id_list; ln != nullptr; ln = ln->next) {
+ id = POINTER_AS_INT(ln->link);
+ if (id >= range_start && id <= range_end) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static void add_to_input_ids(LinkNode **dst, int input_id)
+{
+ if (!id_in_list(*dst, input_id)) {
+ BLI_linklist_prepend(dst, POINTER_FROM_INT(input_id));
+ }
+}
+
+static void add_list_to_input_ids(LinkNode **dst, const LinkNode *src)
+{
+ const LinkNode *ln;
+
+ for (ln = src; ln != nullptr; ln = ln->next) {
+ add_to_input_ids(dst, POINTER_AS_INT(ln->link));
+ }
+}
+
+template<typename T> inline bool is_border_edge(const CDTEdge<T> *e, const CDT_state<T> *cdt)
+{
+ return e->symedges[0].face == cdt->outer_face || e->symedges[1].face == cdt->outer_face;
+}
+
+template<typename T> inline bool is_constrained_edge(const CDTEdge<T> *e)
+{
+ return e->input_ids != NULL;
+}
+
+template<typename T> inline bool is_deleted_edge(const CDTEdge<T> *e)
+{
+ return e->symedges[0].next == NULL;
+}
+
+template<typename T> inline bool is_original_vert(const CDTVert<T> *v, CDT_state<T> *cdt)
+{
+ return (v->index < cdt->input_vert_tot);
+}
+
+/* Return the Symedge that goes from v1 to v2, if it exists, else return nullptr. */
+template<typename T>
+SymEdge<T> *find_symedge_between_verts(const CDTVert<T> *v1, const CDTVert<T> *v2)
+{
+ SymEdge<T> *t = v1->symedge;
+ SymEdge<T> *tstart = t;
+ do {
+ if (t->next->vert == v2) {
+ return t;
+ }
+ } while ((t = t->rot) != tstart);
+ return nullptr;
+}
+
+/**
+ * Return the SymEdge attached to v that has face f, if it exists, else return nullptr.
+ */
+template<typename T> SymEdge<T> *find_symedge_with_face(const CDTVert<T> *v, const CDTFace<T> *f)
+{
+ SymEdge<T> *t = v->symedge;
+ SymEdge<T> *tstart = t;
+ do {
+ if (t->face == f) {
+ return t;
+ }
+ } while ((t = t->rot) != tstart);
+ return nullptr;
+}
+
+/**
+ * Is there already an edge between a and b?
+ */
+template<typename T> inline bool exists_edge(const CDTVert<T> *v1, const CDTVert<T> *v2)
+{
+ return find_symedge_between_verts(v1, v2) != nullptr;
+}
+
+/**
+ * Is the vertex v incident on face f?
+ */
+template<typename T> bool vert_touches_face(const CDTVert<T> *v, const CDTFace<T> *f)
+{
+ SymEdge<T> *se = v->symedge;
+ do {
+ if (se->face == f) {
+ return true;
+ }
+ } while ((se = se->rot) != v->symedge);
+ return false;
+}
+
+/**
+ * Assume s1 and s2 are both #SymEdges in a face with > 3 sides,
+ * and one is not the next of the other.
+ * Add an edge from `s1->v` to `s2->v`, splitting the face in two.
+ * The original face will continue to be associated with the sub-face
+ * that has s1, and a new face will be made for s2's new face.
+ * Return the new diagonal's #CDTEdge pointer.
+ */
+template<typename T> CDTEdge<T> *CDTArrangement<T>::add_diagonal(SymEdge<T> *s1, SymEdge<T> *s2)
+{
+ CDTFace<T> *fold = s1->face;
+ CDTFace<T> *fnew = this->add_face();
+ SymEdge<T> *s1prev = prev(s1);
+ SymEdge<T> *s1prevsym = sym(s1prev);
+ SymEdge<T> *s2prev = prev(s2);
+ SymEdge<T> *s2prevsym = sym(s2prev);
+ CDTEdge<T> *ediag = this->add_edge(s1->vert, s2->vert, fnew, fold);
+ SymEdge<T> *sdiag = &ediag->symedges[0];
+ SymEdge<T> *sdiagsym = &ediag->symedges[1];
+ sdiag->next = s2;
+ sdiagsym->next = s1;
+ s2prev->next = sdiagsym;
+ s1prev->next = sdiag;
+ s1->rot = sdiag;
+ sdiag->rot = s1prevsym;
+ s2->rot = sdiagsym;
+ sdiagsym->rot = s2prevsym;
+ for (SymEdge<T> *se = s2; se != sdiag; se = se->next) {
+ se->face = fnew;
+ }
+ add_list_to_input_ids(&fnew->input_ids, fold->input_ids);
+ return ediag;
+}
+
+template<typename T>
+CDTEdge<T> *CDTArrangement<T>::add_vert_to_symedge_edge(CDTVert<T> *v, SymEdge<T> *se)
+{
+ SymEdge<T> *se_rot = se->rot;
+ SymEdge<T> *se_rotsym = sym(se_rot);
+ /* TODO: check: I think last arg in next should be sym(se)->face. */
+ CDTEdge<T> *e = this->add_edge(v, se->vert, se->face, se->face);
+ SymEdge<T> *new_se = &e->symedges[0];
+ SymEdge<T> *new_se_sym = &e->symedges[1];
+ new_se->next = se;
+ new_se_sym->next = new_se;
+ new_se->rot = new_se;
+ new_se_sym->rot = se_rot;
+ se->rot = new_se_sym;
+ se_rotsym->next = new_se_sym;
+ return e;
+}
+
+/**
+ * Connect the verts of se1 and se2, assuming that currently those two #SymEdge's are on
+ * the outer boundary (have face == outer_face) of two components that are isolated from
+ * each other.
+ */
+template<typename T>
+CDTEdge<T> *CDTArrangement<T>::connect_separate_parts(SymEdge<T> *se1, SymEdge<T> *se2)
+{
+ BLI_assert(se1->face == this->outer_face && se2->face == this->outer_face);
+ SymEdge<T> *se1_rot = se1->rot;
+ SymEdge<T> *se1_rotsym = sym(se1_rot);
+ SymEdge<T> *se2_rot = se2->rot;
+ SymEdge<T> *se2_rotsym = sym(se2_rot);
+ CDTEdge<T> *e = this->add_edge(se1->vert, se2->vert, this->outer_face, this->outer_face);
+ SymEdge<T> *new_se = &e->symedges[0];
+ SymEdge<T> *new_se_sym = &e->symedges[1];
+ new_se->next = se2;
+ new_se_sym->next = se1;
+ new_se->rot = se1_rot;
+ new_se_sym->rot = se2_rot;
+ se1->rot = new_se;
+ se2->rot = new_se_sym;
+ se1_rotsym->next = new_se;
+ se2_rotsym->next = new_se_sym;
+ return e;
+}
+
+/**
+ * Split se at fraction lambda,
+ * and return the new #CDTEdge that is the new second half.
+ * Copy the edge input_ids into the new one.
+ */
+template<typename T> CDTEdge<T> *CDTArrangement<T>::split_edge(SymEdge<T> *se, T lambda)
+{
+ /* Split e at lambda. */
+ const vec2<T> *a = &se->vert->co;
+ const vec2<T> *b = &se->next->vert->co;
+ SymEdge<T> *sesym = sym(se);
+ SymEdge<T> *sesymprev = prev(sesym);
+ SymEdge<T> *sesymprevsym = sym(sesymprev);
+ SymEdge<T> *senext = se->next;
+ CDTVert<T> *v = this->add_vert(vec2<T>::interpolate(*a, *b, lambda));
+ CDTEdge<T> *e = this->add_edge(v, se->next->vert, se->face, sesym->face);
+ sesym->vert = v;
+ SymEdge<T> *newse = &e->symedges[0];
+ SymEdge<T> *newsesym = &e->symedges[1];
+ se->next = newse;
+ newsesym->next = sesym;
+ newse->next = senext;
+ newse->rot = sesym;
+ sesym->rot = newse;
+ senext->rot = newsesym;
+ newsesym->rot = sesymprevsym;
+ sesymprev->next = newsesym;
+ if (newsesym->vert->symedge == sesym) {
+ newsesym->vert->symedge = newsesym;
+ }
+ add_list_to_input_ids(&e->input_ids, se->edge->input_ids);
+ return e;
+}
+
+/**
+ * Delete an edge from the structure. The new combined face on either side of
+ * the deleted edge will be the one that was e's face.
+ * There will be now an unused face, marked by setting its deleted flag,
+ * and an unused #CDTEdge, marked by setting the next and rot pointers of
+ * its #SymEdges to #nullptr.
+ * <pre>
+ * . v2 .
+ * / \ / \
+ * /f|j\ / \
+ * / | \ / \
+ * |
+ * A | B A
+ * \ e| / \ /
+ * \ | / \ /
+ * \h|i/ \ /
+ * . v1 .
+ * </pre>
+ * Also handle variant cases where one or both ends
+ * are attached only to e.
+ */
+template<typename T> void CDTArrangement<T>::delete_edge(SymEdge<T> *se)
+{
+ SymEdge<T> *sesym = sym(se);
+ CDTVert<T> *v1 = se->vert;
+ CDTVert<T> *v2 = sesym->vert;
+ CDTFace<T> *aface = se->face;
+ CDTFace<T> *bface = sesym->face;
+ SymEdge<T> *f = se->next;
+ SymEdge<T> *h = prev(se);
+ SymEdge<T> *i = sesym->next;
+ SymEdge<T> *j = prev(sesym);
+ SymEdge<T> *jsym = sym(j);
+ SymEdge<T> *hsym = sym(h);
+ bool v1_isolated = (i == se);
+ bool v2_isolated = (f == sesym);
+
+ if (!v1_isolated) {
+ h->next = i;
+ i->rot = hsym;
+ }
+ if (!v2_isolated) {
+ j->next = f;
+ f->rot = jsym;
+ }
+ if (!v1_isolated && !v2_isolated && aface != bface) {
+ for (SymEdge<T> *k = i; k != f; k = k->next) {
+ k->face = aface;
+ }
+ }
+
+ /* If e was representative symedge for v1 or v2, fix that. */
+ if (v1_isolated) {
+ v1->symedge = nullptr;
+ }
+ else if (v1->symedge == se) {
+ v1->symedge = i;
+ }
+ if (v2_isolated) {
+ v2->symedge = nullptr;
+ }
+ else if (v2->symedge == sesym) {
+ v2->symedge = f;
+ }
+
+ /* Mark SymEdge as deleted by setting all its pointers to NULL. */
+ se->next = se->rot = nullptr;
+ sesym->next = sesym->rot = nullptr;
+ if (!v1_isolated && !v2_isolated && aface != bface) {
+ bface->deleted = true;
+ if (this->outer_face == bface) {
+ this->outer_face = aface;
+ }
+ }
+}
+
+template<typename T> class SiteInfo {
+ public:
+ CDTVert<T> *v;
+ int orig_index;
+};
+
+/**
+ * Compare function for lexicographic sort: x, then y, then index.
+ */
+template<typename T> bool site_lexicographic_sort(const SiteInfo<T> &a, const SiteInfo<T> &b)
+{
+ const vec2<T> &co_a = a.v->co;
+ const vec2<T> &co_b = b.v->co;
+ if (co_a[0] < co_b[0]) {
+ return true;
+ }
+ if (co_a[0] > co_b[0]) {
+ return false;
+ }
+ if (co_a[1] < co_b[1]) {
+ return true;
+ }
+ if (co_a[1] > co_b[1]) {
+ return false;
+ }
+ return a.orig_index < b.orig_index;
+}
+
+/**
+ * Find series of equal vertices in the sorted sites array
+ * and use the vertices merge_to_index to indicate that
+ * all vertices after the first merge to the first.
+ */
+template<typename T> void find_site_merges(Array<SiteInfo<T>> &sites)
+{
+ int n = sites.size();
+ for (int i = 0; i < n - 1; ++i) {
+ int j = i + 1;
+ while (j < n && sites[j].v->co == sites[i].v->co) {
+ sites[j].v->merge_to_index = sites[i].orig_index;
+ ++j;
+ }
+ if (j - i > 1) {
+ i = j - 1; /* j-1 because loop head will add another 1. */
+ }
+ }
+}
+
+template<typename T> inline bool vert_left_of_symedge(CDTVert<T> *v, SymEdge<T> *se)
+{
+ return orient2d(v->co, se->vert->co, se->next->vert->co) > 0;
+}
+
+template<typename T> inline bool vert_right_of_symedge(CDTVert<T> *v, SymEdge<T> *se)
+{
+ return orient2d(v->co, se->next->vert->co, se->vert->co) > 0;
+}
+
+/* Is se above basel? */
+template<typename T>
+inline bool dc_tri_valid(SymEdge<T> *se, SymEdge<T> *basel, SymEdge<T> *basel_sym)
+{
+ return orient2d(se->next->vert->co, basel_sym->vert->co, basel->vert->co) > 0;
+}
+
+/**
+ * Delaunay triangulate sites[start} to sites[end-1].
+ * Assume sites are lexicographically sorted by coordinate.
+ * Return #SymEdge of CCW convex hull at left-most point in *r_le
+ * and that of right-most point of cw convex null in *r_re.
+ */
+template<typename T>
+void dc_tri(CDTArrangement<T> *cdt,
+ Array<SiteInfo<T>> &sites,
+ int start,
+ int end,
+ SymEdge<T> **r_le,
+ SymEdge<T> **r_re)
+{
+ constexpr int dbg_level = 0;
+ if (dbg_level > 0) {
+ std::cout << "DC_TRI start=" << start << " end=" << end << "\n";
+ }
+ int n = end - start;
+ if (n <= 1) {
+ *r_le = nullptr;
+ *r_re = nullptr;
+ return;
+ }
+
+ /* Base case: if n <= 3, triangulate directly. */
+ if (n <= 3) {
+ CDTVert<T> *v1 = sites[start].v;
+ CDTVert<T> *v2 = sites[start + 1].v;
+ CDTEdge<T> *ea = cdt->add_edge(v1, v2, cdt->outer_face, cdt->outer_face);
+ ea->symedges[0].next = &ea->symedges[1];
+ ea->symedges[1].next = &ea->symedges[0];
+ ea->symedges[0].rot = &ea->symedges[0];
+ ea->symedges[1].rot = &ea->symedges[1];
+ if (n == 2) {
+ *r_le = &ea->symedges[0];
+ *r_re = &ea->symedges[1];
+ return;
+ }
+ CDTVert<T> *v3 = sites[start + 2].v;
+ CDTEdge<T> *eb = cdt->add_vert_to_symedge_edge(v3, &ea->symedges[1]);
+ int orient = orient2d(v1->co, v2->co, v3->co);
+ if (orient > 0) {
+ cdt->add_diagonal(&eb->symedges[0], &ea->symedges[0]);
+ *r_le = &ea->symedges[0];
+ *r_re = &eb->symedges[0];
+ }
+ else if (orient < 0) {
+ cdt->add_diagonal(&ea->symedges[0], &eb->symedges[0]);
+ *r_le = ea->symedges[0].rot;
+ *r_re = eb->symedges[0].rot;
+ }
+ else {
+ /* Collinear points. Just return a line. */
+ *r_le = &ea->symedges[0];
+ *r_re = &eb->symedges[0];
+ }
+ return;
+ }
+ /* Recursive case. Do left (L) and right (R) halves seperately, then join. */
+ int n2 = n / 2;
+ BLI_assert(n2 >= 2 && end - (start + n2) >= 2);
+ SymEdge<T> *ldo;
+ SymEdge<T> *ldi;
+ SymEdge<T> *rdi;
+ SymEdge<T> *rdo;
+ dc_tri(cdt, sites, start, start + n2, &ldo, &ldi);
+ dc_tri(cdt, sites, start + n2, end, &rdi, &rdo);
+ if (dbg_level > 0) {
+ std::cout << "\nDC_TRI merge step for start=" << start << ", end=" << end << "\n";
+ std::cout << "ldo " << ldo << "\n"
+ << "ldi " << ldi << "\n"
+ << "rdi " << rdi << "\n"
+ << "rdo " << rdo << "\n";
+ if (dbg_level > 1) {
+ std::string lab = "dc_tri (" + std::to_string(start) + "," + std::to_string(start + n2) +
+ ")(" + std::to_string(start + n2) + "," + std::to_string(end) + ")";
+ cdt_draw(lab, *cdt);
+ }
+ }
+ /* Find lower common tangent of L and R. */
+ for (;;) {
+ if (vert_left_of_symedge(rdi->vert, ldi)) {
+ ldi = ldi->next;
+ }
+ else if (vert_right_of_symedge(ldi->vert, rdi)) {
+ rdi = sym(rdi)->rot; /* Previous edge to rdi with same right face. */
+ }
+ else {
+ break;
+ }
+ }
+ if (dbg_level > 0) {
+ std::cout << "common lower tangent in between\n"
+ << "rdi " << rdi << "\n"
+ << "ldi" << ldi << "\n";
+ }
+
+ CDTEdge<T> *ebasel = cdt->connect_separate_parts(sym(rdi)->next, ldi);
+ SymEdge<T> *basel = &ebasel->symedges[0];
+ SymEdge<T> *basel_sym = &ebasel->symedges[1];
+ if (dbg_level > 1) {
+ std::cout << "basel " << basel;
+ cdt_draw("after basel made", *cdt);
+ }
+ if (ldi->vert == ldo->vert) {
+ ldo = basel_sym;
+ }
+ if (rdi->vert == rdo->vert) {
+ rdo = basel;
+ }
+
+ /* Merge loop. */
+ for (;;) {
+ /* Locate the first point lcand->next->vert encountered by rising bubble,
+ * and delete L edges out of basel->next->vert that fail the circle test. */
+ SymEdge<T> *lcand = basel_sym->rot;
+ SymEdge<T> *rcand = basel_sym->next;
+ if (dbg_level > 1) {
+ std::cout << "\ntop of merge loop\n";
+ std::cout << "lcand " << lcand << "\n"
+ << "rcand " << rcand << "\n"
+ << "basel " << basel << "\n";
+ }
+ if (dc_tri_valid(lcand, basel, basel_sym)) {
+ if (dbg_level > 1) {
+ std::cout << "found valid lcand\n";
+ std::cout << " lcand" << lcand << "\n";
+ }
+ while (incircle(basel_sym->vert->co,
+ basel->vert->co,
+ lcand->next->vert->co,
+ lcand->rot->next->vert->co) > 0.0) {
+ if (dbg_level > 1) {
+ std::cout << "incircle says to remove lcand\n";
+ std::cout << " lcand" << lcand << "\n";
+ }
+ SymEdge<T> *t = lcand->rot;
+ cdt->delete_edge(sym(lcand));
+ lcand = t;
+ }
+ }
+ /* Symmetrically, locate first R point to be hit and delete R edges. */
+ if (dc_tri_valid(rcand, basel, basel_sym)) {
+ if (dbg_level > 1) {
+ std::cout << "found valid rcand\n";
+ std::cout << " rcand" << rcand << "\n";
+ }
+ while (incircle(basel_sym->vert->co,
+ basel->vert->co,
+ rcand->next->vert->co,
+ sym(rcand)->next->next->vert->co) > 0.0) {
+ if (dbg_level > 0) {
+ std::cout << "incircle says to remove rcand\n";
+ std::cout << " rcand" << rcand << "\n";
+ }
+ SymEdge<T> *t = sym(rcand)->next;
+ cdt->delete_edge(rcand);
+ rcand = t;
+ }
+ }
+ /* If both lcand and rcand are invalid, then basel is the common upper tangent. */
+ bool valid_lcand = dc_tri_valid(lcand, basel, basel_sym);
+ bool valid_rcand = dc_tri_valid(rcand, basel, basel_sym);
+ if (dbg_level > 0) {
+ std::cout << "after bubbling up, valid_lcand=" << valid_lcand
+ << ", valid_rand=" << valid_rcand << "\n";
+ std::cout << "lcand" << lcand << "\n"
+ << "rcand" << rcand << "\n";
+ }
+ if (!valid_lcand && !valid_rcand) {
+ break;
+ }
+ /* The next cross edge to be connected is to either `lcand->next->vert` or `rcand->next->vert`;
+ * if both are valid, choose the appropriate one using the #incircle test. */
+ if (!valid_lcand ||
+ (valid_rcand &&
+ incircle(lcand->next->vert->co, lcand->vert->co, rcand->vert->co, rcand->next->vert->co) >
+ 0)) {
+ if (dbg_level > 0) {
+ std::cout << "connecting rcand\n";
+ std::cout << " se1=basel_sym" << basel_sym << "\n";
+ std::cout << " se2=rcand->next" << rcand->next << "\n";
+ }
+ ebasel = cdt->add_diagonal(rcand->next, basel_sym);
+ }
+ else {
+ if (dbg_level > 0) {
+ std::cout << "connecting lcand\n";
+ std::cout << " se1=sym(lcand)" << sym(lcand) << "\n";
+ std::cout << " se2=basel_sym->next" << basel_sym->next << "\n";
+ }
+ ebasel = cdt->add_diagonal(basel_sym->next, sym(lcand));
+ }
+ basel = &ebasel->symedges[0];
+ basel_sym = &ebasel->symedges[1];
+ BLI_assert(basel_sym->face == cdt->outer_face);
+ if (dbg_level > 2) {
+ cdt_draw("after adding new crossedge", *cdt);
+ }
+ }
+ *r_le = ldo;
+ *r_re = rdo;
+ BLI_assert(sym(ldo)->face == cdt->outer_face && rdo->face == cdt->outer_face);
+}
+
+/* Guibas-Stolfi Divide-and_Conquer algorithm. */
+template<typename T> void dc_triangulate(CDTArrangement<T> *cdt, Array<SiteInfo<T>> &sites)
+{
+ /* Compress sites in place to eliminted verts that merge to others. */
+ int i = 0;
+ int j = 0;
+ int nsites = sites.size();
+ while (j < nsites) {
+ /* Invariante: sites[0..i-1] have non-merged verts from 0..(j-1) in them. */
+ sites[i] = sites[j++];
+ if (sites[i].v->merge_to_index < 0) {
+ i++;
+ }
+ }
+ int n = i;
+ if (n == 0) {
+ return;
+ }
+ SymEdge<T> *le;
+ SymEdge<T> *re;
+ dc_tri(cdt, sites, 0, n, &le, &re);
+}
+
+/**
+ * Do a Delaunay Triangulation of the points in cdt.verts.
+ * This is only a first step in the Constrained Delaunay triangulation,
+ * because it doesn't yet deal with the segment constraints.
+ * The algorithm used is the Divide & Conquer algorithm from the
+ * Guibas-Stolfi "Primitives for the Manipulation of General Subdivision
+ * and the Computation of Voronoi Diagrams" paper.
+ * The data structure here is similar to but not exactly the same as
+ * the quad-edge structure described in that paper.
+ * If T is not exact arithmetic, incircle and CCW tests are done using
+ * Shewchuk's exact primitives, so that this routine is robust.
+ *
+ * As a preprocessing step, we want to merge all vertices that the same.
+ * This is accomplished by lexicographically
+ * sorting the coordinates first (which is needed anyway for the D&C algorithm).
+ * The CDTVerts with merge_to_index not equal to -1 are after this regarded
+ * as having been merged into the vertex with the corresponding index.
+ */
+template<typename T> void initial_triangulation(CDTArrangement<T> *cdt)
+{
+ int n = cdt->verts.size();
+ if (n <= 1) {
+ return;
+ }
+ Array<SiteInfo<T>> sites(n);
+ for (int i = 0; i < n; ++i) {
+ sites[i].v = cdt->verts[i];
+ sites[i].orig_index = i;
+ }
+ std::sort(sites.begin(), sites.end(), site_lexicographic_sort<T>);
+ find_site_merges(sites);
+ dc_triangulate(cdt, sites);
+}
+
+/**
+ * Re-triangulates, assuring constrained delaunay condition,
+ * the pseudo-polygon that cycles from se.
+ * "pseudo" because a vertex may be repeated.
+ * See Anglada paper, "An Improved incremental algorithm
+ * for constructing restricted Delaunay triangulations".
+ */
+template<typename T> static void re_delaunay_triangulate(CDTArrangement<T> *cdt, SymEdge<T> *se)
+{
+ if (se->face == cdt->outer_face || sym(se)->face == cdt->outer_face) {
+ return;
+ }
+ /* 'se' is a diagonal just added, and it is base of area to retriangulate (face on its left) */
+ int count = 1;
+ for (SymEdge<T> *ss = se->next; ss != se; ss = ss->next) {
+ count++;
+ }
+ if (count <= 3) {
+ return;
+ }
+ /* First and last are the SymEdges whose verts are first and last off of base,
+ * continuing from 'se'. */
+ SymEdge<T> *first = se->next->next;
+ /* We want to make a triangle with 'se' as base and some other c as 3rd vertex. */
+ CDTVert<T> *a = se->vert;
+ CDTVert<T> *b = se->next->vert;
+ CDTVert<T> *c = first->vert;
+ SymEdge<T> *cse = first;
+ for (SymEdge<T> *ss = first->next; ss != se; ss = ss->next) {
+ CDTVert<T> *v = ss->vert;
+ if (incircle(a->co, b->co, c->co, v->co) > 0) {
+ c = v;
+ cse = ss;
+ }
+ }
+ /* Add diagonals necessary to make abc a triangle. */
+ CDTEdge<T> *ebc = nullptr;
+ CDTEdge<T> *eca = nullptr;
+ if (!exists_edge(b, c)) {
+ ebc = cdt->add_diagonal(se->next, cse);
+ }
+ if (!exists_edge(c, a)) {
+ eca = cdt->add_diagonal(cse, se);
+ }
+ /* Now recurse. */
+ if (ebc) {
+ re_delaunay_triangulate(cdt, &ebc->symedges[1]);
+ }
+ if (eca) {
+ re_delaunay_triangulate(cdt, &eca->symedges[1]);
+ }
+}
+
+template<typename T> inline int tri_orient(const SymEdge<T> *t)
+{
+ return orient2d(t->vert->co, t->next->vert->co, t->next->next->vert->co);
+}
+
+/**
+ * The #CrossData class defines either an endpoint or an intermediate point
+ * in the path we will take to insert an edge constraint.
+ * Each such point will either be
+ * (a) a vertex or
+ * (b) a fraction lambda (0 < lambda < 1) along some #SymEdge.]
+ *
+ * In general, lambda=0 indicates case a and lambda != 0 indicates case be.
+ * The 'in' edge gives the destination attachment point of a diagonal from the previous crossing,
+ * and the 'out' edge gives the origin attachment point of a diagonal to the next crossing.
+ * But in some cases, 'in' and 'out' are undefined or not needed, and will be NULL.
+ *
+ * For case (a), 'vert' will be the vertex, and lambda will be 0, and 'in' will be the #SymEdge
+ * from 'vert' that has as face the one that you go through to get to this vertex. If you go
+ * exactly along an edge then we set 'in' to NULL, since it won't be needed. The first crossing
+ * will have 'in' = NULL. We set 'out' to the #SymEdge that has the face we go though to get to the
+ * next crossing, or, if the next crossing is a case (a), then it is the edge that goes to that
+ * next vertex. 'out' will be NULL for the last one.
+ *
+ * For case (b), vert will be NULL at first, and later filled in with the created split vertex,
+ * and 'in' will be the #SymEdge that we go through, and lambda will be between 0 and 1,
+ * the fraction from in's vert to in->next's vert to put the split vertex.
+ * 'out' is not needed in this case, since the attachment point will be the sym of the first
+ * half of the split edge.
+ */
+template<typename T> class CrossData {
+ public:
+ T lambda = T(0);
+ CDTVert<T> *vert;
+ SymEdge<T> *in;
+ SymEdge<T> *out;
+
+ CrossData() : lambda(T(0)), vert(nullptr), in(nullptr), out(nullptr)
+ {
+ }
+ CrossData(T l, CDTVert<T> *v, SymEdge<T> *i, SymEdge<T> *o) : lambda(l), vert(v), in(i), out(o)
+ {
+ }
+ CrossData(const CrossData &other)
+ : lambda(other.lambda), vert(other.vert), in(other.in), out(other.out)
+ {
+ }
+ CrossData(CrossData &&other) noexcept
+ : lambda(std::move(other.lambda)),
+ vert(std::move(other.vert)),
+ in(std::move(other.in)),
+ out(std::move(other.out))
+ {
+ }
+ ~CrossData() = default;
+ CrossData &operator=(const CrossData &other)
+ {
+ if (this != &other) {
+ lambda = other.lambda;
+ vert = other.vert;
+ in = other.in;
+ out = other.out;
+ }
+ return *this;
+ }
+ CrossData &operator=(CrossData &&other) noexcept
+ {
+ lambda = std::move(other.lambda);
+ vert = std::move(other.vert);
+ in = std::move(other.in);
+ out = std::move(other.out);
+ return *this;
+ }
+};
+
+template<typename T>
+bool get_next_crossing_from_vert(CDT_state<T> *cdt_state,
+ CrossData<T> *cd,
+ CrossData<T> *cd_next,
+ const CDTVert<T> *v2);
+
+/**
+ * As part of finding crossings, we found a case where the next crossing goes through vert v.
+ * If it came from a previous vert in cd, then cd_out is the edge that leads from that to v.
+ * Else cd_out can be NULL, because it won't be used.
+ * Set *cd_next to indicate this. We can set 'in' but not 'out'. We can set the 'out' of the
+ * current cd.
+ */
+template<typename T>
+void fill_crossdata_for_through_vert(CDTVert<T> *v,
+ SymEdge<T> *cd_out,
+ CrossData<T> *cd,
+ CrossData<T> *cd_next)
+{
+ SymEdge<T> *se;
+
+ cd_next->lambda = T(0);
+ cd_next->vert = v;
+ cd_next->in = NULL;
+ cd_next->out = NULL;
+ if (cd->lambda == 0) {
+ cd->out = cd_out;
+ }
+ else {
+ /* One of the edges in the triangle with edge sym(cd->in) contains v. */
+ se = sym(cd->in);
+ if (se->vert != v) {
+ se = se->next;
+ if (se->vert != v) {
+ se = se->next;
+ }
+ }
+ BLI_assert(se->vert == v);
+ cd_next->in = se;
+ }
+}
+
+/**
+ * As part of finding crossings, we found a case where orient tests say that the next crossing
+ * is on the #SymEdge t, while intersecting with the ray from \a curco to \a v2.
+ * Find the intersection point and fill in the #CrossData for that point.
+ * It may turn out that when doing the intersection, we get an answer that says that
+ * this case is better handled as through-vertex case instead, so we may do that.
+ * In the latter case, we want to avoid a situation where the current crossing is on an edge
+ * and the next will be an endpoint of the same edge. When that happens, we "rewrite history"
+ * and turn the current crossing into a vert one, and then extend from there.
+ *
+ * We cannot fill cd_next's 'out' edge yet, in the case that the next one ends up being a vert
+ * case. We need to fill in cd's 'out' edge if it was a vert case.
+ */
+template<typename T>
+void fill_crossdata_for_intersect(const vec2<T> &curco,
+ const CDTVert<T> *v2,
+ SymEdge<T> *t,
+ CrossData<T> *cd,
+ CrossData<T> *cd_next,
+ const T epsilon)
+{
+ CDTVert<T> *va = t->vert;
+ CDTVert<T> *vb = t->next->vert;
+ CDTVert<T> *vc = t->next->next->vert;
+ SymEdge<T> *se_vcvb = sym(t->next);
+ SymEdge<T> *se_vcva = t->next->next;
+ BLI_assert(se_vcva->vert == vc && se_vcva->next->vert == va);
+ BLI_assert(se_vcvb->vert == vc && se_vcvb->next->vert == vb);
+ UNUSED_VARS_NDEBUG(vc);
+ auto isect = vec2<T>::isect_seg_seg(va->co, vb->co, curco, v2->co);
+ T &lambda = isect.lambda;
+ switch (isect.kind) {
+ case vec2<T>::isect_result::LINE_LINE_CROSS: {
+#ifdef WITH_GMP
+ if (!std::is_same<T, mpq_class>::value) {
+#else
+ if (true) {
+#endif
+ T len_ab = vec2<T>::distance(va->co, vb->co);
+ if (lambda * len_ab <= epsilon) {
+ fill_crossdata_for_through_vert(va, se_vcva, cd, cd_next);
+ }
+ else if ((1 - lambda) * len_ab <= epsilon) {
+ fill_crossdata_for_through_vert(vb, se_vcvb, cd, cd_next);
+ }
+ else {
+ *cd_next = CrossData<T>(lambda, nullptr, t, nullptr);
+ if (cd->lambda == 0) {
+ cd->out = se_vcva;
+ }
+ }
+ }
+ else {
+ *cd_next = CrossData<T>(lambda, nullptr, t, nullptr);
+ if (cd->lambda == 0) {
+ cd->out = se_vcva;
+ }
+ }
+ break;
+ }
+ case vec2<T>::isect_result::LINE_LINE_EXACT: {
+ if (lambda == 0) {
+ fill_crossdata_for_through_vert(va, se_vcva, cd, cd_next);
+ }
+ else if (lambda == 1) {
+ fill_crossdata_for_through_vert(vb, se_vcvb, cd, cd_next);
+ }
+ else {
+ *cd_next = CrossData<T>(lambda, nullptr, t, nullptr);
+ if (cd->lambda == 0) {
+ cd->out = se_vcva;
+ }
+ }
+ break;
+ }
+ case vec2<T>::isect_result::LINE_LINE_NONE: {
+#ifdef WITH_GMP
+ if (std::is_same<T, mpq_class>::value) {
+ BLI_assert(false);
+ }
+#endif
+ /* It should be very near one end or other of segment. */
+ const T middle_lambda = 0.5;
+ if (lambda <= middle_lambda) {
+ fill_crossdata_for_through_vert(va, se_vcva, cd, cd_next);
+ }
+ else {
+ fill_crossdata_for_through_vert(vb, se_vcvb, cd, cd_next);
+ }
+ break;
+ }
+ case vec2<T>::isect_result::LINE_LINE_COLINEAR: {
+ if (vec2<T>::distance_squared(va->co, v2->co) <= vec2<T>::distance_squared(vb->co, v2->co)) {
+ fill_crossdata_for_through_vert(va, se_vcva, cd, cd_next);
+ }
+ else {
+ fill_crossdata_for_through_vert(vb, se_vcvb, cd, cd_next);
+ }
+ break;
+ }
+ }
+} // namespace blender::meshintersect
+
+/**
+ * As part of finding the crossings of a ray to v2, find the next crossing after 'cd', assuming
+ * 'cd' represents a crossing that goes through a vertex.
+ *
+ * We do a rotational scan around cd's vertex, looking for the triangle where the ray from cd->vert
+ * to v2 goes between the two arms from cd->vert, or where it goes along one of the edges.
+ */
+template<typename T>
+bool get_next_crossing_from_vert(CDT_state<T> *cdt_state,
+ CrossData<T> *cd,
+ CrossData<T> *cd_next,
+ const CDTVert<T> *v2)
+{
+ SymEdge<T> *tstart = cd->vert->symedge;
+ SymEdge<T> *t = tstart;
+ CDTVert<T> *vcur = cd->vert;
+ bool ok = false;
+ do {
+ /* The ray from `vcur` to v2 has to go either between two successive
+ * edges around `vcur` or exactly along them. This time through the
+ * loop, check to see if the ray goes along `vcur-va`
+ * or between `vcur-va` and `vcur-vb`, where va is the end of t
+ * and vb is the next vertex (on the next rot edge around vcur, but
+ * should also be the next vert of triangle starting with `vcur-va`. */
+ if (t->face != cdt_state->cdt.outer_face && tri_orient(t) < 0) {
+ BLI_assert(false); /* Shouldn't happen. */
+ }
+ CDTVert<T> *va = t->next->vert;
+ CDTVert<T> *vb = t->next->next->vert;
+ int orient1 = orient2d(t->vert->co, va->co, v2->co);
+ if (orient1 == 0 && in_line<T>(vcur->co, va->co, v2->co)) {
+ fill_crossdata_for_through_vert(va, t, cd, cd_next);
+ ok = true;
+ break;
+ }
+ if (t->face != cdt_state->cdt.outer_face) {
+ int orient2 = orient2d(vcur->co, vb->co, v2->co);
+ /* Don't handle orient2 == 0 case here: next rotation will get it. */
+ if (orient1 > 0 && orient2 < 0) {
+ /* Segment intersection. */
+ t = t->next;
+ fill_crossdata_for_intersect(vcur->co, v2, t, cd, cd_next, cdt_state->epsilon);
+ ok = true;
+ break;
+ }
+ }
+ } while ((t = t->rot) != tstart);
+ return ok;
+}
+
+/**
+ * As part of finding the crossings of a ray to `v2`, find the next crossing after 'cd', assuming
+ * 'cd' represents a crossing that goes through a an edge, not at either end of that edge.
+ *
+ * We have the triangle `vb-va-vc`, where `va` and vb are the split edge and `vc` is the third
+ * vertex on that new side of the edge (should be closer to `v2`).
+ * The next crossing should be through `vc` or intersecting `vb-vc` or `va-vc`.
+ */
+template<typename T>
+void get_next_crossing_from_edge(CrossData<T> *cd,
+ CrossData<T> *cd_next,
+ const CDTVert<T> *v2,
+ const T epsilon)
+{
+ CDTVert<T> *va = cd->in->vert;
+ CDTVert<T> *vb = cd->in->next->vert;
+ vec2<T> curco = vec2<T>::interpolate(va->co, vb->co, cd->lambda);
+ SymEdge<T> *se_ac = sym(cd->in)->next;
+ CDTVert<T> *vc = se_ac->next->vert;
+ int orient = orient2d(curco, v2->co, vc->co);
+ if (orient < 0) {
+ fill_crossdata_for_intersect<T>(curco, v2, se_ac->next, cd, cd_next, epsilon);
+ }
+ else if (orient > 0.0) {
+ fill_crossdata_for_intersect(curco, v2, se_ac, cd, cd_next, epsilon);
+ }
+ else {
+ *cd_next = CrossData<T>{0.0, vc, se_ac->next, nullptr};
+ }
+}
+
+constexpr int inline_crossings_size = 128;
+template<typename T>
+void dump_crossings(const Vector<CrossData<T>, inline_crossings_size> &crossings)
+{
+ std::cout << "CROSSINGS\n";
+ for (int i = 0; i < crossings.size(); ++i) {
+ std::cout << i << ": ";
+ const CrossData<T> &cd = crossings[i];
+ if (cd.lambda == 0) {
+ std::cout << "v" << cd.vert->index;
+ }
+ else {
+ std::cout << "lambda=" << cd.lambda;
+ }
+ if (cd.in != nullptr) {
+ std::cout << " in=" << short_se_dump(cd.in);
+ std::cout << " out=" << short_se_dump(cd.out);
+ }
+ std::cout << "\n";
+ }
+}
+
+/**
+ * Add a constrained edge between v1 and v2 to cdt structure.
+ * This may result in a number of #CDTEdges created, due to intersections
+ * and partial overlaps with existing cdt vertices and edges.
+ * Each created #CDTEdge will have input_id added to its input_ids list.
+ *
+ * If \a r_edges is not NULL, the #CDTEdges generated or found that go from
+ * v1 to v2 are put into that linked list, in order.
+ *
+ * Assumes that #blender_constrained_delaunay_get_output has not been called yet.
+ */
+template<typename T>
+void add_edge_constraint(
+ CDT_state<T> *cdt_state, CDTVert<T> *v1, CDTVert<T> *v2, int input_id, LinkNode **r_edges)
+{
+ constexpr int dbg_level = 0;
+ if (dbg_level > 0) {
+ std::cout << "\nADD EDGE CONSTRAINT\n" << vertname(v1) << " " << vertname(v2) << "\n";
+ }
+ LinkNodePair edge_list = {NULL, NULL};
+
+ if (r_edges) {
+ *r_edges = NULL;
+ }
+
+ /*
+ * Handle two special cases first:
+ * 1) The two end vertices are the same (can happen because of merging).
+ * 2) There is already an edge between v1 and v2.
+ */
+ if (v1 == v2) {
+ return;
+ }
+ SymEdge<T> *t = find_symedge_between_verts(v1, v2);
+ if (t != nullptr) {
+ /* Segment already there. */
+ add_to_input_ids(&t->edge->input_ids, input_id);
+ if (r_edges != NULL) {
+ BLI_linklist_append(&edge_list, t->edge);
+ *r_edges = edge_list.list;
+ }
+ return;
+ }
+
+ /*
+ * Fill crossings array with CrossData points for intersection path from v1 to v2.
+ *
+ * At every point, the crossings array has the path so far, except that
+ * the .out field of the last element of it may not be known yet -- if that
+ * last element is a vertex, then we won't know the output edge until we
+ * find the next crossing.
+ *
+ * To protect against infinite loops, we keep track of which vertices
+ * we have visited by setting their visit_index to a new visit epoch.
+ *
+ * We check a special case first: where the segment is already there in
+ * one hop. Saves a bunch of orient2d tests in that common case.
+ */
+ int visit = ++cdt_state->visit_count;
+ Vector<CrossData<T>, inline_crossings_size> crossings;
+ crossings.append(CrossData<T>(T(0), v1, nullptr, nullptr));
+ int n;
+ while (!((n = crossings.size()) > 0 && crossings[n - 1].vert == v2)) {
+ crossings.append(CrossData<T>());
+ CrossData<T> *cd = &crossings[n - 1];
+ CrossData<T> *cd_next = &crossings[n];
+ bool ok;
+ if (crossings[n - 1].lambda == 0) {
+ ok = get_next_crossing_from_vert(cdt_state, cd, cd_next, v2);
+ }
+ else {
+ get_next_crossing_from_edge(cd, cd_next, v2, cdt_state->epsilon);
+ ok = true;
+ }
+ constexpr int unreasonably_large_crossings = 100000;
+ if (!ok || crossings.size() == unreasonably_large_crossings) {
+ /* Shouldn't happen but if does, just bail out. */
+ BLI_assert(false);
+ return;
+ }
+ if (crossings[n].lambda == 0) {
+ if (crossings[n].vert->visit_index == visit) {
+ /* Shouldn't happen but if it does, just bail out. */
+ BLI_assert(false);
+ return;
+ }
+ crossings[n].vert->visit_index = visit;
+ }
+ }
+
+ if (dbg_level > 0) {
+ dump_crossings(crossings);
+ }
+
+ /*
+ * Post-process crossings.
+ * Some crossings may have an intersection crossing followed
+ * by a vertex crossing that is on the same edge that was just
+ * intersected. We prefer to go directly from the previous
+ * crossing directly to the vertex. This may chain backwards.
+ *
+ * This loop marks certain crossings as "deleted", by setting
+ * their lambdas to -1.0.
+ */
+ int ncrossings = crossings.size();
+ for (int i = 2; i < ncrossings; ++i) {
+ CrossData<T> *cd = &crossings[i];
+ if (cd->lambda == 0.0) {
+ CDTVert<T> *v = cd->vert;
+ int j;
+ CrossData<T> *cd_prev;
+ for (j = i - 1; j > 0; --j) {
+ cd_prev = &crossings[j];
+ if ((cd_prev->lambda == 0.0 && cd_prev->vert != v) ||
+ (cd_prev->lambda != 0.0 && cd_prev->in->vert != v && cd_prev->in->next->vert != v)) {
+ break;
+ }
+ cd_prev->lambda = -1.0; /* Mark cd_prev as 'deleted'. */
+ }
+ if (j < i - 1) {
+ /* Some crossings were deleted. Fix the in and out edges across gap. */
+ cd_prev = &crossings[j];
+ SymEdge<T> *se;
+ if (cd_prev->lambda == 0.0) {
+ se = find_symedge_between_verts(cd_prev->vert, v);
+ if (se == NULL) {
+ return;
+ }
+ cd_prev->out = se;
+ cd->in = NULL;
+ }
+ else {
+ se = find_symedge_with_face(v, sym(cd_prev->in)->face);
+ if (se == NULL) {
+ return;
+ }
+ cd->in = se;
+ }
+ }
+ }
+ }
+
+ /*
+ * Insert all intersection points on constrained edges.
+ */
+ for (int i = 0; i < ncrossings; ++i) {
+ CrossData<T> *cd = &crossings[i];
+ if (cd->lambda != 0.0 && cd->lambda != -1.0 && is_constrained_edge(cd->in->edge)) {
+ CDTEdge<T> *edge = cdt_state->cdt.split_edge(cd->in, cd->lambda);
+ cd->vert = edge->symedges[0].vert;
+ }
+ }
+
+ /*
+ * Remove any crossed, non-intersected edges.
+ */
+ for (int i = 0; i < ncrossings; ++i) {
+ CrossData<T> *cd = &crossings[i];
+ if (cd->lambda != 0.0 && cd->lambda != -1.0 && !is_constrained_edge(cd->in->edge)) {
+ cdt_state->cdt.delete_edge(cd->in);
+ }
+ }
+
+ /*
+ * Insert segments for v1->v2.
+ */
+ SymEdge<T> *tstart = crossings[0].out;
+ for (int i = 1; i < ncrossings; i++) {
+ CrossData<T> *cd = &crossings[i];
+ if (cd->lambda == -1.0) {
+ continue; /* This crossing was deleted. */
+ }
+ t = NULL;
+ SymEdge<T> *tnext = t;
+ CDTEdge<T> *edge;
+ if (cd->lambda != 0.0) {
+ if (is_constrained_edge(cd->in->edge)) {
+ t = cd->vert->symedge;
+ tnext = sym(t)->next;
+ }
+ }
+ else if (cd->lambda == 0.0) {
+ t = cd->in;
+ tnext = cd->out;
+ if (t == NULL) {
+ /* Previous non-deleted crossing must also have been a vert, and segment should exist. */
+ int j;
+ CrossData<T> *cd_prev;
+ for (j = i - 1; j >= 0; j--) {
+ cd_prev = &crossings[j];
+ if (cd_prev->lambda != -1.0) {
+ break;
+ }
+ }
+ BLI_assert(cd_prev->lambda == 0.0);
+ BLI_assert(cd_prev->out->next->vert == cd->vert);
+ edge = cd_prev->out->edge;
+ add_to_input_ids(&edge->input_ids, input_id);
+ if (r_edges != NULL) {
+ BLI_linklist_append(&edge_list, edge);
+ }
+ }
+ }
+ if (t != NULL) {
+ if (tstart->next->vert == t->vert) {
+ edge = tstart->edge;
+ }
+ else {
+ edge = cdt_state->cdt.add_diagonal(tstart, t);
+ }
+ add_to_input_ids(&edge->input_ids, input_id);
+ if (r_edges != NULL) {
+ BLI_linklist_append(&edge_list, edge);
+ }
+ /* Now retriangulate upper and lower gaps. */
+ re_delaunay_triangulate(&cdt_state->cdt, &edge->symedges[0]);
+ re_delaunay_triangulate(&cdt_state->cdt, &edge->symedges[1]);
+ }
+ if (i < ncrossings - 1) {
+ if (tnext != NULL) {
+ tstart = tnext;
+ }
+ }
+ }
+
+ if (r_edges) {
+ *r_edges = edge_list.list;
+ }
+}
+
+/**
+ * Incrementally add edge input edge as a constraint. This may cause the graph structure
+ * to change, in cases where the constraints intersect existing edges.
+ * The code will ensure that #CDTEdge's created will have ids that tie them back
+ * to the original edge constraint index.
+ */
+template<typename T> void add_edge_constraints(CDT_state<T> *cdt_state, const CDT_input<T> &input)
+{
+ int ne = input.edge.size();
+ int nv = input.vert.size();
+ for (int i = 0; i < ne; i++) {
+ int iv1 = input.edge[i].first;
+ int iv2 = input.edge[i].second;
+ if (iv1 < 0 || iv1 >= nv || iv2 < 0 || iv2 >= nv) {
+ /* Ignore invalid indices in edges. */
+ continue;
+ }
+ CDTVert<T> *v1 = cdt_state->cdt.get_vert_resolve_merge(iv1);
+ CDTVert<T> *v2 = cdt_state->cdt.get_vert_resolve_merge(iv2);
+ add_edge_constraint(cdt_state, v1, v2, i, nullptr);
+ }
+ cdt_state->face_edge_offset = ne;
+}
+
+/**
+ * Add face_id to the input_ids lists of all #CDTFace's on the interior of the input face with that
+ * id. face_symedge is on edge of the boundary of the input face, with assumption that interior is
+ * on the left of that #SymEdge.
+ *
+ * The algorithm is: starting from the #CDTFace for face_symedge, add the face_id and then
+ * process all adjacent faces where the adjacency isn't across an edge that was a constraint added
+ * for the boundary of the input face.
+ * fedge_start..fedge_end is the inclusive range of edge input ids that are for the given face.
+ *
+ * Note: if the input face is not CCW oriented, we'll be labeling the outside, not the inside.
+ * Note 2: if the boundary has self-crossings, this method will arbitrarily pick one of the
+ * contiguous set of faces enclosed by parts of the boundary, leaving the other such un-tagged.
+ * This may be a feature instead of a bug if the first contiguous section is most of the face and
+ * the others are tiny self-crossing triangles at some parts of the boundary.
+ * On the other hand, if decide we want to handle these in full generality, then will need a more
+ * complicated algorithm (using "inside" tests and a parity rule) to decide on the interior.
+ */
+template<typename T>
+void add_face_ids(
+ CDT_state<T> *cdt_state, SymEdge<T> *face_symedge, int face_id, int fedge_start, int fedge_end)
+{
+ /* Can't loop forever since eventually would visit every face. */
+ cdt_state->visit_count++;
+ int visit = cdt_state->visit_count;
+ Vector<SymEdge<T> *> stack;
+ stack.append(face_symedge);
+ while (!stack.is_empty()) {
+ SymEdge<T> *se = stack.pop_last();
+ CDTFace<T> *face = se->face;
+ if (face->visit_index == visit) {
+ continue;
+ }
+ face->visit_index = visit;
+ add_to_input_ids(&face->input_ids, face_id);
+ SymEdge<T> *se_start = se;
+ for (se = se->next; se != se_start; se = se->next) {
+ if (!id_range_in_list(se->edge->input_ids, fedge_start, fedge_end)) {
+ SymEdge<T> *se_sym = sym(se);
+ CDTFace<T> *face_other = se_sym->face;
+ if (face_other->visit_index != visit) {
+ stack.append(se_sym);
+ }
+ }
+ }
+ }
+}
+
+/* Return a power of 10 that is greater than or equal to x. */
+static int power_of_10_greater_equal_to(int x)
+{
+ if (x <= 0) {
+ return 1;
+ }
+ int ans = 1;
+ BLI_assert(x < INT_MAX / 10);
+ while (ans < x) {
+ ans *= 10;
+ }
+ return ans;
+}
+
+/**
+ Incrementally each edge of each input face as an edge constraint.
+ * The code will ensure that the #CDTEdge's created will have ids that tie them
+ * back to the original face edge (using a numbering system for those edges
+ * that starts with cdt->face_edge_offset, and continues with the edges in
+ * order around each face in turn. And then the next face starts at
+ * cdt->face_edge_offset beyond the start for the previous face.
+ */
+template<typename T> void add_face_constraints(CDT_state<T> *cdt_state, const CDT_input<T> &input)
+{
+ int nv = input.vert.size();
+ int nf = input.face.size();
+ int fstart = 0;
+ SymEdge<T> *face_symedge0 = nullptr;
+ CDTArrangement<T> *cdt = &cdt_state->cdt;
+ int maxflen = 0;
+ for (int f = 0; f < nf; f++) {
+ maxflen = max_ii(maxflen, input.face[f].size());
+ }
+ /* For convenience in debugging, make face_edge_offset be a power of 10. */
+ cdt_state->face_edge_offset = power_of_10_greater_equal_to(
+ max_ii(maxflen, cdt_state->face_edge_offset));
+ /* The original_edge encoding scheme doesn't work if the following is false.
+ * If we really have that many faces and that large a max face length that when multiplied
+ * together the are >= INT_MAX, then the Delaunay calculation will take unreasonably long anyway.
+ */
+ BLI_assert(INT_MAX / cdt_state->face_edge_offset > nf);
+ for (int f = 0; f < nf; f++) {
+ int flen = input.face[f].size();
+ if (flen <= 2) {
+ /* Ignore faces with fewer than 3 vertices. */
+ fstart += flen;
+ continue;
+ }
+ int fedge_start = (f + 1) * cdt_state->face_edge_offset;
+ for (int i = 0; i < flen; i++) {
+ int face_edge_id = fedge_start + i;
+ int iv1 = input.face[f][i];
+ int iv2 = input.face[f][(i + 1) % flen];
+ if (iv1 < 0 || iv1 >= nv || iv2 < 0 || iv2 >= nv) {
+ /* Ignore face edges with invalid vertices. */
+ continue;
+ }
+ CDTVert<T> *v1 = cdt->get_vert_resolve_merge(iv1);
+ CDTVert<T> *v2 = cdt->get_vert_resolve_merge(iv2);
+ LinkNode *edge_list;
+ add_edge_constraint(cdt_state, v1, v2, face_edge_id, &edge_list);
+ /* Set a new face_symedge0 each time since earlier ones may not
+ * survive later symedge splits. Really, just want the one when
+ * i == flen -1, but this code guards against that one somehow
+ * being null.
+ */
+ if (edge_list != nullptr) {
+ CDTEdge<T> *face_edge = static_cast<CDTEdge<T> *>(edge_list->link);
+ face_symedge0 = &face_edge->symedges[0];
+ if (face_symedge0->vert != v1) {
+ face_symedge0 = &face_edge->symedges[1];
+ BLI_assert(face_symedge0->vert == v1);
+ }
+ }
+ BLI_linklist_free(edge_list, nullptr);
+ }
+ int fedge_end = fedge_start + flen - 1;
+ if (face_symedge0 != nullptr) {
+ add_face_ids(cdt_state, face_symedge0, f, fedge_start, fedge_end);
+ }
+ fstart += flen;
+ }
+}
+
+/* Delete_edge but try not to mess up outer face.
+ * Also faces have symedges now, so make sure not
+ * to mess those up either. */
+template<typename T> void dissolve_symedge(CDT_state<T> *cdt_state, SymEdge<T> *se)
+{
+ CDTArrangement<T> *cdt = &cdt_state->cdt;
+ SymEdge<T> *symse = sym(se);
+ if (symse->face == cdt->outer_face) {
+ se = sym(se);
+ symse = sym(se);
+ }
+ if (cdt->outer_face->symedge == se || cdt->outer_face->symedge == symse) {
+ /* Advancing by 2 to get past possible 'sym(se)'. */
+ if (se->next->next == se) {
+ cdt->outer_face->symedge = NULL;
+ }
+ else {
+ cdt->outer_face->symedge = se->next->next;
+ }
+ }
+ else {
+ if (se->face->symedge == se) {
+ se->face->symedge = se->next;
+ }
+ if (symse->face->symedge == symse) {
+ symse->face->symedge = symse->next;
+ }
+ }
+ cdt->delete_edge(se);
+}
+
+/**
+ * Remove all non-constraint edges.
+ */
+template<typename T> void remove_non_constraint_edges(CDT_state<T> *cdt_state)
+{
+ for (CDTEdge<T> *e : cdt_state->cdt.edges) {
+ SymEdge<T> *se = &e->symedges[0];
+ if (!is_deleted_edge(e) && !is_constrained_edge(e)) {
+ dissolve_symedge(cdt_state, se);
+ }
+ }
+}
+
+/*
+ * Remove the non-constraint edges, but leave enough of them so that all of the
+ * faces that would be #BMesh faces (that is, the faces that have some input representative)
+ * are valid: they can't have holes, they can't have repeated vertices, and they can't have
+ * repeated edges.
+ *
+ * Not essential, but to make the result look more aesthetically nice,
+ * remove the edges in order of decreasing length, so that it is more likely that the
+ * final remaining support edges are short, and therefore likely to make a fairly
+ * direct path from an outer face to an inner hole face.
+ */
+
+/**
+ * For sorting edges by decreasing length (squared).
+ */
+template<typename T> struct EdgeToSort {
+ T len_squared = T(0);
+ CDTEdge<T> *e{nullptr};
+
+ EdgeToSort() = default;
+ EdgeToSort(const EdgeToSort &other) : len_squared(other.len_squared), e(other.e)
+ {
+ }
+ EdgeToSort(EdgeToSort &&other) noexcept : len_squared(std::move(other.len_squared)), e(other.e)
+ {
+ }
+ ~EdgeToSort() = default;
+ EdgeToSort &operator=(const EdgeToSort &other)
+ {
+ if (this != &other) {
+ len_squared = other.len_squared;
+ e = other.e;
+ }
+ return *this;
+ }
+ EdgeToSort &operator=(EdgeToSort &&other)
+ {
+ len_squared = std::move(other.len_squared);
+ e = other.e;
+ return *this;
+ }
+};
+
+template<typename T> void remove_non_constraint_edges_leave_valid_bmesh(CDT_state<T> *cdt_state)
+{
+ CDTArrangement<T> *cdt = &cdt_state->cdt;
+ size_t nedges = cdt->edges.size();
+ if (nedges == 0) {
+ return;
+ }
+ Vector<EdgeToSort<T>> dissolvable_edges;
+ dissolvable_edges.reserve(cdt->edges.size());
+ int i = 0;
+ for (CDTEdge<T> *e : cdt->edges) {
+ if (!is_deleted_edge(e) && !is_constrained_edge(e)) {
+ dissolvable_edges.append(EdgeToSort<T>());
+ dissolvable_edges[i].e = e;
+ const vec2<T> &co1 = e->symedges[0].vert->co;
+ const vec2<T> &co2 = e->symedges[1].vert->co;
+ dissolvable_edges[i].len_squared = vec2<T>::distance_squared(co1, co2);
+ i++;
+ }
+ }
+ std::sort(dissolvable_edges.begin(),
+ dissolvable_edges.end(),
+ [](const EdgeToSort<T> &a, const EdgeToSort<T> &b) -> bool {
+ return (a.len_squared < b.len_squared);
+ });
+ for (EdgeToSort<T> &ets : dissolvable_edges) {
+ CDTEdge<T> *e = ets.e;
+ SymEdge<T> *se = &e->symedges[0];
+ bool dissolve = true;
+ CDTFace<T> *fleft = se->face;
+ CDTFace<T> *fright = sym(se)->face;
+ if (fleft != cdt->outer_face && fright != cdt->outer_face &&
+ (fleft->input_ids != nullptr || fright->input_ids != nullptr)) {
+ /* Is there another #SymEdge with same left and right faces?
+ * Or is there a vertex not part of e touching the same left and right faces? */
+ for (SymEdge<T> *se2 = se->next; dissolve && se2 != se; se2 = se2->next) {
+ if (sym(se2)->face == fright ||
+ (se2->vert != se->next->vert && vert_touches_face(se2->vert, fright))) {
+ dissolve = false;
+ }
+ }
+ }
+
+ if (dissolve) {
+ dissolve_symedge(cdt_state, se);
+ }
+ }
+}
+
+template<typename T> void remove_outer_edges_until_constraints(CDT_state<T> *cdt_state)
+{
+ // LinkNode *fstack = NULL;
+ // SymEdge *se, *se_start;
+ // CDTFace *f, *fsym;
+ int visit = ++cdt_state->visit_count;
+
+ cdt_state->cdt.outer_face->visit_index = visit;
+ /* Walk around outer face, adding faces on other side of dissolvable edges to stack. */
+ Vector<CDTFace<T> *> fstack;
+ SymEdge<T> *se_start = cdt_state->cdt.outer_face->symedge;
+ SymEdge<T> *se = se_start;
+ do {
+ if (!is_constrained_edge(se->edge)) {
+ CDTFace<T> *fsym = sym(se)->face;
+ if (fsym->visit_index != visit) {
+ fstack.append(fsym);
+ }
+ }
+ } while ((se = se->next) != se_start);
+
+ while (!fstack.is_empty()) {
+ LinkNode *to_dissolve = nullptr;
+ bool dissolvable;
+ CDTFace<T> *f = fstack.pop_last();
+ if (f->visit_index == visit) {
+ continue;
+ }
+ BLI_assert(f != cdt_state->cdt.outer_face);
+ f->visit_index = visit;
+ se_start = se = f->symedge;
+ do {
+ dissolvable = !is_constrained_edge(se->edge);
+ if (dissolvable) {
+ CDTFace<T> *fsym = sym(se)->face;
+ if (fsym->visit_index != visit) {
+ fstack.append(fsym);
+ }
+ else {
+ BLI_linklist_prepend(&to_dissolve, se);
+ }
+ }
+ se = se->next;
+ } while (se != se_start);
+ while (to_dissolve != NULL) {
+ se = static_cast<SymEdge<T> *>(BLI_linklist_pop(&to_dissolve));
+ if (se->next != NULL) {
+ dissolve_symedge(cdt_state, se);
+ }
+ }
+ }
+}
+
+/**
+ * Remove edges and merge faces to get desired output, as per options.
+ * \note the cdt cannot be further changed after this.
+ */
+template<typename T>
+void prepare_cdt_for_output(CDT_state<T> *cdt_state, const CDT_output_type output_type)
+{
+ CDTArrangement<T> *cdt = &cdt_state->cdt;
+ if (cdt->edges.is_empty()) {
+ return;
+ }
+
+ /* Make sure all non-deleted faces have a symedge. */
+ for (CDTEdge<T> *e : cdt->edges) {
+ if (!is_deleted_edge(e)) {
+ if (e->symedges[0].face->symedge == nullptr) {
+ e->symedges[0].face->symedge = &e->symedges[0];
+ }
+ if (e->symedges[1].face->symedge == nullptr) {
+ e->symedges[1].face->symedge = &e->symedges[1];
+ }
+ }
+ }
+
+ if (output_type == CDT_CONSTRAINTS) {
+ remove_non_constraint_edges(cdt_state);
+ }
+ else if (output_type == CDT_CONSTRAINTS_VALID_BMESH) {
+ remove_non_constraint_edges_leave_valid_bmesh(cdt_state);
+ }
+ else if (output_type == CDT_INSIDE) {
+ remove_outer_edges_until_constraints(cdt_state);
+ }
+}
+
+template<typename T>
+CDT_result<T> get_cdt_output(CDT_state<T> *cdt_state,
+ const CDT_input<T> UNUSED(input),
+ CDT_output_type output_type)
+{
+ prepare_cdt_for_output(cdt_state, output_type);
+ CDT_result<T> result;
+ CDTArrangement<T> *cdt = &cdt_state->cdt;
+ result.face_edge_offset = cdt_state->face_edge_offset;
+
+ /* All verts without a merge_to_index will be output.
+ * vert_to_output_map[i] will hold the output vertex index
+ * corresponding to the vert in position i in cdt->verts.
+ * This first loop sets vert_to_output_map for un-merged verts. */
+ int verts_size = cdt->verts.size();
+ Array<int> vert_to_output_map(verts_size);
+ int nv = 0;
+ for (int i = 0; i < verts_size; ++i) {
+ CDTVert<T> *v = cdt->verts[i];
+ if (v->merge_to_index == -1) {
+ vert_to_output_map[i] = nv;
+ ++nv;
+ }
+ }
+ if (nv <= 0) {
+ return result;
+ }
+ /* Now we can set vert_to_output_map for merged verts,
+ * and also add the input indices of merged verts to the input_ids
+ * list of the merge target if they were an original input id. */
+ if (nv < verts_size) {
+ for (int i = 0; i < verts_size; ++i) {
+ CDTVert<T> *v = cdt->verts[i];
+ if (v->merge_to_index != -1) {
+ if (i < cdt_state->input_vert_tot) {
+ add_to_input_ids(&cdt->verts[v->merge_to_index]->input_ids, i);
+ }
+ vert_to_output_map[i] = vert_to_output_map[v->merge_to_index];
+ }
+ }
+ }
+ result.vert = Array<vec2<T>>(nv);
+ result.vert_orig = Array<Vector<int>>(nv);
+ int i_out = 0;
+ for (int i = 0; i < verts_size; ++i) {
+ CDTVert<T> *v = cdt->verts[i];
+ if (v->merge_to_index == -1) {
+ result.vert[i_out] = v->co;
+ if (i < cdt_state->input_vert_tot) {
+ result.vert_orig[i_out].append(i);
+ }
+ for (LinkNode *ln = v->input_ids; ln; ln = ln->next) {
+ result.vert_orig[i_out].append(POINTER_AS_INT(ln->link));
+ }
+ ++i_out;
+ }
+ }
+
+ /* All non-deleted edges will be output. */
+ int ne = std::count_if(cdt->edges.begin(), cdt->edges.end(), [](const CDTEdge<T> *e) -> bool {
+ return !is_deleted_edge(e);
+ });
+ result.edge = Array<std::pair<int, int>>(ne);
+ result.edge_orig = Array<Vector<int>>(ne);
+ int e_out = 0;
+ for (const CDTEdge<T> *e : cdt->edges) {
+ if (!is_deleted_edge(e)) {
+ int vo1 = vert_to_output_map[e->symedges[0].vert->index];
+ int vo2 = vert_to_output_map[e->symedges[1].vert->index];
+ result.edge[e_out] = std::pair<int, int>(vo1, vo2);
+ for (LinkNode *ln = e->input_ids; ln; ln = ln->next) {
+ result.edge_orig[e_out].append(POINTER_AS_INT(ln->link));
+ }
+ ++e_out;
+ }
+ }
+
+ /* All non-deleted, non-outer faces will be output. */
+ int nf = std::count_if(cdt->faces.begin(), cdt->faces.end(), [=](const CDTFace<T> *f) -> bool {
+ return !f->deleted && f != cdt->outer_face;
+ });
+ result.face = Array<Vector<int>>(nf);
+ result.face_orig = Array<Vector<int>>(nf);
+ int f_out = 0;
+ for (const CDTFace<T> *f : cdt->faces) {
+ if (!f->deleted && f != cdt->outer_face) {
+ SymEdge<T> *se = f->symedge;
+ BLI_assert(se != nullptr);
+ SymEdge<T> *se_start = se;
+ do {
+ result.face[f_out].append(vert_to_output_map[se->vert->index]);
+ se = se->next;
+ } while (se != se_start);
+ for (LinkNode *ln = f->input_ids; ln; ln = ln->next) {
+ result.face_orig[f_out].append(POINTER_AS_INT(ln->link));
+ }
+ ++f_out;
+ }
+ }
+ return result;
+}
+
+/**
+ * Add all the input verts into cdt. This will deduplicate,
+ * setting vertices merge_to_index to show merges.
+ */
+template<typename T> void add_input_verts(CDT_state<T> *cdt_state, const CDT_input<T> &input)
+{
+ for (int i = 0; i < cdt_state->input_vert_tot; ++i) {
+ cdt_state->cdt.add_vert(input.vert[i]);
+ }
+}
+
+template<typename T>
+CDT_result<T> delaunay_calc(const CDT_input<T> &input, CDT_output_type output_type)
+{
+ int nv = input.vert.size();
+ int ne = input.edge.size();
+ int nf = input.face.size();
+ CDT_state<T> cdt_state(nv, ne, nf, input.epsilon);
+ add_input_verts(&cdt_state, input);
+ initial_triangulation(&cdt_state.cdt);
+ add_edge_constraints(&cdt_state, input);
+ add_face_constraints(&cdt_state, input);
+ return get_cdt_output(&cdt_state, input, output_type);
+}
+
+blender::meshintersect::CDT_result<double> delaunay_2d_calc(const CDT_input<double> &input,
+ CDT_output_type output_type)
+{
+ return delaunay_calc(input, output_type);
+}
+
+#ifdef WITH_GMP
+blender::meshintersect::CDT_result<mpq_class> delaunay_2d_calc(const CDT_input<mpq_class> &input,
+ CDT_output_type output_type)
+{
+ return delaunay_calc(input, output_type);
+}
+#endif
+
+} /* namespace blender::meshintersect */
+
+/* C interface. */
+
+/**
+ This function uses the double version of #CDT::delaunay_calc.
+ * Almost all of the work here is to convert between C++ #Arrays<Vector<int>>
+ * and a C version that linearizes all the elements and uses a "start"
+ * and "len" array to say where the individual vectors start and how
+ * long they are.
+ */
+extern "C" ::CDT_result *BLI_delaunay_2d_cdt_calc(const ::CDT_input *input,
+ const CDT_output_type output_type)
+{
+ blender::meshintersect::CDT_input<double> in;
+ in.vert = blender::Array<blender::meshintersect::vec2<double>>(input->verts_len);
+ in.edge = blender::Array<std::pair<int, int>>(input->edges_len);
+ in.face = blender::Array<blender::Vector<int>>(input->faces_len);
+ for (int v = 0; v < input->verts_len; ++v) {
+ double x = static_cast<double>(input->vert_coords[v][0]);
+ double y = static_cast<double>(input->vert_coords[v][1]);
+ in.vert[v] = blender::meshintersect::vec2<double>(x, y);
+ }
+ for (int e = 0; e < input->edges_len; ++e) {
+ in.edge[e] = std::pair<int, int>(input->edges[e][0], input->edges[e][1]);
+ }
+ for (int f = 0; f < input->faces_len; ++f) {
+ in.face[f] = blender::Vector<int>(input->faces_len_table[f]);
+ int fstart = input->faces_start_table[f];
+ for (int j = 0; j < input->faces_len_table[f]; ++j) {
+ in.face[f][j] = input->faces[fstart + j];
+ }
+ }
+ in.epsilon = static_cast<double>(input->epsilon);
+
+ blender::meshintersect::CDT_result<double> res = blender::meshintersect::delaunay_2d_calc(
+ in, output_type);
+
+ ::CDT_result *output = static_cast<::CDT_result *>(MEM_mallocN(sizeof(*output), __func__));
+ int nv = output->verts_len = res.vert.size();
+ int ne = output->edges_len = res.edge.size();
+ int nf = output->faces_len = res.face.size();
+ int tot_v_orig = 0;
+ int tot_e_orig = 0;
+ int tot_f_orig = 0;
+ int tot_f_lens = 0;
+ for (int v = 0; v < nv; ++v) {
+ tot_v_orig += res.vert_orig[v].size();
+ }
+ for (int e = 0; e < ne; ++e) {
+ tot_e_orig += res.edge_orig[e].size();
+ }
+ for (int f = 0; f < nf; ++f) {
+ tot_f_orig += res.face_orig[f].size();
+ tot_f_lens += res.face[f].size();
+ }
+
+ output->vert_coords = static_cast<decltype(output->vert_coords)>(
+ MEM_malloc_arrayN(nv, sizeof(output->vert_coords[0]), __func__));
+ output->verts_orig = static_cast<int *>(MEM_malloc_arrayN(tot_v_orig, sizeof(int), __func__));
+ output->verts_orig_start_table = static_cast<int *>(
+ MEM_malloc_arrayN(nv, sizeof(int), __func__));
+ output->verts_orig_len_table = static_cast<int *>(MEM_malloc_arrayN(nv, sizeof(int), __func__));
+ output->edges = static_cast<decltype(output->edges)>(
+ MEM_malloc_arrayN(ne, sizeof(output->edges[0]), __func__));
+ output->edges_orig = static_cast<int *>(MEM_malloc_arrayN(tot_e_orig, sizeof(int), __func__));
+ output->edges_orig_start_table = static_cast<int *>(
+ MEM_malloc_arrayN(ne, sizeof(int), __func__));
+ output->edges_orig_len_table = static_cast<int *>(MEM_malloc_arrayN(ne, sizeof(int), __func__));
+ output->faces = static_cast<int *>(MEM_malloc_arrayN(tot_f_lens, sizeof(int), __func__));
+ output->faces_start_table = static_cast<int *>(MEM_malloc_arrayN(nf, sizeof(int), __func__));
+ output->faces_len_table = static_cast<int *>(MEM_malloc_arrayN(nf, sizeof(int), __func__));
+ output->faces_orig = static_cast<int *>(MEM_malloc_arrayN(tot_f_orig, sizeof(int), __func__));
+ output->faces_orig_start_table = static_cast<int *>(
+ MEM_malloc_arrayN(nf, sizeof(int), __func__));
+ output->faces_orig_len_table = static_cast<int *>(MEM_malloc_arrayN(nf, sizeof(int), __func__));
+
+ int v_orig_index = 0;
+ for (int v = 0; v < nv; ++v) {
+ output->vert_coords[v][0] = static_cast<float>(res.vert[v][0]);
+ output->vert_coords[v][1] = static_cast<float>(res.vert[v][1]);
+ int this_start = v_orig_index;
+ output->verts_orig_start_table[v] = this_start;
+ for (int j : res.vert_orig[v].index_range()) {
+ output->verts_orig[v_orig_index++] = res.vert_orig[v][j];
+ }
+ output->verts_orig_len_table[v] = v_orig_index - this_start;
+ }
+ int e_orig_index = 0;
+ for (int e = 0; e < ne; ++e) {
+ output->edges[e][0] = res.edge[e].first;
+ output->edges[e][1] = res.edge[e].second;
+ int this_start = e_orig_index;
+ output->edges_orig_start_table[e] = this_start;
+ for (int j : res.edge_orig[e].index_range()) {
+ output->edges_orig[e_orig_index++] = res.edge_orig[e][j];
+ }
+ output->edges_orig_len_table[e] = e_orig_index - this_start;
+ }
+ int f_orig_index = 0;
+ int f_index = 0;
+ for (int f = 0; f < nf; ++f) {
+ output->faces_start_table[f] = f_index;
+ int flen = res.face[f].size();
+ output->faces_len_table[f] = flen;
+ for (int j = 0; j < flen; ++j) {
+ output->faces[f_index++] = res.face[f][j];
+ }
+ int this_start = f_orig_index;
+ output->faces_orig_start_table[f] = this_start;
+ for (int k : res.face_orig[f].index_range()) {
+ output->faces_orig[f_orig_index++] = res.face_orig[f][k];
+ }
+ output->faces_orig_len_table[f] = f_orig_index - this_start;
+ }
+ return output;
+}
+
+extern "C" void BLI_delaunay_2d_cdt_free(::CDT_result *result)
+{
+ MEM_freeN(result->vert_coords);
+ MEM_freeN(result->edges);
+ MEM_freeN(result->faces);
+ MEM_freeN(result->faces_start_table);
+ MEM_freeN(result->faces_len_table);
+ MEM_freeN(result->verts_orig);
+ MEM_freeN(result->verts_orig_start_table);
+ MEM_freeN(result->verts_orig_len_table);
+ MEM_freeN(result->edges_orig);
+ MEM_freeN(result->edges_orig_start_table);
+ MEM_freeN(result->edges_orig_len_table);
+ MEM_freeN(result->faces_orig);
+ MEM_freeN(result->faces_orig_start_table);
+ MEM_freeN(result->faces_orig_len_table);
+ MEM_freeN(result);
+}
diff --git a/source/blender/blenlib/intern/math_boolean.cc b/source/blender/blenlib/intern/math_boolean.cc
new file mode 100644
index 00000000000..2210911ad9c
--- /dev/null
+++ b/source/blender/blenlib/intern/math_boolean.cc
@@ -0,0 +1,2533 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup bli
+ */
+
+#include "BLI_double2.hh"
+#include "BLI_double3.hh"
+#include "BLI_float2.hh"
+#include "BLI_float3.hh"
+#include "BLI_hash.hh"
+#include "BLI_math_boolean.hh"
+#include "BLI_math_mpq.hh"
+#include "BLI_mpq2.hh"
+#include "BLI_mpq3.hh"
+#include "BLI_span.hh"
+#include "BLI_utildefines.h"
+
+namespace blender {
+
+#ifdef WITH_GMP
+/**
+ * Return +1 if a, b, c are in CCW order around a circle in the plane.
+ * Return -1 if they are in CW order, and 0 if they are in line.
+ */
+int orient2d(const mpq2 &a, const mpq2 &b, const mpq2 &c)
+{
+ mpq_class detleft = (a[0] - c[0]) * (b[1] - c[1]);
+ mpq_class detright = (a[1] - c[1]) * (b[0] - c[0]);
+ mpq_class det = detleft - detright;
+ return sgn(det);
+}
+
+/**
+ Return +1 if d is in the oriented circle through a, b, and c.
+ * The oriented circle goes CCW through a, b, and c.
+ * Return -1 if d is outside, and 0 if it is on the circle.
+ */
+int incircle(const mpq2 &a, const mpq2 &b, const mpq2 &c, const mpq2 &d)
+{
+ mpq_class adx = a[0] - d[0];
+ mpq_class bdx = b[0] - d[0];
+ mpq_class cdx = c[0] - d[0];
+ mpq_class ady = a[1] - d[1];
+ mpq_class bdy = b[1] - d[1];
+ mpq_class cdy = c[1] - d[1];
+
+ mpq_class bdxcdy = bdx * cdy;
+ mpq_class cdxbdy = cdx * bdy;
+ mpq_class alift = adx * adx + ady * ady;
+
+ mpq_class cdxady = cdx * ady;
+ mpq_class adxcdy = adx * cdy;
+ mpq_class blift = bdx * bdx + bdy * bdy;
+
+ mpq_class adxbdy = adx * bdy;
+ mpq_class bdxady = bdx * ady;
+ mpq_class clift = cdx * cdx + cdy * cdy;
+
+ mpq_class det = alift * (bdxcdy - cdxbdy) + blift * (cdxady - adxcdy) +
+ clift * (adxbdy - bdxady);
+ return sgn(det);
+}
+
+/**
+ * Return +1 if d is below the plane containing a, b, c (which appear
+ * CCW when viewed from above the plane).
+ * Return -1 if d is above the plane.
+ * Return 0 if it is on the plane.
+ */
+int orient3d(const mpq3 &a, const mpq3 &b, const mpq3 &c, const mpq3 &d)
+{
+ mpq_class adx = a[0] - d[0];
+ mpq_class bdx = b[0] - d[0];
+ mpq_class cdx = c[0] - d[0];
+ mpq_class ady = a[1] - d[1];
+ mpq_class bdy = b[1] - d[1];
+ mpq_class cdy = c[1] - d[1];
+ mpq_class adz = a[2] - d[2];
+ mpq_class bdz = b[2] - d[2];
+ mpq_class cdz = c[2] - d[2];
+
+ mpq_class bdxcdy = bdx * cdy;
+ mpq_class cdxbdy = cdx * bdy;
+
+ mpq_class cdxady = cdx * ady;
+ mpq_class adxcdy = adx * cdy;
+
+ mpq_class adxbdy = adx * bdy;
+ mpq_class bdxady = bdx * ady;
+
+ mpq_class det = adz * (bdxcdy - cdxbdy) + bdz * (cdxady - adxcdy) + cdz * (adxbdy - bdxady);
+ return sgn(det);
+}
+#endif /* WITH_GMP */
+
+/**
+ * For double versions of orient and incircle functions, use robust predicates
+ * that give exact answers for double inputs.
+ * First, encapsulate functions frm Jonathan Shewchuk's implementation.
+ * After this namespace, see the implementation of the double3 primitives.
+ */
+namespace robust_pred {
+
+/* Using Shewchuk's file here, edited to removed unneeded functions,
+ * change REAL to double everywhere, added const to some arguments,
+ * and to export only the following declared non-static functions.
+ *
+ * Since this is C++, an instantiated singleton class is used to make
+ * sure that exactinit() is called once.
+ * (Because of undefinedness of when this is called in initialization of all
+ * modules, other modules shouldn't use these functions in initialization.)
+ */
+
+void exactinit();
+double orient2dfast(const double *pa, const double *pb, const double *pc);
+double orient2d(const double *pa, const double *pb, const double *pc);
+double orient3dfast(const double *pa, const double *pb, const double *pc, const double *pd);
+double orient3d(const double *pa, const double *pb, const double *pc, const double *pd);
+double incirclefast(const double *pa, const double *pb, const double *pc, const double *pd);
+double incircle(const double *pa, const double *pb, const double *pc, const double *pd);
+double inspherefast(
+ const double *pa, const double *pb, const double *pc, const double *pd, const double *pe);
+double insphere(
+ const double *pa, const double *pb, const double *pc, const double *pd, const double *pe);
+
+class RobustInitCaller {
+ public:
+ RobustInitCaller()
+ {
+ exactinit();
+ }
+};
+
+static RobustInitCaller init_caller;
+
+/* Routines for Arbitrary Precision Floating-point Arithmetic
+ * and Fast Robust Geometric Predicates
+ * (predicates.c)
+ *
+ * May 18, 1996
+ *
+ * Placed in the public domain by
+ * Jonathan Richard Shewchuk
+ * School of Computer Science
+ * Carnegie Mellon University
+ * 5000 Forbes Avenue
+ * Pittsburgh, Pennsylvania 15213-3891
+ * jrs@cs.cmu.edu
+ *
+ * This file contains C implementation of algorithms for exact addition
+ * and multiplication of floating-point numbers, and predicates for
+ * robustly performing the orientation and incircle tests used in
+ * computational geometry. The algorithms and underlying theory are
+ * described in Jonathan Richard Shewchuk. "Adaptive Precision Floating-
+ * Point Arithmetic and Fast Robust Geometric Predicates." Technical
+ * Report CMU-CS-96-140, School of Computer Science, Carnegie Mellon
+ * University, Pittsburgh, Pennsylvania, May 1996. (Submitted to
+ * Discrete & Computational Geometry.)
+ *
+ * This file, the paper listed above, and other information are available
+ * from the Web page http://www.cs.cmu.edu/~quake/robust.html .
+ *
+ *
+ * Using this code:
+ *
+ * First, read the short or long version of the paper (from the Web page above).
+ *
+ * Be sure to call #exactinit() once, before calling any of the arithmetic
+ * functions or geometric predicates. Also be sure to turn on the
+ * optimizer when compiling this file.
+ */
+
+/* On some machines, the exact arithmetic routines might be defeated by the
+ * use of internal extended precision floating-point registers. Sometimes
+ * this problem can be fixed by defining certain values to be volatile,
+ * thus forcing them to be stored to memory and rounded off. This isn't
+ * a great solution, though, as it slows the arithmetic down.
+ *
+ * To try this out, write "#define INEXACT volatile" below. Normally,
+ * however, INEXACT should be defined to be nothing. ("#define INEXACT".)
+ */
+
+#define INEXACT /* Nothing */
+/* #define INEXACT volatile */
+
+/* Which of the following two methods of finding the absolute values is
+ * fastest is compiler-dependent. A few compilers can inline and optimize
+ * the fabs() call; but most will incur the overhead of a function call,
+ * which is disastrously slow. A faster way on IEEE machines might be to
+ * mask the appropriate bit, but that's difficult to do in C.
+ */
+
+#define Absolute(a) ((a) >= 0.0 ? (a) : -(a))
+/* #define Absolute(a) fabs(a) */
+
+/* Many of the operations are broken up into two pieces, a main part that
+ * performs an approximate operation, and a "tail" that computes the
+ * round-off error of that operation.
+ *
+ * The operations Fast_Two_Sum(), Fast_Two_Diff(), Two_Sum(), Two_Diff(),
+ * Split(), and Two_Product() are all implemented as described in the
+ * reference. Each of these macros requires certain variables to be
+ * defined in the calling routine. The variables `bvirt', `c', `abig',
+ * `_i', `_j', `_k', `_l', `_m', and `_n' are declared `INEXACT' because
+ * they store the result of an operation that may incur round-off error.
+ * The input parameter `x' (or the highest numbered `x_' parameter) must
+ * also be declared `INEXACT'.
+ */
+
+#define Fast_Two_Sum_Tail(a, b, x, y) \
+ bvirt = x - a; \
+ y = b - bvirt
+
+#define Fast_Two_Sum(a, b, x, y) \
+ x = (double)(a + b); \
+ Fast_Two_Sum_Tail(a, b, x, y)
+
+#define Fast_Two_Diff_Tail(a, b, x, y) \
+ bvirt = a - x; \
+ y = bvirt - b
+
+#define Fast_Two_Diff(a, b, x, y) \
+ x = (double)(a - b); \
+ Fast_Two_Diff_Tail(a, b, x, y)
+
+#define Two_Sum_Tail(a, b, x, y) \
+ bvirt = (double)(x - a); \
+ avirt = x - bvirt; \
+ bround = b - bvirt; \
+ around = a - avirt; \
+ y = around + bround
+
+#define Two_Sum(a, b, x, y) \
+ x = (double)(a + b); \
+ Two_Sum_Tail(a, b, x, y)
+
+#define Two_Diff_Tail(a, b, x, y) \
+ bvirt = (double)(a - x); \
+ avirt = x + bvirt; \
+ bround = bvirt - b; \
+ around = a - avirt; \
+ y = around + bround
+
+#define Two_Diff(a, b, x, y) \
+ x = (double)(a - b); \
+ Two_Diff_Tail(a, b, x, y)
+
+#define Split(a, ahi, alo) \
+ c = (double)(splitter * a); \
+ abig = (double)(c - a); \
+ ahi = c - abig; \
+ alo = a - ahi
+
+#define Two_Product_Tail(a, b, x, y) \
+ Split(a, ahi, alo); \
+ Split(b, bhi, blo); \
+ err1 = x - (ahi * bhi); \
+ err2 = err1 - (alo * bhi); \
+ err3 = err2 - (ahi * blo); \
+ y = (alo * blo) - err3
+
+#define Two_Product(a, b, x, y) \
+ x = (double)(a * b); \
+ Two_Product_Tail(a, b, x, y)
+
+#define Two_Product_Presplit(a, b, bhi, blo, x, y) \
+ x = (double)(a * b); \
+ Split(a, ahi, alo); \
+ err1 = x - (ahi * bhi); \
+ err2 = err1 - (alo * bhi); \
+ err3 = err2 - (ahi * blo); \
+ y = (alo * blo) - err3
+
+#define Two_Product_2Presplit(a, ahi, alo, b, bhi, blo, x, y) \
+ x = (double)(a * b); \
+ err1 = x - (ahi * bhi); \
+ err2 = err1 - (alo * bhi); \
+ err3 = err2 - (ahi * blo); \
+ y = (alo * blo) - err3
+
+#define Square_Tail(a, x, y) \
+ Split(a, ahi, alo); \
+ err1 = x - (ahi * ahi); \
+ err3 = err1 - ((ahi + ahi) * alo); \
+ y = (alo * alo) - err3
+
+#define Square(a, x, y) \
+ x = (double)(a * a); \
+ Square_Tail(a, x, y)
+
+#define Two_One_Sum(a1, a0, b, x2, x1, x0) \
+ Two_Sum(a0, b, _i, x0); \
+ Two_Sum(a1, _i, x2, x1)
+
+#define Two_One_Diff(a1, a0, b, x2, x1, x0) \
+ Two_Diff(a0, b, _i, x0); \
+ Two_Sum(a1, _i, x2, x1)
+
+#define Two_Two_Sum(a1, a0, b1, b0, x3, x2, x1, x0) \
+ Two_One_Sum(a1, a0, b0, _j, _0, x0); \
+ Two_One_Sum(_j, _0, b1, x3, x2, x1)
+
+#define Two_Two_Diff(a1, a0, b1, b0, x3, x2, x1, x0) \
+ Two_One_Diff(a1, a0, b0, _j, _0, x0); \
+ Two_One_Diff(_j, _0, b1, x3, x2, x1)
+
+#define Four_One_Sum(a3, a2, a1, a0, b, x4, x3, x2, x1, x0) \
+ Two_One_Sum(a1, a0, b, _j, x1, x0); \
+ Two_One_Sum(a3, a2, _j, x4, x3, x2)
+
+#define Four_Two_Sum(a3, a2, a1, a0, b1, b0, x5, x4, x3, x2, x1, x0) \
+ Four_One_Sum(a3, a2, a1, a0, b0, _k, _2, _1, _0, x0); \
+ Four_One_Sum(_k, _2, _1, _0, b1, x5, x4, x3, x2, x1)
+
+#define Four_Four_Sum(a3, a2, a1, a0, b4, b3, b1, b0, x7, x6, x5, x4, x3, x2, x1, x0) \
+ Four_Two_Sum(a3, a2, a1, a0, b1, b0, _l, _2, _1, _0, x1, x0); \
+ Four_Two_Sum(_l, _2, _1, _0, b4, b3, x7, x6, x5, x4, x3, x2)
+
+#define Eight_One_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b, x8, x7, x6, x5, x4, x3, x2, x1, x0) \
+ Four_One_Sum(a3, a2, a1, a0, b, _j, x3, x2, x1, x0); \
+ Four_One_Sum(a7, a6, a5, a4, _j, x8, x7, x6, x5, x4)
+
+#define Eight_Two_Sum( \
+ a7, a6, a5, a4, a3, a2, a1, a0, b1, b0, x9, x8, x7, x6, x5, x4, x3, x2, x1, x0) \
+ Eight_One_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b0, _k, _6, _5, _4, _3, _2, _1, _0, x0); \
+ Eight_One_Sum(_k, _6, _5, _4, _3, _2, _1, _0, b1, x9, x8, x7, x6, x5, x4, x3, x2, x1)
+
+#define Eight_Four_Sum(a7, \
+ a6, \
+ a5, \
+ a4, \
+ a3, \
+ a2, \
+ a1, \
+ a0, \
+ b4, \
+ b3, \
+ b1, \
+ b0, \
+ x11, \
+ x10, \
+ x9, \
+ x8, \
+ x7, \
+ x6, \
+ x5, \
+ x4, \
+ x3, \
+ x2, \
+ x1, \
+ x0) \
+ Eight_Two_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b1, b0, _l, _6, _5, _4, _3, _2, _1, _0, x1, x0); \
+ Eight_Two_Sum(_l, _6, _5, _4, _3, _2, _1, _0, b4, b3, x11, x10, x9, x8, x7, x6, x5, x4, x3, x2)
+
+#define Two_One_Product(a1, a0, b, x3, x2, x1, x0) \
+ Split(b, bhi, blo); \
+ Two_Product_Presplit(a0, b, bhi, blo, _i, x0); \
+ Two_Product_Presplit(a1, b, bhi, blo, _j, _0); \
+ Two_Sum(_i, _0, _k, x1); \
+ Fast_Two_Sum(_j, _k, x3, x2)
+
+#define Four_One_Product(a3, a2, a1, a0, b, x7, x6, x5, x4, x3, x2, x1, x0) \
+ Split(b, bhi, blo); \
+ Two_Product_Presplit(a0, b, bhi, blo, _i, x0); \
+ Two_Product_Presplit(a1, b, bhi, blo, _j, _0); \
+ Two_Sum(_i, _0, _k, x1); \
+ Fast_Two_Sum(_j, _k, _i, x2); \
+ Two_Product_Presplit(a2, b, bhi, blo, _j, _0); \
+ Two_Sum(_i, _0, _k, x3); \
+ Fast_Two_Sum(_j, _k, _i, x4); \
+ Two_Product_Presplit(a3, b, bhi, blo, _j, _0); \
+ Two_Sum(_i, _0, _k, x5); \
+ Fast_Two_Sum(_j, _k, x7, x6)
+
+#define Two_Two_Product(a1, a0, b1, b0, x7, x6, x5, x4, x3, x2, x1, x0) \
+ Split(a0, a0hi, a0lo); \
+ Split(b0, bhi, blo); \
+ Two_Product_2Presplit(a0, a0hi, a0lo, b0, bhi, blo, _i, x0); \
+ Split(a1, a1hi, a1lo); \
+ Two_Product_2Presplit(a1, a1hi, a1lo, b0, bhi, blo, _j, _0); \
+ Two_Sum(_i, _0, _k, _1); \
+ Fast_Two_Sum(_j, _k, _l, _2); \
+ Split(b1, bhi, blo); \
+ Two_Product_2Presplit(a0, a0hi, a0lo, b1, bhi, blo, _i, _0); \
+ Two_Sum(_1, _0, _k, x1); \
+ Two_Sum(_2, _k, _j, _1); \
+ Two_Sum(_l, _j, _m, _2); \
+ Two_Product_2Presplit(a1, a1hi, a1lo, b1, bhi, blo, _j, _0); \
+ Two_Sum(_i, _0, _n, _0); \
+ Two_Sum(_1, _0, _i, x2); \
+ Two_Sum(_2, _i, _k, _1); \
+ Two_Sum(_m, _k, _l, _2); \
+ Two_Sum(_j, _n, _k, _0); \
+ Two_Sum(_1, _0, _j, x3); \
+ Two_Sum(_2, _j, _i, _1); \
+ Two_Sum(_l, _i, _m, _2); \
+ Two_Sum(_1, _k, _i, x4); \
+ Two_Sum(_2, _i, _k, x5); \
+ Two_Sum(_m, _k, x7, x6)
+
+#define Two_Square(a1, a0, x5, x4, x3, x2, x1, x0) \
+ Square(a0, _j, x0); \
+ _0 = a0 + a0; \
+ Two_Product(a1, _0, _k, _1); \
+ Two_One_Sum(_k, _1, _j, _l, _2, x1); \
+ Square(a1, _j, _1); \
+ Two_Two_Sum(_j, _1, _l, _2, x5, x4, x3, x2)
+
+static double splitter; /* = 2^ceiling(p / 2) + 1. Used to split floats in half. */
+static double epsilon; /* = 2^(-p). Used to estimate round-off errors. */
+/* A set of coefficients used to calculate maximum round-off errors. */
+static double resulterrbound;
+static double ccwerrboundA, ccwerrboundB, ccwerrboundC;
+static double o3derrboundA, o3derrboundB, o3derrboundC;
+static double iccerrboundA, iccerrboundB, iccerrboundC;
+static double isperrboundA, isperrboundB, isperrboundC;
+
+/**
+ * exactinit() Initialize the variables used for exact arithmetic.
+ *
+ * `epsilon' is the largest power of two such that 1.0 + epsilon = 1.0 in
+ * floating-point arithmetic. `epsilon' bounds the relative round-off
+ * error. It is used for floating-point error analysis.
+ *
+ * `splitter' is used to split floating-point numbers into two half-
+ * length significands for exact multiplication.
+ *
+ * I imagine that a highly optimizing compiler might be too smart for its
+ * own good, and somehow cause this routine to fail, if it pretends that
+ * floating-point arithmetic is too much like real arithmetic.
+ *
+ * Don't change this routine unless you fully understand it.
+ */
+
+void exactinit()
+{
+ double half;
+ double check, lastcheck;
+ int every_other;
+
+ every_other = 1;
+ half = 0.5;
+ epsilon = 1.0;
+ splitter = 1.0;
+ check = 1.0;
+ /* Repeatedly divide `epsilon' by two until it is too small to add to
+ * one without causing round-off. (Also check if the sum is equal to
+ * the previous sum, for machines that round up instead of using exact
+ * rounding. Not that this library will work on such machines anyway. */
+ do {
+ lastcheck = check;
+ epsilon *= half;
+ if (every_other) {
+ splitter *= 2.0;
+ }
+ every_other = !every_other;
+ check = 1.0 + epsilon;
+ } while ((check != 1.0) && (check != lastcheck));
+ splitter += 1.0;
+
+ /* Error bounds for orientation and #incircle tests. */
+ resulterrbound = (3.0 + 8.0 * epsilon) * epsilon;
+ ccwerrboundA = (3.0 + 16.0 * epsilon) * epsilon;
+ ccwerrboundB = (2.0 + 12.0 * epsilon) * epsilon;
+ ccwerrboundC = (9.0 + 64.0 * epsilon) * epsilon * epsilon;
+ o3derrboundA = (7.0 + 56.0 * epsilon) * epsilon;
+ o3derrboundB = (3.0 + 28.0 * epsilon) * epsilon;
+ o3derrboundC = (26.0 + 288.0 * epsilon) * epsilon * epsilon;
+ iccerrboundA = (10.0 + 96.0 * epsilon) * epsilon;
+ iccerrboundB = (4.0 + 48.0 * epsilon) * epsilon;
+ iccerrboundC = (44.0 + 576.0 * epsilon) * epsilon * epsilon;
+ isperrboundA = (16.0 + 224.0 * epsilon) * epsilon;
+ isperrboundB = (5.0 + 72.0 * epsilon) * epsilon;
+ isperrboundC = (71.0 + 1408.0 * epsilon) * epsilon * epsilon;
+}
+
+/**
+ * fast_expansion_sum_zeroelim() Sum two expansions, eliminating zero
+ * components from the output expansion.
+ *
+ * Sets h = e + f. See the long version of my paper for details.
+ * h cannot be e or f.
+ */
+static int fast_expansion_sum_zeroelim(
+ int elen, const double *e, int flen, const double *f, double *h)
+{
+ double Q;
+ INEXACT double Qnew;
+ INEXACT double hh;
+ INEXACT double bvirt;
+ double avirt, bround, around;
+ int eindex, findex, hindex;
+ double enow, fnow;
+
+ enow = e[0];
+ fnow = f[0];
+ eindex = findex = 0;
+ if ((fnow > enow) == (fnow > -enow)) {
+ Q = enow;
+ enow = e[++eindex];
+ }
+ else {
+ Q = fnow;
+ fnow = f[++findex];
+ }
+ hindex = 0;
+ if ((eindex < elen) && (findex < flen)) {
+ if ((fnow > enow) == (fnow > -enow)) {
+ Fast_Two_Sum(enow, Q, Qnew, hh);
+ enow = e[++eindex];
+ }
+ else {
+ Fast_Two_Sum(fnow, Q, Qnew, hh);
+ fnow = f[++findex];
+ }
+ Q = Qnew;
+ if (hh != 0.0) {
+ h[hindex++] = hh;
+ }
+ while ((eindex < elen) && (findex < flen)) {
+ if ((fnow > enow) == (fnow > -enow)) {
+ Two_Sum(Q, enow, Qnew, hh);
+ enow = e[++eindex];
+ }
+ else {
+ Two_Sum(Q, fnow, Qnew, hh);
+ fnow = f[++findex];
+ }
+ Q = Qnew;
+ if (hh != 0.0) {
+ h[hindex++] = hh;
+ }
+ }
+ }
+ while (eindex < elen) {
+ Two_Sum(Q, enow, Qnew, hh);
+ enow = e[++eindex];
+ Q = Qnew;
+ if (hh != 0.0) {
+ h[hindex++] = hh;
+ }
+ }
+ while (findex < flen) {
+ Two_Sum(Q, fnow, Qnew, hh);
+ fnow = f[++findex];
+ Q = Qnew;
+ if (hh != 0.0) {
+ h[hindex++] = hh;
+ }
+ }
+ if ((Q != 0.0) || (hindex == 0)) {
+ h[hindex++] = Q;
+ }
+ return hindex;
+}
+
+/* scale_expansion_zeroelim() Multiply an expansion by a scalar,
+ * eliminating zero components from the
+ * output expansion.
+ *
+ * Sets h = be. See either version of my paper for details.
+ * e and h cannot be the same.
+ */
+static int scale_expansion_zeroelim(int elen, const double *e, double b, double *h)
+{
+ INEXACT double Q, sum;
+ double hh;
+ INEXACT double product1;
+ double product0;
+ int eindex, hindex;
+ double enow;
+ INEXACT double bvirt;
+ double avirt, bround, around;
+ INEXACT double c;
+ INEXACT double abig;
+ double ahi, alo, bhi, blo;
+ double err1, err2, err3;
+
+ Split(b, bhi, blo);
+ Two_Product_Presplit(e[0], b, bhi, blo, Q, hh);
+ hindex = 0;
+ if (hh != 0) {
+ h[hindex++] = hh;
+ }
+ for (eindex = 1; eindex < elen; eindex++) {
+ enow = e[eindex];
+ Two_Product_Presplit(enow, b, bhi, blo, product1, product0);
+ Two_Sum(Q, product0, sum, hh);
+ if (hh != 0) {
+ h[hindex++] = hh;
+ }
+ Fast_Two_Sum(product1, sum, Q, hh);
+ if (hh != 0) {
+ h[hindex++] = hh;
+ }
+ }
+ if ((Q != 0.0) || (hindex == 0)) {
+ h[hindex++] = Q;
+ }
+ return hindex;
+}
+
+/* estimate() Produce a one-word estimate of an expansion's value. */
+static double estimate(int elen, const double *e)
+{
+ double Q;
+ int eindex;
+
+ Q = e[0];
+ for (eindex = 1; eindex < elen; eindex++) {
+ Q += e[eindex];
+ }
+ return Q;
+}
+
+/**
+ * orient2dfast() Approximate 2D orientation test. Non-robust.
+ * orient2d() Adaptive exact 2D orientation test. Robust.
+ * Return a positive value if the points pa, pb, and pc occur
+ * in counterclockwise order; a negative value if they occur
+ * in clockwise order; and zero if they are co-linear. The
+ * result is also a rough approximation of twice the signed
+ * area of the triangle defined by the three points.
+ *
+ * The second uses exact arithmetic to ensure a correct answer. The
+ * result returned is the determinant of a matrix. In orient2d() only,
+ * this determinant is computed adaptively, in the sense that exact
+ * arithmetic is used only to the degree it is needed to ensure that the
+ * returned value has the correct sign. Hence, orient2d() is usually quite
+ * fast, but will run more slowly when the input points are co-linear or
+ * nearly so.
+ */
+
+double orient2dfast(const double *pa, const double *pb, const double *pc)
+{
+ double acx, bcx, acy, bcy;
+
+ acx = pa[0] - pc[0];
+ bcx = pb[0] - pc[0];
+ acy = pa[1] - pc[1];
+ bcy = pb[1] - pc[1];
+ return acx * bcy - acy * bcx;
+}
+
+static double orient2dadapt(const double *pa, const double *pb, const double *pc, double detsum)
+{
+ INEXACT double acx, acy, bcx, bcy;
+ double acxtail, acytail, bcxtail, bcytail;
+ INEXACT double detleft, detright;
+ double detlefttail, detrighttail;
+ double det, errbound;
+ double B[4], C1[8], C2[12], D[16];
+ INEXACT double B3;
+ int C1length, C2length, Dlength;
+ double u[4];
+ INEXACT double u3;
+ INEXACT double s1, t1;
+ double s0, t0;
+
+ INEXACT double bvirt;
+ double avirt, bround, around;
+ INEXACT double c;
+ INEXACT double abig;
+ double ahi, alo, bhi, blo;
+ double err1, err2, err3;
+ INEXACT double _i, _j;
+ double _0;
+
+ acx = (double)(pa[0] - pc[0]);
+ bcx = (double)(pb[0] - pc[0]);
+ acy = (double)(pa[1] - pc[1]);
+ bcy = (double)(pb[1] - pc[1]);
+
+ Two_Product(acx, bcy, detleft, detlefttail);
+ Two_Product(acy, bcx, detright, detrighttail);
+
+ Two_Two_Diff(detleft, detlefttail, detright, detrighttail, B3, B[2], B[1], B[0]);
+ B[3] = B3;
+
+ det = estimate(4, B);
+ errbound = ccwerrboundB * detsum;
+ if ((det >= errbound) || (-det >= errbound)) {
+ return det;
+ }
+
+ Two_Diff_Tail(pa[0], pc[0], acx, acxtail);
+ Two_Diff_Tail(pb[0], pc[0], bcx, bcxtail);
+ Two_Diff_Tail(pa[1], pc[1], acy, acytail);
+ Two_Diff_Tail(pb[1], pc[1], bcy, bcytail);
+
+ if ((acxtail == 0.0) && (acytail == 0.0) && (bcxtail == 0.0) && (bcytail == 0.0)) {
+ return det;
+ }
+
+ errbound = ccwerrboundC * detsum + resulterrbound * Absolute(det);
+ det += (acx * bcytail + bcy * acxtail) - (acy * bcxtail + bcx * acytail);
+ if ((det >= errbound) || (-det >= errbound)) {
+ return det;
+ }
+
+ Two_Product(acxtail, bcy, s1, s0);
+ Two_Product(acytail, bcx, t1, t0);
+ Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ C1length = fast_expansion_sum_zeroelim(4, B, 4, u, C1);
+
+ Two_Product(acx, bcytail, s1, s0);
+ Two_Product(acy, bcxtail, t1, t0);
+ Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ C2length = fast_expansion_sum_zeroelim(C1length, C1, 4, u, C2);
+
+ Two_Product(acxtail, bcytail, s1, s0);
+ Two_Product(acytail, bcxtail, t1, t0);
+ Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ Dlength = fast_expansion_sum_zeroelim(C2length, C2, 4, u, D);
+
+ return (D[Dlength - 1]);
+}
+
+double orient2d(const double *pa, const double *pb, const double *pc)
+{
+ double detleft, detright, det;
+ double detsum, errbound;
+
+ detleft = (pa[0] - pc[0]) * (pb[1] - pc[1]);
+ detright = (pa[1] - pc[1]) * (pb[0] - pc[0]);
+ det = detleft - detright;
+
+ if (detleft > 0.0) {
+ if (detright <= 0.0) {
+ return det;
+ }
+ detsum = detleft + detright;
+ }
+ else if (detleft < 0.0) {
+ if (detright >= 0.0) {
+ return det;
+ }
+ detsum = -detleft - detright;
+ }
+ else {
+ return det;
+ }
+
+ errbound = ccwerrboundA * detsum;
+ if ((det >= errbound) || (-det >= errbound)) {
+ return det;
+ }
+
+ return orient2dadapt(pa, pb, pc, detsum);
+}
+
+/**
+ * orient3dfast() Approximate 3D orientation test. Nonrobust.
+ * orient3d() Adaptive exact 3D orientation test. Robust.
+ *
+ * Return a positive value if the point pd lies below the
+ * plane passing through pa, pb, and pc; "below" is defined so
+ * that pa, pb, and pc appear in counterclockwise order when
+ * viewed from above the plane. Returns a negative value if
+ * pd lies above the plane. Returns zero if the points are
+ * co-planar. The result is also a rough approximation of six
+ * times the signed volume of the tetrahedron defined by the
+ * four points.
+ *
+ * The second uses exact arithmetic to ensure a correct answer. The
+ * result returned is the determinant of a matrix. In orient3d() only,
+ * this determinant is computed adaptively, in the sense that exact
+ * arithmetic is used only to the degree it is needed to ensure that the
+ * returned value has the correct sign. Hence, orient3d() is usually quite
+ * fast, but will run more slowly when the input points are co-planar or
+ * nearly so.
+ */
+
+double orient3dfast(const double *pa, const double *pb, const double *pc, const double *pd)
+{
+ double adx, bdx, cdx;
+ double ady, bdy, cdy;
+ double adz, bdz, cdz;
+
+ adx = pa[0] - pd[0];
+ bdx = pb[0] - pd[0];
+ cdx = pc[0] - pd[0];
+ ady = pa[1] - pd[1];
+ bdy = pb[1] - pd[1];
+ cdy = pc[1] - pd[1];
+ adz = pa[2] - pd[2];
+ bdz = pb[2] - pd[2];
+ cdz = pc[2] - pd[2];
+
+ return adx * (bdy * cdz - bdz * cdy) + bdx * (cdy * adz - cdz * ady) +
+ cdx * (ady * bdz - adz * bdy);
+}
+
+/**
+ * \note since this code comes from an external source, prefer not to break it
+ * up to fix this clang-tidy warning.
+ * NOLINTNEXTLINE: readability-function-size
+ */
+static double orient3dadapt(
+ const double *pa, const double *pb, const double *pc, const double *pd, double permanent)
+{
+ INEXACT double adx, bdx, cdx, ady, bdy, cdy, adz, bdz, cdz;
+ double det, errbound;
+
+ INEXACT double bdxcdy1, cdxbdy1, cdxady1, adxcdy1, adxbdy1, bdxady1;
+ double bdxcdy0, cdxbdy0, cdxady0, adxcdy0, adxbdy0, bdxady0;
+ double bc[4], ca[4], ab[4];
+ INEXACT double bc3, ca3, ab3;
+ double adet[8], bdet[8], cdet[8];
+ int alen, blen, clen;
+ double abdet[16];
+ int ablen;
+ double *finnow, *finother, *finswap;
+ double fin1[192], fin2[192];
+ int finlength;
+
+ double adxtail, bdxtail, cdxtail;
+ double adytail, bdytail, cdytail;
+ double adztail, bdztail, cdztail;
+ INEXACT double at_blarge, at_clarge;
+ INEXACT double bt_clarge, bt_alarge;
+ INEXACT double ct_alarge, ct_blarge;
+ double at_b[4], at_c[4], bt_c[4], bt_a[4], ct_a[4], ct_b[4];
+ int at_blen, at_clen, bt_clen, bt_alen, ct_alen, ct_blen;
+ INEXACT double bdxt_cdy1, cdxt_bdy1, cdxt_ady1;
+ INEXACT double adxt_cdy1, adxt_bdy1, bdxt_ady1;
+ double bdxt_cdy0, cdxt_bdy0, cdxt_ady0;
+ double adxt_cdy0, adxt_bdy0, bdxt_ady0;
+ INEXACT double bdyt_cdx1, cdyt_bdx1, cdyt_adx1;
+ INEXACT double adyt_cdx1, adyt_bdx1, bdyt_adx1;
+ double bdyt_cdx0, cdyt_bdx0, cdyt_adx0;
+ double adyt_cdx0, adyt_bdx0, bdyt_adx0;
+ double bct[8], cat[8], abt[8];
+ int bctlen, catlen, abtlen;
+ INEXACT double bdxt_cdyt1, cdxt_bdyt1, cdxt_adyt1;
+ INEXACT double adxt_cdyt1, adxt_bdyt1, bdxt_adyt1;
+ double bdxt_cdyt0, cdxt_bdyt0, cdxt_adyt0;
+ double adxt_cdyt0, adxt_bdyt0, bdxt_adyt0;
+ double u[4], v[12], w[16];
+ INEXACT double u3;
+ int vlength, wlength;
+ double negate;
+
+ INEXACT double bvirt;
+ double avirt, bround, around;
+ INEXACT double c;
+ INEXACT double abig;
+ double ahi, alo, bhi, blo;
+ double err1, err2, err3;
+ INEXACT double _i, _j, _k;
+ double _0;
+
+ adx = (double)(pa[0] - pd[0]);
+ bdx = (double)(pb[0] - pd[0]);
+ cdx = (double)(pc[0] - pd[0]);
+ ady = (double)(pa[1] - pd[1]);
+ bdy = (double)(pb[1] - pd[1]);
+ cdy = (double)(pc[1] - pd[1]);
+ adz = (double)(pa[2] - pd[2]);
+ bdz = (double)(pb[2] - pd[2]);
+ cdz = (double)(pc[2] - pd[2]);
+
+ Two_Product(bdx, cdy, bdxcdy1, bdxcdy0);
+ Two_Product(cdx, bdy, cdxbdy1, cdxbdy0);
+ Two_Two_Diff(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0, bc3, bc[2], bc[1], bc[0]);
+ bc[3] = bc3;
+ alen = scale_expansion_zeroelim(4, bc, adz, adet);
+
+ Two_Product(cdx, ady, cdxady1, cdxady0);
+ Two_Product(adx, cdy, adxcdy1, adxcdy0);
+ Two_Two_Diff(cdxady1, cdxady0, adxcdy1, adxcdy0, ca3, ca[2], ca[1], ca[0]);
+ ca[3] = ca3;
+ blen = scale_expansion_zeroelim(4, ca, bdz, bdet);
+
+ Two_Product(adx, bdy, adxbdy1, adxbdy0);
+ Two_Product(bdx, ady, bdxady1, bdxady0);
+ Two_Two_Diff(adxbdy1, adxbdy0, bdxady1, bdxady0, ab3, ab[2], ab[1], ab[0]);
+ ab[3] = ab3;
+ clen = scale_expansion_zeroelim(4, ab, cdz, cdet);
+
+ ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
+ finlength = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, fin1);
+
+ det = estimate(finlength, fin1);
+ errbound = o3derrboundB * permanent;
+ if ((det >= errbound) || (-det >= errbound)) {
+ return det;
+ }
+
+ Two_Diff_Tail(pa[0], pd[0], adx, adxtail);
+ Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail);
+ Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail);
+ Two_Diff_Tail(pa[1], pd[1], ady, adytail);
+ Two_Diff_Tail(pb[1], pd[1], bdy, bdytail);
+ Two_Diff_Tail(pc[1], pd[1], cdy, cdytail);
+ Two_Diff_Tail(pa[2], pd[2], adz, adztail);
+ Two_Diff_Tail(pb[2], pd[2], bdz, bdztail);
+ Two_Diff_Tail(pc[2], pd[2], cdz, cdztail);
+
+ if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0) && (adytail == 0.0) &&
+ (bdytail == 0.0) && (cdytail == 0.0) && (adztail == 0.0) && (bdztail == 0.0) &&
+ (cdztail == 0.0)) {
+ return det;
+ }
+
+ errbound = o3derrboundC * permanent + resulterrbound * Absolute(det);
+ det += (adz * ((bdx * cdytail + cdy * bdxtail) - (bdy * cdxtail + cdx * bdytail)) +
+ adztail * (bdx * cdy - bdy * cdx)) +
+ (bdz * ((cdx * adytail + ady * cdxtail) - (cdy * adxtail + adx * cdytail)) +
+ bdztail * (cdx * ady - cdy * adx)) +
+ (cdz * ((adx * bdytail + bdy * adxtail) - (ady * bdxtail + bdx * adytail)) +
+ cdztail * (adx * bdy - ady * bdx));
+ if ((det >= errbound) || (-det >= errbound)) {
+ return det;
+ }
+
+ finnow = fin1;
+ finother = fin2;
+
+ if (adxtail == 0.0) {
+ if (adytail == 0.0) {
+ at_b[0] = 0.0;
+ at_blen = 1;
+ at_c[0] = 0.0;
+ at_clen = 1;
+ }
+ else {
+ negate = -adytail;
+ Two_Product(negate, bdx, at_blarge, at_b[0]);
+ at_b[1] = at_blarge;
+ at_blen = 2;
+ Two_Product(adytail, cdx, at_clarge, at_c[0]);
+ at_c[1] = at_clarge;
+ at_clen = 2;
+ }
+ }
+ else {
+ if (adytail == 0.0) {
+ Two_Product(adxtail, bdy, at_blarge, at_b[0]);
+ at_b[1] = at_blarge;
+ at_blen = 2;
+ negate = -adxtail;
+ Two_Product(negate, cdy, at_clarge, at_c[0]);
+ at_c[1] = at_clarge;
+ at_clen = 2;
+ }
+ else {
+ Two_Product(adxtail, bdy, adxt_bdy1, adxt_bdy0);
+ Two_Product(adytail, bdx, adyt_bdx1, adyt_bdx0);
+ Two_Two_Diff(
+ adxt_bdy1, adxt_bdy0, adyt_bdx1, adyt_bdx0, at_blarge, at_b[2], at_b[1], at_b[0]);
+ at_b[3] = at_blarge;
+ at_blen = 4;
+ Two_Product(adytail, cdx, adyt_cdx1, adyt_cdx0);
+ Two_Product(adxtail, cdy, adxt_cdy1, adxt_cdy0);
+ Two_Two_Diff(
+ adyt_cdx1, adyt_cdx0, adxt_cdy1, adxt_cdy0, at_clarge, at_c[2], at_c[1], at_c[0]);
+ at_c[3] = at_clarge;
+ at_clen = 4;
+ }
+ }
+ if (bdxtail == 0.0) {
+ if (bdytail == 0.0) {
+ bt_c[0] = 0.0;
+ bt_clen = 1;
+ bt_a[0] = 0.0;
+ bt_alen = 1;
+ }
+ else {
+ negate = -bdytail;
+ Two_Product(negate, cdx, bt_clarge, bt_c[0]);
+ bt_c[1] = bt_clarge;
+ bt_clen = 2;
+ Two_Product(bdytail, adx, bt_alarge, bt_a[0]);
+ bt_a[1] = bt_alarge;
+ bt_alen = 2;
+ }
+ }
+ else {
+ if (bdytail == 0.0) {
+ Two_Product(bdxtail, cdy, bt_clarge, bt_c[0]);
+ bt_c[1] = bt_clarge;
+ bt_clen = 2;
+ negate = -bdxtail;
+ Two_Product(negate, ady, bt_alarge, bt_a[0]);
+ bt_a[1] = bt_alarge;
+ bt_alen = 2;
+ }
+ else {
+ Two_Product(bdxtail, cdy, bdxt_cdy1, bdxt_cdy0);
+ Two_Product(bdytail, cdx, bdyt_cdx1, bdyt_cdx0);
+ Two_Two_Diff(
+ bdxt_cdy1, bdxt_cdy0, bdyt_cdx1, bdyt_cdx0, bt_clarge, bt_c[2], bt_c[1], bt_c[0]);
+ bt_c[3] = bt_clarge;
+ bt_clen = 4;
+ Two_Product(bdytail, adx, bdyt_adx1, bdyt_adx0);
+ Two_Product(bdxtail, ady, bdxt_ady1, bdxt_ady0);
+ Two_Two_Diff(
+ bdyt_adx1, bdyt_adx0, bdxt_ady1, bdxt_ady0, bt_alarge, bt_a[2], bt_a[1], bt_a[0]);
+ bt_a[3] = bt_alarge;
+ bt_alen = 4;
+ }
+ }
+ if (cdxtail == 0.0) {
+ if (cdytail == 0.0) {
+ ct_a[0] = 0.0;
+ ct_alen = 1;
+ ct_b[0] = 0.0;
+ ct_blen = 1;
+ }
+ else {
+ negate = -cdytail;
+ Two_Product(negate, adx, ct_alarge, ct_a[0]);
+ ct_a[1] = ct_alarge;
+ ct_alen = 2;
+ Two_Product(cdytail, bdx, ct_blarge, ct_b[0]);
+ ct_b[1] = ct_blarge;
+ ct_blen = 2;
+ }
+ }
+ else {
+ if (cdytail == 0.0) {
+ Two_Product(cdxtail, ady, ct_alarge, ct_a[0]);
+ ct_a[1] = ct_alarge;
+ ct_alen = 2;
+ negate = -cdxtail;
+ Two_Product(negate, bdy, ct_blarge, ct_b[0]);
+ ct_b[1] = ct_blarge;
+ ct_blen = 2;
+ }
+ else {
+ Two_Product(cdxtail, ady, cdxt_ady1, cdxt_ady0);
+ Two_Product(cdytail, adx, cdyt_adx1, cdyt_adx0);
+ Two_Two_Diff(
+ cdxt_ady1, cdxt_ady0, cdyt_adx1, cdyt_adx0, ct_alarge, ct_a[2], ct_a[1], ct_a[0]);
+ ct_a[3] = ct_alarge;
+ ct_alen = 4;
+ Two_Product(cdytail, bdx, cdyt_bdx1, cdyt_bdx0);
+ Two_Product(cdxtail, bdy, cdxt_bdy1, cdxt_bdy0);
+ Two_Two_Diff(
+ cdyt_bdx1, cdyt_bdx0, cdxt_bdy1, cdxt_bdy0, ct_blarge, ct_b[2], ct_b[1], ct_b[0]);
+ ct_b[3] = ct_blarge;
+ ct_blen = 4;
+ }
+ }
+
+ bctlen = fast_expansion_sum_zeroelim(bt_clen, bt_c, ct_blen, ct_b, bct);
+ wlength = scale_expansion_zeroelim(bctlen, bct, adz, w);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+
+ catlen = fast_expansion_sum_zeroelim(ct_alen, ct_a, at_clen, at_c, cat);
+ wlength = scale_expansion_zeroelim(catlen, cat, bdz, w);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+
+ abtlen = fast_expansion_sum_zeroelim(at_blen, at_b, bt_alen, bt_a, abt);
+ wlength = scale_expansion_zeroelim(abtlen, abt, cdz, w);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+
+ if (adztail != 0.0) {
+ vlength = scale_expansion_zeroelim(4, bc, adztail, v);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ if (bdztail != 0.0) {
+ vlength = scale_expansion_zeroelim(4, ca, bdztail, v);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ if (cdztail != 0.0) {
+ vlength = scale_expansion_zeroelim(4, ab, cdztail, v);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+
+ if (adxtail != 0.0) {
+ if (bdytail != 0.0) {
+ Two_Product(adxtail, bdytail, adxt_bdyt1, adxt_bdyt0);
+ Two_One_Product(adxt_bdyt1, adxt_bdyt0, cdz, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ if (cdztail != 0.0) {
+ Two_One_Product(adxt_bdyt1, adxt_bdyt0, cdztail, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ }
+ if (cdytail != 0.0) {
+ negate = -adxtail;
+ Two_Product(negate, cdytail, adxt_cdyt1, adxt_cdyt0);
+ Two_One_Product(adxt_cdyt1, adxt_cdyt0, bdz, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ if (bdztail != 0.0) {
+ Two_One_Product(adxt_cdyt1, adxt_cdyt0, bdztail, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ }
+ }
+ if (bdxtail != 0.0) {
+ if (cdytail != 0.0) {
+ Two_Product(bdxtail, cdytail, bdxt_cdyt1, bdxt_cdyt0);
+ Two_One_Product(bdxt_cdyt1, bdxt_cdyt0, adz, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ if (adztail != 0.0) {
+ Two_One_Product(bdxt_cdyt1, bdxt_cdyt0, adztail, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ }
+ if (adytail != 0.0) {
+ negate = -bdxtail;
+ Two_Product(negate, adytail, bdxt_adyt1, bdxt_adyt0);
+ Two_One_Product(bdxt_adyt1, bdxt_adyt0, cdz, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ if (cdztail != 0.0) {
+ Two_One_Product(bdxt_adyt1, bdxt_adyt0, cdztail, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ }
+ }
+ if (cdxtail != 0.0) {
+ if (adytail != 0.0) {
+ Two_Product(cdxtail, adytail, cdxt_adyt1, cdxt_adyt0);
+ Two_One_Product(cdxt_adyt1, cdxt_adyt0, bdz, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ if (bdztail != 0.0) {
+ Two_One_Product(cdxt_adyt1, cdxt_adyt0, bdztail, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ }
+ if (bdytail != 0.0) {
+ negate = -cdxtail;
+ Two_Product(negate, bdytail, cdxt_bdyt1, cdxt_bdyt0);
+ Two_One_Product(cdxt_bdyt1, cdxt_bdyt0, adz, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ if (adztail != 0.0) {
+ Two_One_Product(cdxt_bdyt1, cdxt_bdyt0, adztail, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ }
+ }
+
+ if (adztail != 0.0) {
+ wlength = scale_expansion_zeroelim(bctlen, bct, adztail, w);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ if (bdztail != 0.0) {
+ wlength = scale_expansion_zeroelim(catlen, cat, bdztail, w);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ if (cdztail != 0.0) {
+ wlength = scale_expansion_zeroelim(abtlen, abt, cdztail, w);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+
+ return finnow[finlength - 1];
+}
+
+double orient3d(const double *pa, const double *pb, const double *pc, const double *pd)
+{
+ double adx, bdx, cdx, ady, bdy, cdy, adz, bdz, cdz;
+ double bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady;
+ double det;
+ double permanent, errbound;
+
+ adx = pa[0] - pd[0];
+ bdx = pb[0] - pd[0];
+ cdx = pc[0] - pd[0];
+ ady = pa[1] - pd[1];
+ bdy = pb[1] - pd[1];
+ cdy = pc[1] - pd[1];
+ adz = pa[2] - pd[2];
+ bdz = pb[2] - pd[2];
+ cdz = pc[2] - pd[2];
+
+ bdxcdy = bdx * cdy;
+ cdxbdy = cdx * bdy;
+
+ cdxady = cdx * ady;
+ adxcdy = adx * cdy;
+
+ adxbdy = adx * bdy;
+ bdxady = bdx * ady;
+
+ det = adz * (bdxcdy - cdxbdy) + bdz * (cdxady - adxcdy) + cdz * (adxbdy - bdxady);
+
+ permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * Absolute(adz) +
+ (Absolute(cdxady) + Absolute(adxcdy)) * Absolute(bdz) +
+ (Absolute(adxbdy) + Absolute(bdxady)) * Absolute(cdz);
+ errbound = o3derrboundA * permanent;
+ if ((det > errbound) || (-det > errbound)) {
+ return det;
+ }
+
+ return orient3dadapt(pa, pb, pc, pd, permanent);
+}
+
+/**
+ * incirclefast() Approximate 2D incircle test. Non-robust.
+ * incircle()
+ *
+ * Return a positive value if the point pd lies inside the
+ * circle passing through pa, pb, and pc; a negative value if
+ * it lies outside; and zero if the four points are co-circular.
+ * The points pa, pb, and pc must be in counterclockwise
+ * order, or the sign of the result will be reversed.
+ *
+ * The second uses exact arithmetic to ensure a correct answer. The
+ * result returned is the determinant of a matrix. In incircle() only,
+ * this determinant is computed adaptively, in the sense that exact
+ * arithmetic is used only to the degree it is needed to ensure that the
+ * returned value has the correct sign. Hence, incircle() is usually quite
+ * fast, but will run more slowly when the input points are co-circular or
+ * nearly so.
+ */
+
+double incirclefast(const double *pa, const double *pb, const double *pc, const double *pd)
+{
+ double adx, ady, bdx, bdy, cdx, cdy;
+ double abdet, bcdet, cadet;
+ double alift, blift, clift;
+
+ adx = pa[0] - pd[0];
+ ady = pa[1] - pd[1];
+ bdx = pb[0] - pd[0];
+ bdy = pb[1] - pd[1];
+ cdx = pc[0] - pd[0];
+ cdy = pc[1] - pd[1];
+
+ abdet = adx * bdy - bdx * ady;
+ bcdet = bdx * cdy - cdx * bdy;
+ cadet = cdx * ady - adx * cdy;
+ alift = adx * adx + ady * ady;
+ blift = bdx * bdx + bdy * bdy;
+ clift = cdx * cdx + cdy * cdy;
+
+ return alift * bcdet + blift * cadet + clift * abdet;
+}
+
+/**
+ * \note since this code comes from an external source, prefer not to break it
+ * up to fix this clang-tidy warning.
+ * NOLINTNEXTLINE: readability-function-size
+ */
+static double incircleadapt(
+ const double *pa, const double *pb, const double *pc, const double *pd, double permanent)
+{
+ INEXACT double adx, bdx, cdx, ady, bdy, cdy;
+ double det, errbound;
+
+ INEXACT double bdxcdy1, cdxbdy1, cdxady1, adxcdy1, adxbdy1, bdxady1;
+ double bdxcdy0, cdxbdy0, cdxady0, adxcdy0, adxbdy0, bdxady0;
+ double bc[4], ca[4], ab[4];
+ INEXACT double bc3, ca3, ab3;
+ double axbc[8], axxbc[16], aybc[8], ayybc[16], adet[32];
+ int axbclen, axxbclen, aybclen, ayybclen, alen;
+ double bxca[8], bxxca[16], byca[8], byyca[16], bdet[32];
+ int bxcalen, bxxcalen, bycalen, byycalen, blen;
+ double cxab[8], cxxab[16], cyab[8], cyyab[16], cdet[32];
+ int cxablen, cxxablen, cyablen, cyyablen, clen;
+ double abdet[64];
+ int ablen;
+ double fin1[1152], fin2[1152];
+ double *finnow, *finother, *finswap;
+ int finlength;
+
+ double adxtail, bdxtail, cdxtail, adytail, bdytail, cdytail;
+ INEXACT double adxadx1, adyady1, bdxbdx1, bdybdy1, cdxcdx1, cdycdy1;
+ double adxadx0, adyady0, bdxbdx0, bdybdy0, cdxcdx0, cdycdy0;
+ double aa[4], bb[4], cc[4];
+ INEXACT double aa3, bb3, cc3;
+ INEXACT double ti1, tj1;
+ double ti0, tj0;
+ double u[4], v[4];
+ INEXACT double u3, v3;
+ double temp8[8], temp16a[16], temp16b[16], temp16c[16];
+ double temp32a[32], temp32b[32], temp48[48], temp64[64];
+ int temp8len, temp16alen, temp16blen, temp16clen;
+ int temp32alen, temp32blen, temp48len, temp64len;
+ double axtbb[8], axtcc[8], aytbb[8], aytcc[8];
+ int axtbblen, axtcclen, aytbblen, aytcclen;
+ double bxtaa[8], bxtcc[8], bytaa[8], bytcc[8];
+ int bxtaalen, bxtcclen, bytaalen, bytcclen;
+ double cxtaa[8], cxtbb[8], cytaa[8], cytbb[8];
+ int cxtaalen, cxtbblen, cytaalen, cytbblen;
+ double axtbc[8], aytbc[8], bxtca[8], bytca[8], cxtab[8], cytab[8];
+ int axtbclen, aytbclen, bxtcalen, bytcalen, cxtablen, cytablen;
+ double axtbct[16], aytbct[16], bxtcat[16], bytcat[16], cxtabt[16], cytabt[16];
+ int axtbctlen, aytbctlen, bxtcatlen, bytcatlen, cxtabtlen, cytabtlen;
+ double axtbctt[8], aytbctt[8], bxtcatt[8];
+ double bytcatt[8], cxtabtt[8], cytabtt[8];
+ int axtbcttlen, aytbcttlen, bxtcattlen, bytcattlen, cxtabttlen, cytabttlen;
+ double abt[8], bct[8], cat[8];
+ int abtlen, bctlen, catlen;
+ double abtt[4], bctt[4], catt[4];
+ int abttlen, bcttlen, cattlen;
+ INEXACT double abtt3, bctt3, catt3;
+ double negate;
+
+ INEXACT double bvirt;
+ double avirt, bround, around;
+ INEXACT double c;
+ INEXACT double abig;
+ double ahi, alo, bhi, blo;
+ double err1, err2, err3;
+ INEXACT double _i, _j;
+ double _0;
+
+ adx = (double)(pa[0] - pd[0]);
+ bdx = (double)(pb[0] - pd[0]);
+ cdx = (double)(pc[0] - pd[0]);
+ ady = (double)(pa[1] - pd[1]);
+ bdy = (double)(pb[1] - pd[1]);
+ cdy = (double)(pc[1] - pd[1]);
+
+ Two_Product(bdx, cdy, bdxcdy1, bdxcdy0);
+ Two_Product(cdx, bdy, cdxbdy1, cdxbdy0);
+ Two_Two_Diff(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0, bc3, bc[2], bc[1], bc[0]);
+ bc[3] = bc3;
+ axbclen = scale_expansion_zeroelim(4, bc, adx, axbc);
+ axxbclen = scale_expansion_zeroelim(axbclen, axbc, adx, axxbc);
+ aybclen = scale_expansion_zeroelim(4, bc, ady, aybc);
+ ayybclen = scale_expansion_zeroelim(aybclen, aybc, ady, ayybc);
+ alen = fast_expansion_sum_zeroelim(axxbclen, axxbc, ayybclen, ayybc, adet);
+
+ Two_Product(cdx, ady, cdxady1, cdxady0);
+ Two_Product(adx, cdy, adxcdy1, adxcdy0);
+ Two_Two_Diff(cdxady1, cdxady0, adxcdy1, adxcdy0, ca3, ca[2], ca[1], ca[0]);
+ ca[3] = ca3;
+ bxcalen = scale_expansion_zeroelim(4, ca, bdx, bxca);
+ bxxcalen = scale_expansion_zeroelim(bxcalen, bxca, bdx, bxxca);
+ bycalen = scale_expansion_zeroelim(4, ca, bdy, byca);
+ byycalen = scale_expansion_zeroelim(bycalen, byca, bdy, byyca);
+ blen = fast_expansion_sum_zeroelim(bxxcalen, bxxca, byycalen, byyca, bdet);
+
+ Two_Product(adx, bdy, adxbdy1, adxbdy0);
+ Two_Product(bdx, ady, bdxady1, bdxady0);
+ Two_Two_Diff(adxbdy1, adxbdy0, bdxady1, bdxady0, ab3, ab[2], ab[1], ab[0]);
+ ab[3] = ab3;
+ cxablen = scale_expansion_zeroelim(4, ab, cdx, cxab);
+ cxxablen = scale_expansion_zeroelim(cxablen, cxab, cdx, cxxab);
+ cyablen = scale_expansion_zeroelim(4, ab, cdy, cyab);
+ cyyablen = scale_expansion_zeroelim(cyablen, cyab, cdy, cyyab);
+ clen = fast_expansion_sum_zeroelim(cxxablen, cxxab, cyyablen, cyyab, cdet);
+
+ ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
+ finlength = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, fin1);
+
+ det = estimate(finlength, fin1);
+ errbound = iccerrboundB * permanent;
+ if ((det >= errbound) || (-det >= errbound)) {
+ return det;
+ }
+
+ Two_Diff_Tail(pa[0], pd[0], adx, adxtail);
+ Two_Diff_Tail(pa[1], pd[1], ady, adytail);
+ Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail);
+ Two_Diff_Tail(pb[1], pd[1], bdy, bdytail);
+ Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail);
+ Two_Diff_Tail(pc[1], pd[1], cdy, cdytail);
+ if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0) && (adytail == 0.0) &&
+ (bdytail == 0.0) && (cdytail == 0.0)) {
+ return det;
+ }
+
+ errbound = iccerrboundC * permanent + resulterrbound * Absolute(det);
+ det += ((adx * adx + ady * ady) *
+ ((bdx * cdytail + cdy * bdxtail) - (bdy * cdxtail + cdx * bdytail)) +
+ 2.0 * (adx * adxtail + ady * adytail) * (bdx * cdy - bdy * cdx)) +
+ ((bdx * bdx + bdy * bdy) *
+ ((cdx * adytail + ady * cdxtail) - (cdy * adxtail + adx * cdytail)) +
+ 2.0 * (bdx * bdxtail + bdy * bdytail) * (cdx * ady - cdy * adx)) +
+ ((cdx * cdx + cdy * cdy) *
+ ((adx * bdytail + bdy * adxtail) - (ady * bdxtail + bdx * adytail)) +
+ 2.0 * (cdx * cdxtail + cdy * cdytail) * (adx * bdy - ady * bdx));
+ if ((det >= errbound) || (-det >= errbound)) {
+ return det;
+ }
+
+ finnow = fin1;
+ finother = fin2;
+
+ if ((bdxtail != 0.0) || (bdytail != 0.0) || (cdxtail != 0.0) || (cdytail != 0.0)) {
+ Square(adx, adxadx1, adxadx0);
+ Square(ady, adyady1, adyady0);
+ Two_Two_Sum(adxadx1, adxadx0, adyady1, adyady0, aa3, aa[2], aa[1], aa[0]);
+ aa[3] = aa3;
+ }
+ if ((cdxtail != 0.0) || (cdytail != 0.0) || (adxtail != 0.0) || (adytail != 0.0)) {
+ Square(bdx, bdxbdx1, bdxbdx0);
+ Square(bdy, bdybdy1, bdybdy0);
+ Two_Two_Sum(bdxbdx1, bdxbdx0, bdybdy1, bdybdy0, bb3, bb[2], bb[1], bb[0]);
+ bb[3] = bb3;
+ }
+ if ((adxtail != 0.0) || (adytail != 0.0) || (bdxtail != 0.0) || (bdytail != 0.0)) {
+ Square(cdx, cdxcdx1, cdxcdx0);
+ Square(cdy, cdycdy1, cdycdy0);
+ Two_Two_Sum(cdxcdx1, cdxcdx0, cdycdy1, cdycdy0, cc3, cc[2], cc[1], cc[0]);
+ cc[3] = cc3;
+ }
+
+ if (adxtail != 0.0) {
+ axtbclen = scale_expansion_zeroelim(4, bc, adxtail, axtbc);
+ temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, 2.0 * adx, temp16a);
+
+ axtcclen = scale_expansion_zeroelim(4, cc, adxtail, axtcc);
+ temp16blen = scale_expansion_zeroelim(axtcclen, axtcc, bdy, temp16b);
+
+ axtbblen = scale_expansion_zeroelim(4, bb, adxtail, axtbb);
+ temp16clen = scale_expansion_zeroelim(axtbblen, axtbb, -cdy, temp16c);
+
+ temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a);
+ temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ if (adytail != 0.0) {
+ aytbclen = scale_expansion_zeroelim(4, bc, adytail, aytbc);
+ temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, 2.0 * ady, temp16a);
+
+ aytbblen = scale_expansion_zeroelim(4, bb, adytail, aytbb);
+ temp16blen = scale_expansion_zeroelim(aytbblen, aytbb, cdx, temp16b);
+
+ aytcclen = scale_expansion_zeroelim(4, cc, adytail, aytcc);
+ temp16clen = scale_expansion_zeroelim(aytcclen, aytcc, -bdx, temp16c);
+
+ temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a);
+ temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ if (bdxtail != 0.0) {
+ bxtcalen = scale_expansion_zeroelim(4, ca, bdxtail, bxtca);
+ temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, 2.0 * bdx, temp16a);
+
+ bxtaalen = scale_expansion_zeroelim(4, aa, bdxtail, bxtaa);
+ temp16blen = scale_expansion_zeroelim(bxtaalen, bxtaa, cdy, temp16b);
+
+ bxtcclen = scale_expansion_zeroelim(4, cc, bdxtail, bxtcc);
+ temp16clen = scale_expansion_zeroelim(bxtcclen, bxtcc, -ady, temp16c);
+
+ temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a);
+ temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ if (bdytail != 0.0) {
+ bytcalen = scale_expansion_zeroelim(4, ca, bdytail, bytca);
+ temp16alen = scale_expansion_zeroelim(bytcalen, bytca, 2.0 * bdy, temp16a);
+
+ bytcclen = scale_expansion_zeroelim(4, cc, bdytail, bytcc);
+ temp16blen = scale_expansion_zeroelim(bytcclen, bytcc, adx, temp16b);
+
+ bytaalen = scale_expansion_zeroelim(4, aa, bdytail, bytaa);
+ temp16clen = scale_expansion_zeroelim(bytaalen, bytaa, -cdx, temp16c);
+
+ temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a);
+ temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ if (cdxtail != 0.0) {
+ cxtablen = scale_expansion_zeroelim(4, ab, cdxtail, cxtab);
+ temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, 2.0 * cdx, temp16a);
+
+ cxtbblen = scale_expansion_zeroelim(4, bb, cdxtail, cxtbb);
+ temp16blen = scale_expansion_zeroelim(cxtbblen, cxtbb, ady, temp16b);
+
+ cxtaalen = scale_expansion_zeroelim(4, aa, cdxtail, cxtaa);
+ temp16clen = scale_expansion_zeroelim(cxtaalen, cxtaa, -bdy, temp16c);
+
+ temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a);
+ temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ if (cdytail != 0.0) {
+ cytablen = scale_expansion_zeroelim(4, ab, cdytail, cytab);
+ temp16alen = scale_expansion_zeroelim(cytablen, cytab, 2.0 * cdy, temp16a);
+
+ cytaalen = scale_expansion_zeroelim(4, aa, cdytail, cytaa);
+ temp16blen = scale_expansion_zeroelim(cytaalen, cytaa, bdx, temp16b);
+
+ cytbblen = scale_expansion_zeroelim(4, bb, cdytail, cytbb);
+ temp16clen = scale_expansion_zeroelim(cytbblen, cytbb, -adx, temp16c);
+
+ temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a);
+ temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+
+ if ((adxtail != 0.0) || (adytail != 0.0)) {
+ if ((bdxtail != 0.0) || (bdytail != 0.0) || (cdxtail != 0.0) || (cdytail != 0.0)) {
+ Two_Product(bdxtail, cdy, ti1, ti0);
+ Two_Product(bdx, cdytail, tj1, tj0);
+ Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ negate = -bdy;
+ Two_Product(cdxtail, negate, ti1, ti0);
+ negate = -bdytail;
+ Two_Product(cdx, negate, tj1, tj0);
+ Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]);
+ v[3] = v3;
+ bctlen = fast_expansion_sum_zeroelim(4, u, 4, v, bct);
+
+ Two_Product(bdxtail, cdytail, ti1, ti0);
+ Two_Product(cdxtail, bdytail, tj1, tj0);
+ Two_Two_Diff(ti1, ti0, tj1, tj0, bctt3, bctt[2], bctt[1], bctt[0]);
+ bctt[3] = bctt3;
+ bcttlen = 4;
+ }
+ else {
+ bct[0] = 0.0;
+ bctlen = 1;
+ bctt[0] = 0.0;
+ bcttlen = 1;
+ }
+
+ if (adxtail != 0.0) {
+ temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, adxtail, temp16a);
+ axtbctlen = scale_expansion_zeroelim(bctlen, bct, adxtail, axtbct);
+ temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, 2.0 * adx, temp32a);
+ temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ if (bdytail != 0.0) {
+ temp8len = scale_expansion_zeroelim(4, cc, adxtail, temp8);
+ temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail, temp16a);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ if (cdytail != 0.0) {
+ temp8len = scale_expansion_zeroelim(4, bb, -adxtail, temp8);
+ temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail, temp16a);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+
+ temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, adxtail, temp32a);
+ axtbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adxtail, axtbctt);
+ temp16alen = scale_expansion_zeroelim(axtbcttlen, axtbctt, 2.0 * adx, temp16a);
+ temp16blen = scale_expansion_zeroelim(axtbcttlen, axtbctt, adxtail, temp16b);
+ temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b);
+ temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ if (adytail != 0.0) {
+ temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, adytail, temp16a);
+ aytbctlen = scale_expansion_zeroelim(bctlen, bct, adytail, aytbct);
+ temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, 2.0 * ady, temp32a);
+ temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+
+ temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, adytail, temp32a);
+ aytbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adytail, aytbctt);
+ temp16alen = scale_expansion_zeroelim(aytbcttlen, aytbctt, 2.0 * ady, temp16a);
+ temp16blen = scale_expansion_zeroelim(aytbcttlen, aytbctt, adytail, temp16b);
+ temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b);
+ temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ }
+ if ((bdxtail != 0.0) || (bdytail != 0.0)) {
+ if ((cdxtail != 0.0) || (cdytail != 0.0) || (adxtail != 0.0) || (adytail != 0.0)) {
+ Two_Product(cdxtail, ady, ti1, ti0);
+ Two_Product(cdx, adytail, tj1, tj0);
+ Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ negate = -cdy;
+ Two_Product(adxtail, negate, ti1, ti0);
+ negate = -cdytail;
+ Two_Product(adx, negate, tj1, tj0);
+ Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]);
+ v[3] = v3;
+ catlen = fast_expansion_sum_zeroelim(4, u, 4, v, cat);
+
+ Two_Product(cdxtail, adytail, ti1, ti0);
+ Two_Product(adxtail, cdytail, tj1, tj0);
+ Two_Two_Diff(ti1, ti0, tj1, tj0, catt3, catt[2], catt[1], catt[0]);
+ catt[3] = catt3;
+ cattlen = 4;
+ }
+ else {
+ cat[0] = 0.0;
+ catlen = 1;
+ catt[0] = 0.0;
+ cattlen = 1;
+ }
+
+ if (bdxtail != 0.0) {
+ temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, bdxtail, temp16a);
+ bxtcatlen = scale_expansion_zeroelim(catlen, cat, bdxtail, bxtcat);
+ temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, 2.0 * bdx, temp32a);
+ temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ if (cdytail != 0.0) {
+ temp8len = scale_expansion_zeroelim(4, aa, bdxtail, temp8);
+ temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail, temp16a);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ if (adytail != 0.0) {
+ temp8len = scale_expansion_zeroelim(4, cc, -bdxtail, temp8);
+ temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail, temp16a);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+
+ temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, bdxtail, temp32a);
+ bxtcattlen = scale_expansion_zeroelim(cattlen, catt, bdxtail, bxtcatt);
+ temp16alen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, 2.0 * bdx, temp16a);
+ temp16blen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, bdxtail, temp16b);
+ temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b);
+ temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ if (bdytail != 0.0) {
+ temp16alen = scale_expansion_zeroelim(bytcalen, bytca, bdytail, temp16a);
+ bytcatlen = scale_expansion_zeroelim(catlen, cat, bdytail, bytcat);
+ temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, 2.0 * bdy, temp32a);
+ temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+
+ temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, bdytail, temp32a);
+ bytcattlen = scale_expansion_zeroelim(cattlen, catt, bdytail, bytcatt);
+ temp16alen = scale_expansion_zeroelim(bytcattlen, bytcatt, 2.0 * bdy, temp16a);
+ temp16blen = scale_expansion_zeroelim(bytcattlen, bytcatt, bdytail, temp16b);
+ temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b);
+ temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ }
+ if ((cdxtail != 0.0) || (cdytail != 0.0)) {
+ if ((adxtail != 0.0) || (adytail != 0.0) || (bdxtail != 0.0) || (bdytail != 0.0)) {
+ Two_Product(adxtail, bdy, ti1, ti0);
+ Two_Product(adx, bdytail, tj1, tj0);
+ Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ negate = -ady;
+ Two_Product(bdxtail, negate, ti1, ti0);
+ negate = -adytail;
+ Two_Product(bdx, negate, tj1, tj0);
+ Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]);
+ v[3] = v3;
+ abtlen = fast_expansion_sum_zeroelim(4, u, 4, v, abt);
+
+ Two_Product(adxtail, bdytail, ti1, ti0);
+ Two_Product(bdxtail, adytail, tj1, tj0);
+ Two_Two_Diff(ti1, ti0, tj1, tj0, abtt3, abtt[2], abtt[1], abtt[0]);
+ abtt[3] = abtt3;
+ abttlen = 4;
+ }
+ else {
+ abt[0] = 0.0;
+ abtlen = 1;
+ abtt[0] = 0.0;
+ abttlen = 1;
+ }
+
+ if (cdxtail != 0.0) {
+ temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, cdxtail, temp16a);
+ cxtabtlen = scale_expansion_zeroelim(abtlen, abt, cdxtail, cxtabt);
+ temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, 2.0 * cdx, temp32a);
+ temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ if (adytail != 0.0) {
+ temp8len = scale_expansion_zeroelim(4, bb, cdxtail, temp8);
+ temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail, temp16a);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ if (bdytail != 0.0) {
+ temp8len = scale_expansion_zeroelim(4, aa, -cdxtail, temp8);
+ temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail, temp16a);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+
+ temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, cdxtail, temp32a);
+ cxtabttlen = scale_expansion_zeroelim(abttlen, abtt, cdxtail, cxtabtt);
+ temp16alen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, 2.0 * cdx, temp16a);
+ temp16blen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, cdxtail, temp16b);
+ temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b);
+ temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ if (cdytail != 0.0) {
+ temp16alen = scale_expansion_zeroelim(cytablen, cytab, cdytail, temp16a);
+ cytabtlen = scale_expansion_zeroelim(abtlen, abt, cdytail, cytabt);
+ temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, 2.0 * cdy, temp32a);
+ temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+
+ temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, cdytail, temp32a);
+ cytabttlen = scale_expansion_zeroelim(abttlen, abtt, cdytail, cytabtt);
+ temp16alen = scale_expansion_zeroelim(cytabttlen, cytabtt, 2.0 * cdy, temp16a);
+ temp16blen = scale_expansion_zeroelim(cytabttlen, cytabtt, cdytail, temp16b);
+ temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b);
+ temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ }
+
+ return finnow[finlength - 1];
+}
+
+double incircle(const double *pa, const double *pb, const double *pc, const double *pd)
+{
+ double adx, bdx, cdx, ady, bdy, cdy;
+ double bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady;
+ double alift, blift, clift;
+ double det;
+ double permanent, errbound;
+
+ adx = pa[0] - pd[0];
+ bdx = pb[0] - pd[0];
+ cdx = pc[0] - pd[0];
+ ady = pa[1] - pd[1];
+ bdy = pb[1] - pd[1];
+ cdy = pc[1] - pd[1];
+
+ bdxcdy = bdx * cdy;
+ cdxbdy = cdx * bdy;
+ alift = adx * adx + ady * ady;
+
+ cdxady = cdx * ady;
+ adxcdy = adx * cdy;
+ blift = bdx * bdx + bdy * bdy;
+
+ adxbdy = adx * bdy;
+ bdxady = bdx * ady;
+ clift = cdx * cdx + cdy * cdy;
+
+ det = alift * (bdxcdy - cdxbdy) + blift * (cdxady - adxcdy) + clift * (adxbdy - bdxady);
+
+ permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * alift +
+ (Absolute(cdxady) + Absolute(adxcdy)) * blift +
+ (Absolute(adxbdy) + Absolute(bdxady)) * clift;
+ errbound = iccerrboundA * permanent;
+ if ((det > errbound) || (-det > errbound)) {
+ return det;
+ }
+
+ return incircleadapt(pa, pb, pc, pd, permanent);
+}
+
+/**
+ * inspherefast() Approximate 3D insphere test. Non-robust.
+ * insphere() Adaptive exact 3D insphere test. Robust.
+ *
+ * Return a positive value if the point pe lies inside the
+ * sphere passing through pa, pb, pc, and pd; a negative value
+ * if it lies outside; and zero if the five points are
+ * co-spherical. The points pa, pb, pc, and pd must be ordered
+ * so that they have a positive orientation (as defined by
+ * orient3d()), or the sign of the result will be reversed.
+ *
+ * The second uses exact arithmetic to ensure a correct answer. The
+ * result returned is the determinant of a matrix. In insphere() only,
+ * this determinant is computed adaptively, in the sense that exact
+ * arithmetic is used only to the degree it is needed to ensure that the
+ * returned value has the correct sign. Hence, insphere() is usually quite
+ * fast, but will run more slowly when the input points are co-spherical or
+ * nearly so.
+ */
+
+double inspherefast(
+ const double *pa, const double *pb, const double *pc, const double *pd, const double *pe)
+{
+ double aex, bex, cex, dex;
+ double aey, bey, cey, dey;
+ double aez, bez, cez, dez;
+ double alift, blift, clift, dlift;
+ double ab, bc, cd, da, ac, bd;
+ double abc, bcd, cda, dab;
+
+ aex = pa[0] - pe[0];
+ bex = pb[0] - pe[0];
+ cex = pc[0] - pe[0];
+ dex = pd[0] - pe[0];
+ aey = pa[1] - pe[1];
+ bey = pb[1] - pe[1];
+ cey = pc[1] - pe[1];
+ dey = pd[1] - pe[1];
+ aez = pa[2] - pe[2];
+ bez = pb[2] - pe[2];
+ cez = pc[2] - pe[2];
+ dez = pd[2] - pe[2];
+
+ ab = aex * bey - bex * aey;
+ bc = bex * cey - cex * bey;
+ cd = cex * dey - dex * cey;
+ da = dex * aey - aex * dey;
+
+ ac = aex * cey - cex * aey;
+ bd = bex * dey - dex * bey;
+
+ abc = aez * bc - bez * ac + cez * ab;
+ bcd = bez * cd - cez * bd + dez * bc;
+ cda = cez * da + dez * ac + aez * cd;
+ dab = dez * ab + aez * bd + bez * da;
+
+ alift = aex * aex + aey * aey + aez * aez;
+ blift = bex * bex + bey * bey + bez * bez;
+ clift = cex * cex + cey * cey + cez * cez;
+ dlift = dex * dex + dey * dey + dez * dez;
+
+ return (dlift * abc - clift * dab) + (blift * cda - alift * bcd);
+}
+
+static double insphereexact(
+ const double *pa, const double *pb, const double *pc, const double *pd, const double *pe)
+{
+ INEXACT double axby1, bxcy1, cxdy1, dxey1, exay1;
+ INEXACT double bxay1, cxby1, dxcy1, exdy1, axey1;
+ INEXACT double axcy1, bxdy1, cxey1, dxay1, exby1;
+ INEXACT double cxay1, dxby1, excy1, axdy1, bxey1;
+ double axby0, bxcy0, cxdy0, dxey0, exay0;
+ double bxay0, cxby0, dxcy0, exdy0, axey0;
+ double axcy0, bxdy0, cxey0, dxay0, exby0;
+ double cxay0, dxby0, excy0, axdy0, bxey0;
+ double ab[4], bc[4], cd[4], de[4], ea[4];
+ double ac[4], bd[4], ce[4], da[4], eb[4];
+ double temp8a[8], temp8b[8], temp16[16];
+ int temp8alen, temp8blen, temp16len;
+ double abc[24], bcd[24], cde[24], dea[24], eab[24];
+ double abd[24], bce[24], cda[24], deb[24], eac[24];
+ int abclen, bcdlen, cdelen, dealen, eablen;
+ int abdlen, bcelen, cdalen, deblen, eaclen;
+ double temp48a[48], temp48b[48];
+ int temp48alen, temp48blen;
+ double abcd[96], bcde[96], cdea[96], deab[96], eabc[96];
+ int abcdlen, bcdelen, cdealen, deablen, eabclen;
+ double temp192[192];
+ double det384x[384], det384y[384], det384z[384];
+ int xlen, ylen, zlen;
+ double detxy[768];
+ int xylen;
+ double adet[1152], bdet[1152], cdet[1152], ddet[1152], edet[1152];
+ int alen, blen, clen, dlen, elen;
+ double abdet[2304], cddet[2304], cdedet[3456];
+ int ablen, cdlen;
+ double deter[5760];
+ int deterlen;
+ int i;
+
+ INEXACT double bvirt;
+ double avirt, bround, around;
+ INEXACT double c;
+ INEXACT double abig;
+ double ahi, alo, bhi, blo;
+ double err1, err2, err3;
+ INEXACT double _i, _j;
+ double _0;
+
+ Two_Product(pa[0], pb[1], axby1, axby0);
+ Two_Product(pb[0], pa[1], bxay1, bxay0);
+ Two_Two_Diff(axby1, axby0, bxay1, bxay0, ab[3], ab[2], ab[1], ab[0]);
+
+ Two_Product(pb[0], pc[1], bxcy1, bxcy0);
+ Two_Product(pc[0], pb[1], cxby1, cxby0);
+ Two_Two_Diff(bxcy1, bxcy0, cxby1, cxby0, bc[3], bc[2], bc[1], bc[0]);
+
+ Two_Product(pc[0], pd[1], cxdy1, cxdy0);
+ Two_Product(pd[0], pc[1], dxcy1, dxcy0);
+ Two_Two_Diff(cxdy1, cxdy0, dxcy1, dxcy0, cd[3], cd[2], cd[1], cd[0]);
+
+ Two_Product(pd[0], pe[1], dxey1, dxey0);
+ Two_Product(pe[0], pd[1], exdy1, exdy0);
+ Two_Two_Diff(dxey1, dxey0, exdy1, exdy0, de[3], de[2], de[1], de[0]);
+
+ Two_Product(pe[0], pa[1], exay1, exay0);
+ Two_Product(pa[0], pe[1], axey1, axey0);
+ Two_Two_Diff(exay1, exay0, axey1, axey0, ea[3], ea[2], ea[1], ea[0]);
+
+ Two_Product(pa[0], pc[1], axcy1, axcy0);
+ Two_Product(pc[0], pa[1], cxay1, cxay0);
+ Two_Two_Diff(axcy1, axcy0, cxay1, cxay0, ac[3], ac[2], ac[1], ac[0]);
+
+ Two_Product(pb[0], pd[1], bxdy1, bxdy0);
+ Two_Product(pd[0], pb[1], dxby1, dxby0);
+ Two_Two_Diff(bxdy1, bxdy0, dxby1, dxby0, bd[3], bd[2], bd[1], bd[0]);
+
+ Two_Product(pc[0], pe[1], cxey1, cxey0);
+ Two_Product(pe[0], pc[1], excy1, excy0);
+ Two_Two_Diff(cxey1, cxey0, excy1, excy0, ce[3], ce[2], ce[1], ce[0]);
+
+ Two_Product(pd[0], pa[1], dxay1, dxay0);
+ Two_Product(pa[0], pd[1], axdy1, axdy0);
+ Two_Two_Diff(dxay1, dxay0, axdy1, axdy0, da[3], da[2], da[1], da[0]);
+
+ Two_Product(pe[0], pb[1], exby1, exby0);
+ Two_Product(pb[0], pe[1], bxey1, bxey0);
+ Two_Two_Diff(exby1, exby0, bxey1, bxey0, eb[3], eb[2], eb[1], eb[0]);
+
+ temp8alen = scale_expansion_zeroelim(4, bc, pa[2], temp8a);
+ temp8blen = scale_expansion_zeroelim(4, ac, -pb[2], temp8b);
+ temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16);
+ temp8alen = scale_expansion_zeroelim(4, ab, pc[2], temp8a);
+ abclen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, abc);
+
+ temp8alen = scale_expansion_zeroelim(4, cd, pb[2], temp8a);
+ temp8blen = scale_expansion_zeroelim(4, bd, -pc[2], temp8b);
+ temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16);
+ temp8alen = scale_expansion_zeroelim(4, bc, pd[2], temp8a);
+ bcdlen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, bcd);
+
+ temp8alen = scale_expansion_zeroelim(4, de, pc[2], temp8a);
+ temp8blen = scale_expansion_zeroelim(4, ce, -pd[2], temp8b);
+ temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16);
+ temp8alen = scale_expansion_zeroelim(4, cd, pe[2], temp8a);
+ cdelen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, cde);
+
+ temp8alen = scale_expansion_zeroelim(4, ea, pd[2], temp8a);
+ temp8blen = scale_expansion_zeroelim(4, da, -pe[2], temp8b);
+ temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16);
+ temp8alen = scale_expansion_zeroelim(4, de, pa[2], temp8a);
+ dealen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, dea);
+
+ temp8alen = scale_expansion_zeroelim(4, ab, pe[2], temp8a);
+ temp8blen = scale_expansion_zeroelim(4, eb, -pa[2], temp8b);
+ temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16);
+ temp8alen = scale_expansion_zeroelim(4, ea, pb[2], temp8a);
+ eablen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, eab);
+
+ temp8alen = scale_expansion_zeroelim(4, bd, pa[2], temp8a);
+ temp8blen = scale_expansion_zeroelim(4, da, pb[2], temp8b);
+ temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16);
+ temp8alen = scale_expansion_zeroelim(4, ab, pd[2], temp8a);
+ abdlen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, abd);
+
+ temp8alen = scale_expansion_zeroelim(4, ce, pb[2], temp8a);
+ temp8blen = scale_expansion_zeroelim(4, eb, pc[2], temp8b);
+ temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16);
+ temp8alen = scale_expansion_zeroelim(4, bc, pe[2], temp8a);
+ bcelen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, bce);
+
+ temp8alen = scale_expansion_zeroelim(4, da, pc[2], temp8a);
+ temp8blen = scale_expansion_zeroelim(4, ac, pd[2], temp8b);
+ temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16);
+ temp8alen = scale_expansion_zeroelim(4, cd, pa[2], temp8a);
+ cdalen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, cda);
+
+ temp8alen = scale_expansion_zeroelim(4, eb, pd[2], temp8a);
+ temp8blen = scale_expansion_zeroelim(4, bd, pe[2], temp8b);
+ temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16);
+ temp8alen = scale_expansion_zeroelim(4, de, pb[2], temp8a);
+ deblen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, deb);
+
+ temp8alen = scale_expansion_zeroelim(4, ac, pe[2], temp8a);
+ temp8blen = scale_expansion_zeroelim(4, ce, pa[2], temp8b);
+ temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16);
+ temp8alen = scale_expansion_zeroelim(4, ea, pc[2], temp8a);
+ eaclen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, eac);
+
+ temp48alen = fast_expansion_sum_zeroelim(cdelen, cde, bcelen, bce, temp48a);
+ temp48blen = fast_expansion_sum_zeroelim(deblen, deb, bcdlen, bcd, temp48b);
+ for (i = 0; i < temp48blen; i++) {
+ temp48b[i] = -temp48b[i];
+ }
+ bcdelen = fast_expansion_sum_zeroelim(temp48alen, temp48a, temp48blen, temp48b, bcde);
+ xlen = scale_expansion_zeroelim(bcdelen, bcde, pa[0], temp192);
+ xlen = scale_expansion_zeroelim(xlen, temp192, pa[0], det384x);
+ ylen = scale_expansion_zeroelim(bcdelen, bcde, pa[1], temp192);
+ ylen = scale_expansion_zeroelim(ylen, temp192, pa[1], det384y);
+ zlen = scale_expansion_zeroelim(bcdelen, bcde, pa[2], temp192);
+ zlen = scale_expansion_zeroelim(zlen, temp192, pa[2], det384z);
+ xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy);
+ alen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, adet);
+
+ temp48alen = fast_expansion_sum_zeroelim(dealen, dea, cdalen, cda, temp48a);
+ temp48blen = fast_expansion_sum_zeroelim(eaclen, eac, cdelen, cde, temp48b);
+ for (i = 0; i < temp48blen; i++) {
+ temp48b[i] = -temp48b[i];
+ }
+ cdealen = fast_expansion_sum_zeroelim(temp48alen, temp48a, temp48blen, temp48b, cdea);
+ xlen = scale_expansion_zeroelim(cdealen, cdea, pb[0], temp192);
+ xlen = scale_expansion_zeroelim(xlen, temp192, pb[0], det384x);
+ ylen = scale_expansion_zeroelim(cdealen, cdea, pb[1], temp192);
+ ylen = scale_expansion_zeroelim(ylen, temp192, pb[1], det384y);
+ zlen = scale_expansion_zeroelim(cdealen, cdea, pb[2], temp192);
+ zlen = scale_expansion_zeroelim(zlen, temp192, pb[2], det384z);
+ xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy);
+ blen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, bdet);
+
+ temp48alen = fast_expansion_sum_zeroelim(eablen, eab, deblen, deb, temp48a);
+ temp48blen = fast_expansion_sum_zeroelim(abdlen, abd, dealen, dea, temp48b);
+ for (i = 0; i < temp48blen; i++) {
+ temp48b[i] = -temp48b[i];
+ }
+ deablen = fast_expansion_sum_zeroelim(temp48alen, temp48a, temp48blen, temp48b, deab);
+ xlen = scale_expansion_zeroelim(deablen, deab, pc[0], temp192);
+ xlen = scale_expansion_zeroelim(xlen, temp192, pc[0], det384x);
+ ylen = scale_expansion_zeroelim(deablen, deab, pc[1], temp192);
+ ylen = scale_expansion_zeroelim(ylen, temp192, pc[1], det384y);
+ zlen = scale_expansion_zeroelim(deablen, deab, pc[2], temp192);
+ zlen = scale_expansion_zeroelim(zlen, temp192, pc[2], det384z);
+ xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy);
+ clen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, cdet);
+
+ temp48alen = fast_expansion_sum_zeroelim(abclen, abc, eaclen, eac, temp48a);
+ temp48blen = fast_expansion_sum_zeroelim(bcelen, bce, eablen, eab, temp48b);
+ for (i = 0; i < temp48blen; i++) {
+ temp48b[i] = -temp48b[i];
+ }
+ eabclen = fast_expansion_sum_zeroelim(temp48alen, temp48a, temp48blen, temp48b, eabc);
+ xlen = scale_expansion_zeroelim(eabclen, eabc, pd[0], temp192);
+ xlen = scale_expansion_zeroelim(xlen, temp192, pd[0], det384x);
+ ylen = scale_expansion_zeroelim(eabclen, eabc, pd[1], temp192);
+ ylen = scale_expansion_zeroelim(ylen, temp192, pd[1], det384y);
+ zlen = scale_expansion_zeroelim(eabclen, eabc, pd[2], temp192);
+ zlen = scale_expansion_zeroelim(zlen, temp192, pd[2], det384z);
+ xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy);
+ dlen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, ddet);
+
+ temp48alen = fast_expansion_sum_zeroelim(bcdlen, bcd, abdlen, abd, temp48a);
+ temp48blen = fast_expansion_sum_zeroelim(cdalen, cda, abclen, abc, temp48b);
+ for (i = 0; i < temp48blen; i++) {
+ temp48b[i] = -temp48b[i];
+ }
+ abcdlen = fast_expansion_sum_zeroelim(temp48alen, temp48a, temp48blen, temp48b, abcd);
+ xlen = scale_expansion_zeroelim(abcdlen, abcd, pe[0], temp192);
+ xlen = scale_expansion_zeroelim(xlen, temp192, pe[0], det384x);
+ ylen = scale_expansion_zeroelim(abcdlen, abcd, pe[1], temp192);
+ ylen = scale_expansion_zeroelim(ylen, temp192, pe[1], det384y);
+ zlen = scale_expansion_zeroelim(abcdlen, abcd, pe[2], temp192);
+ zlen = scale_expansion_zeroelim(zlen, temp192, pe[2], det384z);
+ xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy);
+ elen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, edet);
+
+ ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
+ cdlen = fast_expansion_sum_zeroelim(clen, cdet, dlen, ddet, cddet);
+ cdelen = fast_expansion_sum_zeroelim(cdlen, cddet, elen, edet, cdedet);
+ deterlen = fast_expansion_sum_zeroelim(ablen, abdet, cdelen, cdedet, deter);
+
+ return deter[deterlen - 1];
+}
+
+static double insphereadapt(const double *pa,
+ const double *pb,
+ const double *pc,
+ const double *pd,
+ const double *pe,
+ double permanent)
+{
+ INEXACT double aex, bex, cex, dex, aey, bey, cey, dey, aez, bez, cez, dez;
+ double det, errbound;
+
+ INEXACT double aexbey1, bexaey1, bexcey1, cexbey1;
+ INEXACT double cexdey1, dexcey1, dexaey1, aexdey1;
+ INEXACT double aexcey1, cexaey1, bexdey1, dexbey1;
+ double aexbey0, bexaey0, bexcey0, cexbey0;
+ double cexdey0, dexcey0, dexaey0, aexdey0;
+ double aexcey0, cexaey0, bexdey0, dexbey0;
+ double ab[4], bc[4], cd[4], da[4], ac[4], bd[4];
+ INEXACT double ab3, bc3, cd3, da3, ac3, bd3;
+ double abeps, bceps, cdeps, daeps, aceps, bdeps;
+ double temp8a[8], temp8b[8], temp8c[8], temp16[16], temp24[24], temp48[48];
+ int temp8alen, temp8blen, temp8clen, temp16len, temp24len, temp48len;
+ double xdet[96], ydet[96], zdet[96], xydet[192];
+ int xlen, ylen, zlen, xylen;
+ double adet[288], bdet[288], cdet[288], ddet[288];
+ int alen, blen, clen, dlen;
+ double abdet[576], cddet[576];
+ int ablen, cdlen;
+ double fin1[1152];
+ int finlength;
+
+ double aextail, bextail, cextail, dextail;
+ double aeytail, beytail, ceytail, deytail;
+ double aeztail, beztail, ceztail, deztail;
+
+ INEXACT double bvirt;
+ double avirt, bround, around;
+ INEXACT double c;
+ INEXACT double abig;
+ double ahi, alo, bhi, blo;
+ double err1, err2, err3;
+ INEXACT double _i, _j;
+ double _0;
+
+ aex = (double)(pa[0] - pe[0]);
+ bex = (double)(pb[0] - pe[0]);
+ cex = (double)(pc[0] - pe[0]);
+ dex = (double)(pd[0] - pe[0]);
+ aey = (double)(pa[1] - pe[1]);
+ bey = (double)(pb[1] - pe[1]);
+ cey = (double)(pc[1] - pe[1]);
+ dey = (double)(pd[1] - pe[1]);
+ aez = (double)(pa[2] - pe[2]);
+ bez = (double)(pb[2] - pe[2]);
+ cez = (double)(pc[2] - pe[2]);
+ dez = (double)(pd[2] - pe[2]);
+
+ Two_Product(aex, bey, aexbey1, aexbey0);
+ Two_Product(bex, aey, bexaey1, bexaey0);
+ Two_Two_Diff(aexbey1, aexbey0, bexaey1, bexaey0, ab3, ab[2], ab[1], ab[0]);
+ ab[3] = ab3;
+
+ Two_Product(bex, cey, bexcey1, bexcey0);
+ Two_Product(cex, bey, cexbey1, cexbey0);
+ Two_Two_Diff(bexcey1, bexcey0, cexbey1, cexbey0, bc3, bc[2], bc[1], bc[0]);
+ bc[3] = bc3;
+
+ Two_Product(cex, dey, cexdey1, cexdey0);
+ Two_Product(dex, cey, dexcey1, dexcey0);
+ Two_Two_Diff(cexdey1, cexdey0, dexcey1, dexcey0, cd3, cd[2], cd[1], cd[0]);
+ cd[3] = cd3;
+
+ Two_Product(dex, aey, dexaey1, dexaey0);
+ Two_Product(aex, dey, aexdey1, aexdey0);
+ Two_Two_Diff(dexaey1, dexaey0, aexdey1, aexdey0, da3, da[2], da[1], da[0]);
+ da[3] = da3;
+
+ Two_Product(aex, cey, aexcey1, aexcey0);
+ Two_Product(cex, aey, cexaey1, cexaey0);
+ Two_Two_Diff(aexcey1, aexcey0, cexaey1, cexaey0, ac3, ac[2], ac[1], ac[0]);
+ ac[3] = ac3;
+
+ Two_Product(bex, dey, bexdey1, bexdey0);
+ Two_Product(dex, bey, dexbey1, dexbey0);
+ Two_Two_Diff(bexdey1, bexdey0, dexbey1, dexbey0, bd3, bd[2], bd[1], bd[0]);
+ bd[3] = bd3;
+
+ temp8alen = scale_expansion_zeroelim(4, cd, bez, temp8a);
+ temp8blen = scale_expansion_zeroelim(4, bd, -cez, temp8b);
+ temp8clen = scale_expansion_zeroelim(4, bc, dez, temp8c);
+ temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16);
+ temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c, temp16len, temp16, temp24);
+ temp48len = scale_expansion_zeroelim(temp24len, temp24, aex, temp48);
+ xlen = scale_expansion_zeroelim(temp48len, temp48, -aex, xdet);
+ temp48len = scale_expansion_zeroelim(temp24len, temp24, aey, temp48);
+ ylen = scale_expansion_zeroelim(temp48len, temp48, -aey, ydet);
+ temp48len = scale_expansion_zeroelim(temp24len, temp24, aez, temp48);
+ zlen = scale_expansion_zeroelim(temp48len, temp48, -aez, zdet);
+ xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet);
+ alen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, adet);
+
+ temp8alen = scale_expansion_zeroelim(4, da, cez, temp8a);
+ temp8blen = scale_expansion_zeroelim(4, ac, dez, temp8b);
+ temp8clen = scale_expansion_zeroelim(4, cd, aez, temp8c);
+ temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16);
+ temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c, temp16len, temp16, temp24);
+ temp48len = scale_expansion_zeroelim(temp24len, temp24, bex, temp48);
+ xlen = scale_expansion_zeroelim(temp48len, temp48, bex, xdet);
+ temp48len = scale_expansion_zeroelim(temp24len, temp24, bey, temp48);
+ ylen = scale_expansion_zeroelim(temp48len, temp48, bey, ydet);
+ temp48len = scale_expansion_zeroelim(temp24len, temp24, bez, temp48);
+ zlen = scale_expansion_zeroelim(temp48len, temp48, bez, zdet);
+ xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet);
+ blen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, bdet);
+
+ temp8alen = scale_expansion_zeroelim(4, ab, dez, temp8a);
+ temp8blen = scale_expansion_zeroelim(4, bd, aez, temp8b);
+ temp8clen = scale_expansion_zeroelim(4, da, bez, temp8c);
+ temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16);
+ temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c, temp16len, temp16, temp24);
+ temp48len = scale_expansion_zeroelim(temp24len, temp24, cex, temp48);
+ xlen = scale_expansion_zeroelim(temp48len, temp48, -cex, xdet);
+ temp48len = scale_expansion_zeroelim(temp24len, temp24, cey, temp48);
+ ylen = scale_expansion_zeroelim(temp48len, temp48, -cey, ydet);
+ temp48len = scale_expansion_zeroelim(temp24len, temp24, cez, temp48);
+ zlen = scale_expansion_zeroelim(temp48len, temp48, -cez, zdet);
+ xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet);
+ clen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, cdet);
+
+ temp8alen = scale_expansion_zeroelim(4, bc, aez, temp8a);
+ temp8blen = scale_expansion_zeroelim(4, ac, -bez, temp8b);
+ temp8clen = scale_expansion_zeroelim(4, ab, cez, temp8c);
+ temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16);
+ temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c, temp16len, temp16, temp24);
+ temp48len = scale_expansion_zeroelim(temp24len, temp24, dex, temp48);
+ xlen = scale_expansion_zeroelim(temp48len, temp48, dex, xdet);
+ temp48len = scale_expansion_zeroelim(temp24len, temp24, dey, temp48);
+ ylen = scale_expansion_zeroelim(temp48len, temp48, dey, ydet);
+ temp48len = scale_expansion_zeroelim(temp24len, temp24, dez, temp48);
+ zlen = scale_expansion_zeroelim(temp48len, temp48, dez, zdet);
+ xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet);
+ dlen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, ddet);
+
+ ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
+ cdlen = fast_expansion_sum_zeroelim(clen, cdet, dlen, ddet, cddet);
+ finlength = fast_expansion_sum_zeroelim(ablen, abdet, cdlen, cddet, fin1);
+
+ det = estimate(finlength, fin1);
+ errbound = isperrboundB * permanent;
+ if ((det >= errbound) || (-det >= errbound)) {
+ return det;
+ }
+
+ Two_Diff_Tail(pa[0], pe[0], aex, aextail);
+ Two_Diff_Tail(pa[1], pe[1], aey, aeytail);
+ Two_Diff_Tail(pa[2], pe[2], aez, aeztail);
+ Two_Diff_Tail(pb[0], pe[0], bex, bextail);
+ Two_Diff_Tail(pb[1], pe[1], bey, beytail);
+ Two_Diff_Tail(pb[2], pe[2], bez, beztail);
+ Two_Diff_Tail(pc[0], pe[0], cex, cextail);
+ Two_Diff_Tail(pc[1], pe[1], cey, ceytail);
+ Two_Diff_Tail(pc[2], pe[2], cez, ceztail);
+ Two_Diff_Tail(pd[0], pe[0], dex, dextail);
+ Two_Diff_Tail(pd[1], pe[1], dey, deytail);
+ Two_Diff_Tail(pd[2], pe[2], dez, deztail);
+ if ((aextail == 0.0) && (aeytail == 0.0) && (aeztail == 0.0) && (bextail == 0.0) &&
+ (beytail == 0.0) && (beztail == 0.0) && (cextail == 0.0) && (ceytail == 0.0) &&
+ (ceztail == 0.0) && (dextail == 0.0) && (deytail == 0.0) && (deztail == 0.0)) {
+ return det;
+ }
+
+ errbound = isperrboundC * permanent + resulterrbound * Absolute(det);
+ abeps = (aex * beytail + bey * aextail) - (aey * bextail + bex * aeytail);
+ bceps = (bex * ceytail + cey * bextail) - (bey * cextail + cex * beytail);
+ cdeps = (cex * deytail + dey * cextail) - (cey * dextail + dex * ceytail);
+ daeps = (dex * aeytail + aey * dextail) - (dey * aextail + aex * deytail);
+ aceps = (aex * ceytail + cey * aextail) - (aey * cextail + cex * aeytail);
+ bdeps = (bex * deytail + dey * bextail) - (bey * dextail + dex * beytail);
+ det +=
+ (((bex * bex + bey * bey + bez * bez) * ((cez * daeps + dez * aceps + aez * cdeps) +
+ (ceztail * da3 + deztail * ac3 + aeztail * cd3)) +
+ (dex * dex + dey * dey + dez * dez) * ((aez * bceps - bez * aceps + cez * abeps) +
+ (aeztail * bc3 - beztail * ac3 + ceztail * ab3))) -
+ ((aex * aex + aey * aey + aez * aez) * ((bez * cdeps - cez * bdeps + dez * bceps) +
+ (beztail * cd3 - ceztail * bd3 + deztail * bc3)) +
+ (cex * cex + cey * cey + cez * cez) * ((dez * abeps + aez * bdeps + bez * daeps) +
+ (deztail * ab3 + aeztail * bd3 + beztail * da3)))) +
+ 2.0 *
+ (((bex * bextail + bey * beytail + bez * beztail) * (cez * da3 + dez * ac3 + aez * cd3) +
+ (dex * dextail + dey * deytail + dez * deztail) *
+ (aez * bc3 - bez * ac3 + cez * ab3)) -
+ ((aex * aextail + aey * aeytail + aez * aeztail) * (bez * cd3 - cez * bd3 + dez * bc3) +
+ (cex * cextail + cey * ceytail + cez * ceztail) *
+ (dez * ab3 + aez * bd3 + bez * da3)));
+ if ((det >= errbound) || (-det >= errbound)) {
+ return det;
+ }
+
+ return insphereexact(pa, pb, pc, pd, pe);
+}
+
+double insphere(
+ const double *pa, const double *pb, const double *pc, const double *pd, const double *pe)
+{
+ double aex, bex, cex, dex;
+ double aey, bey, cey, dey;
+ double aez, bez, cez, dez;
+ double aexbey, bexaey, bexcey, cexbey, cexdey, dexcey, dexaey, aexdey;
+ double aexcey, cexaey, bexdey, dexbey;
+ double alift, blift, clift, dlift;
+ double ab, bc, cd, da, ac, bd;
+ double abc, bcd, cda, dab;
+ double aezplus, bezplus, cezplus, dezplus;
+ double aexbeyplus, bexaeyplus, bexceyplus, cexbeyplus;
+ double cexdeyplus, dexceyplus, dexaeyplus, aexdeyplus;
+ double aexceyplus, cexaeyplus, bexdeyplus, dexbeyplus;
+ double det;
+ double permanent, errbound;
+
+ aex = pa[0] - pe[0];
+ bex = pb[0] - pe[0];
+ cex = pc[0] - pe[0];
+ dex = pd[0] - pe[0];
+ aey = pa[1] - pe[1];
+ bey = pb[1] - pe[1];
+ cey = pc[1] - pe[1];
+ dey = pd[1] - pe[1];
+ aez = pa[2] - pe[2];
+ bez = pb[2] - pe[2];
+ cez = pc[2] - pe[2];
+ dez = pd[2] - pe[2];
+
+ aexbey = aex * bey;
+ bexaey = bex * aey;
+ ab = aexbey - bexaey;
+ bexcey = bex * cey;
+ cexbey = cex * bey;
+ bc = bexcey - cexbey;
+ cexdey = cex * dey;
+ dexcey = dex * cey;
+ cd = cexdey - dexcey;
+ dexaey = dex * aey;
+ aexdey = aex * dey;
+ da = dexaey - aexdey;
+
+ aexcey = aex * cey;
+ cexaey = cex * aey;
+ ac = aexcey - cexaey;
+ bexdey = bex * dey;
+ dexbey = dex * bey;
+ bd = bexdey - dexbey;
+
+ abc = aez * bc - bez * ac + cez * ab;
+ bcd = bez * cd - cez * bd + dez * bc;
+ cda = cez * da + dez * ac + aez * cd;
+ dab = dez * ab + aez * bd + bez * da;
+
+ alift = aex * aex + aey * aey + aez * aez;
+ blift = bex * bex + bey * bey + bez * bez;
+ clift = cex * cex + cey * cey + cez * cez;
+ dlift = dex * dex + dey * dey + dez * dez;
+
+ det = (dlift * abc - clift * dab) + (blift * cda - alift * bcd);
+
+ aezplus = Absolute(aez);
+ bezplus = Absolute(bez);
+ cezplus = Absolute(cez);
+ dezplus = Absolute(dez);
+ aexbeyplus = Absolute(aexbey);
+ bexaeyplus = Absolute(bexaey);
+ bexceyplus = Absolute(bexcey);
+ cexbeyplus = Absolute(cexbey);
+ cexdeyplus = Absolute(cexdey);
+ dexceyplus = Absolute(dexcey);
+ dexaeyplus = Absolute(dexaey);
+ aexdeyplus = Absolute(aexdey);
+ aexceyplus = Absolute(aexcey);
+ cexaeyplus = Absolute(cexaey);
+ bexdeyplus = Absolute(bexdey);
+ dexbeyplus = Absolute(dexbey);
+ permanent = ((cexdeyplus + dexceyplus) * bezplus + (dexbeyplus + bexdeyplus) * cezplus +
+ (bexceyplus + cexbeyplus) * dezplus) *
+ alift +
+ ((dexaeyplus + aexdeyplus) * cezplus + (aexceyplus + cexaeyplus) * dezplus +
+ (cexdeyplus + dexceyplus) * aezplus) *
+ blift +
+ ((aexbeyplus + bexaeyplus) * dezplus + (bexdeyplus + dexbeyplus) * aezplus +
+ (dexaeyplus + aexdeyplus) * bezplus) *
+ clift +
+ ((bexceyplus + cexbeyplus) * aezplus + (cexaeyplus + aexceyplus) * bezplus +
+ (aexbeyplus + bexaeyplus) * cezplus) *
+ dlift;
+ errbound = isperrboundA * permanent;
+ if ((det > errbound) || (-det > errbound)) {
+ return det;
+ }
+
+ return insphereadapt(pa, pb, pc, pd, pe, permanent);
+}
+
+} /* namespace robust_pred */
+
+static int sgn(double x)
+{
+ return (x > 0) ? 1 : ((x < 0) ? -1 : 0);
+}
+
+int orient2d(const double2 &a, const double2 &b, const double2 &c)
+{
+ return sgn(blender::robust_pred::orient2d(a, b, c));
+}
+
+int orient2d_fast(const double2 &a, const double2 &b, const double2 &c)
+{
+ return sgn(blender::robust_pred::orient2dfast(a, b, c));
+}
+
+int incircle(const double2 &a, const double2 &b, const double2 &c, const double2 &d)
+{
+ return sgn(robust_pred::incircle(a, b, c, d));
+}
+
+int incircle_fast(const double2 &a, const double2 &b, const double2 &c, const double2 &d)
+{
+ return sgn(robust_pred::incirclefast(a, b, c, d));
+}
+
+int orient3d(const double3 &a, const double3 &b, const double3 &c, const double3 &d)
+{
+ return sgn(robust_pred::orient3d(a, b, c, d));
+}
+
+int orient3d_fast(const double3 &a, const double3 &b, const double3 &c, const double3 &d)
+{
+ return sgn(robust_pred::orient3dfast(a, b, c, d));
+}
+
+int insphere(
+ const double3 &a, const double3 &b, const double3 &c, const double3 &d, const double3 &e)
+{
+ return sgn(robust_pred::insphere(a, b, c, d, e));
+}
+
+int insphere_fast(
+ const double3 &a, const double3 &b, const double3 &c, const double3 &d, const double3 &e)
+{
+ return sgn(robust_pred::inspherefast(a, b, c, d, e));
+}
+
+} // namespace blender
diff --git a/source/blender/blenlib/intern/math_matrix.c b/source/blender/blenlib/intern/math_matrix.c
index fadd7d83444..f523bd07c09 100644
--- a/source/blender/blenlib/intern/math_matrix.c
+++ b/source/blender/blenlib/intern/math_matrix.c
@@ -3118,6 +3118,115 @@ void invert_m4_m4_safe(float Ainv[4][4], const float A[4][4])
}
}
+/* -------------------------------------------------------------------- */
+/** \name Invert (Safe Orthographic)
+ *
+ * Invert the matrix, filling in zeroed axes using the valid ones where possible.
+ *
+ * Unlike #invert_m4_m4_safe set degenerate axis unit length instead of adding a small value,
+ * which has the results in:
+ *
+ * - Scaling by a large value on the resulting matrix.
+ * - Changing axis which aren't degenerate.
+ *
+ * \note We could support passing in a length value if there is a good use-case
+ * where we want to specify the length of the degenerate axes.
+ * \{ */
+
+/**
+ * Return true if invert should be attempted again.
+ *
+ * \note Takes an array of points to be usable from 3x3 and 4x4 matrices.
+ */
+static bool invert_m3_m3_safe_ortho_prepare(float *mat[3])
+{
+ enum { X = 1 << 0, Y = 1 << 1, Z = 1 << 2 };
+ int flag = 0;
+ for (int i = 0; i < 3; i++) {
+ flag |= (len_squared_v3(mat[i]) == 0.0f) ? (1 << i) : 0;
+ }
+
+ /* Either all or none are zero, either way we can't properly resolve this
+ * since we need to fill invalid axes from valid ones. */
+ if (ELEM(flag, 0, X | Y | Z)) {
+ return false;
+ }
+
+ switch (flag) {
+ case X | Y: {
+ ortho_v3_v3(mat[1], mat[2]);
+ ATTR_FALLTHROUGH;
+ }
+ case X: {
+ cross_v3_v3v3(mat[0], mat[1], mat[2]);
+ break;
+ }
+
+ case Y | Z: {
+ ortho_v3_v3(mat[2], mat[0]);
+ ATTR_FALLTHROUGH;
+ }
+ case Y: {
+ cross_v3_v3v3(mat[1], mat[0], mat[2]);
+ break;
+ }
+
+ case Z | X: {
+ ortho_v3_v3(mat[0], mat[1]);
+ ATTR_FALLTHROUGH;
+ }
+ case Z: {
+ cross_v3_v3v3(mat[2], mat[0], mat[1]);
+ break;
+ }
+ default: {
+ BLI_assert(0); /* Unreachable! */
+ }
+ }
+
+ for (int i = 0; i < 3; i++) {
+ if (flag & (1 << i)) {
+ if (UNLIKELY(normalize_v3(mat[i]) == 0.0f)) {
+ mat[i][i] = 1.0f;
+ }
+ }
+ }
+
+ return true;
+}
+
+/**
+ * A safe version of invert that uses valid axes, calculating the zero'd axis
+ * based on the non-zero ones.
+ *
+ * This works well for transformation matrices, when a single axis is zerod.
+ */
+void invert_m4_m4_safe_ortho(float Ainv[4][4], const float A[4][4])
+{
+ if (UNLIKELY(!invert_m4_m4(Ainv, A))) {
+ float Atemp[4][4];
+ copy_m4_m4(Atemp, A);
+ if (UNLIKELY(!(invert_m3_m3_safe_ortho_prepare((float *[3]){UNPACK3(Atemp)}) &&
+ invert_m4_m4(Ainv, Atemp)))) {
+ unit_m4(Ainv);
+ }
+ }
+}
+
+void invert_m3_m3_safe_ortho(float Ainv[3][3], const float A[3][3])
+{
+ if (UNLIKELY(!invert_m3_m3(Ainv, A))) {
+ float Atemp[3][3];
+ copy_m3_m3(Atemp, A);
+ if (UNLIKELY(!(invert_m3_m3_safe_ortho_prepare((float *[3]){UNPACK3(Atemp)}) &&
+ invert_m3_m3(Ainv, Atemp)))) {
+ unit_m3(Ainv);
+ }
+ }
+}
+
+/** \} */
+
/**
* #SpaceTransform struct encapsulates all needed data to convert between two coordinate spaces
* (where conversion can be represented by a matrix multiplication).
diff --git a/source/blender/blenlib/intern/math_vec.cc b/source/blender/blenlib/intern/math_vec.cc
new file mode 100644
index 00000000000..54926f84eb9
--- /dev/null
+++ b/source/blender/blenlib/intern/math_vec.cc
@@ -0,0 +1,195 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup bli
+ */
+
+#include "BLI_double2.hh"
+#include "BLI_double3.hh"
+#include "BLI_float2.hh"
+#include "BLI_float3.hh"
+#include "BLI_hash.hh"
+#include "BLI_math_mpq.hh"
+#include "BLI_mpq2.hh"
+#include "BLI_mpq3.hh"
+#include "BLI_span.hh"
+#include "BLI_utildefines.h"
+
+namespace blender {
+
+float2::isect_result float2::isect_seg_seg(const float2 &v1,
+ const float2 &v2,
+ const float2 &v3,
+ const float2 &v4)
+{
+ float2::isect_result ans;
+ float div = (v2[0] - v1[0]) * (v4[1] - v3[1]) - (v2[1] - v1[1]) * (v4[0] - v3[0]);
+ if (div == 0.0f) {
+ ans.lambda = 0.0f;
+ ans.mu = 0.0f;
+ ans.kind = float2::isect_result::LINE_LINE_COLINEAR;
+ }
+ else {
+ ans.lambda = ((v1[1] - v3[1]) * (v4[0] - v3[0]) - (v1[0] - v3[0]) * (v4[1] - v3[1])) / div;
+ ans.mu = ((v1[1] - v3[1]) * (v2[0] - v1[0]) - (v1[0] - v3[0]) * (v2[1] - v1[1])) / div;
+ if (ans.lambda >= 0.0f && ans.lambda <= 1.0f && ans.mu >= 0.0f && ans.mu <= 1.0f) {
+ if (ans.lambda == 0.0f || ans.lambda == 1.0f || ans.mu == 0.0f || ans.mu == 1.0f) {
+ ans.kind = float2::isect_result::LINE_LINE_EXACT;
+ }
+ else {
+ ans.kind = float2::isect_result::LINE_LINE_CROSS;
+ }
+ }
+ else {
+ ans.kind = float2::isect_result::LINE_LINE_NONE;
+ }
+ }
+ return ans;
+}
+
+double2::isect_result double2::isect_seg_seg(const double2 &v1,
+ const double2 &v2,
+ const double2 &v3,
+ const double2 &v4)
+{
+ double2::isect_result ans;
+ double div = (v2[0] - v1[0]) * (v4[1] - v3[1]) - (v2[1] - v1[1]) * (v4[0] - v3[0]);
+ if (div == 0.0) {
+ ans.lambda = 0.0;
+ ans.mu = 0.0;
+ ans.kind = double2::isect_result::LINE_LINE_COLINEAR;
+ }
+ else {
+ ans.lambda = ((v1[1] - v3[1]) * (v4[0] - v3[0]) - (v1[0] - v3[0]) * (v4[1] - v3[1])) / div;
+ ans.mu = ((v1[1] - v3[1]) * (v2[0] - v1[0]) - (v1[0] - v3[0]) * (v2[1] - v1[1])) / div;
+ if (ans.lambda >= 0.0 && ans.lambda <= 1.0 && ans.mu >= 0.0 && ans.mu <= 1.0) {
+ if (ans.lambda == 0.0 || ans.lambda == 1.0 || ans.mu == 0.0 || ans.mu == 1.0) {
+ ans.kind = double2::isect_result::LINE_LINE_EXACT;
+ }
+ else {
+ ans.kind = double2::isect_result::LINE_LINE_CROSS;
+ }
+ }
+ else {
+ ans.kind = double2::isect_result::LINE_LINE_NONE;
+ }
+ }
+ return ans;
+}
+
+#ifdef WITH_GMP
+mpq2::isect_result mpq2::isect_seg_seg(const mpq2 &v1,
+ const mpq2 &v2,
+ const mpq2 &v3,
+ const mpq2 &v4)
+{
+ mpq2::isect_result ans;
+ mpq_class div = (v2[0] - v1[0]) * (v4[1] - v3[1]) - (v2[1] - v1[1]) * (v4[0] - v3[0]);
+ if (div == 0.0) {
+ ans.lambda = 0.0;
+ ans.mu = 0.0;
+ ans.kind = mpq2::isect_result::LINE_LINE_COLINEAR;
+ }
+ else {
+ ans.lambda = ((v1[1] - v3[1]) * (v4[0] - v3[0]) - (v1[0] - v3[0]) * (v4[1] - v3[1])) / div;
+ ans.mu = ((v1[1] - v3[1]) * (v2[0] - v1[0]) - (v1[0] - v3[0]) * (v2[1] - v1[1])) / div;
+ if (ans.lambda >= 0 && ans.lambda <= 1 && ans.mu >= 0 && ans.mu <= 1) {
+ if (ans.lambda == 0 || ans.lambda == 1 || ans.mu == 0 || ans.mu == 1) {
+ ans.kind = mpq2::isect_result::LINE_LINE_EXACT;
+ }
+ else {
+ ans.kind = mpq2::isect_result::LINE_LINE_CROSS;
+ }
+ }
+ else {
+ ans.kind = mpq2::isect_result::LINE_LINE_NONE;
+ }
+ }
+ return ans;
+}
+#endif
+
+double3 double3::cross_poly(Span<double3> poly)
+{
+ /* Newell's Method. */
+ int nv = static_cast<int>(poly.size());
+ if (nv < 3) {
+ return double3(0, 0, 0);
+ }
+ const double3 *v_prev = &poly[nv - 1];
+ const double3 *v_curr = &poly[0];
+ double3 n(0, 0, 0);
+ for (int i = 0; i < nv;) {
+ n[0] = n[0] + ((*v_prev)[1] - (*v_curr)[1]) * ((*v_prev)[2] + (*v_curr)[2]);
+ n[1] = n[1] + ((*v_prev)[2] - (*v_curr)[2]) * ((*v_prev)[0] + (*v_curr)[0]);
+ n[2] = n[2] + ((*v_prev)[0] - (*v_curr)[0]) * ((*v_prev)[1] + (*v_curr)[1]);
+ v_prev = v_curr;
+ ++i;
+ if (i < nv) {
+ v_curr = &poly[i];
+ }
+ }
+ return n;
+}
+
+#ifdef WITH_GMP
+mpq3 mpq3::cross_poly(Span<mpq3> poly)
+{
+ /* Newell's Method. */
+ int nv = static_cast<int>(poly.size());
+ if (nv < 3) {
+ return mpq3(0);
+ }
+ const mpq3 *v_prev = &poly[nv - 1];
+ const mpq3 *v_curr = &poly[0];
+ mpq3 n(0);
+ for (int i = 0; i < nv;) {
+ n[0] = n[0] + ((*v_prev)[1] - (*v_curr)[1]) * ((*v_prev)[2] + (*v_curr)[2]);
+ n[1] = n[1] + ((*v_prev)[2] - (*v_curr)[2]) * ((*v_prev)[0] + (*v_curr)[0]);
+ n[2] = n[2] + ((*v_prev)[0] - (*v_curr)[0]) * ((*v_prev)[1] + (*v_curr)[1]);
+ v_prev = v_curr;
+ ++i;
+ if (i < nv) {
+ v_curr = &poly[i];
+ }
+ }
+ return n;
+}
+
+uint64_t hash_mpq_class(const mpq_class &value)
+{
+ /* TODO: better/faster implementation of this. */
+ return DefaultHash<float>{}(static_cast<float>(value.get_d()));
+}
+
+uint64_t mpq2::hash() const
+{
+ uint64_t hashx = hash_mpq_class(this->x);
+ uint64_t hashy = hash_mpq_class(this->y);
+ return hashx ^ (hashy * 33);
+}
+
+uint64_t mpq3::hash() const
+{
+ uint64_t hashx = hash_mpq_class(this->x);
+ uint64_t hashy = hash_mpq_class(this->y);
+ uint64_t hashz = hash_mpq_class(this->z);
+ return hashx ^ (hashy * 33) ^ (hashz * 33 * 37);
+}
+#endif
+
+} // namespace blender
diff --git a/source/blender/blenlib/intern/math_vector.c b/source/blender/blenlib/intern/math_vector.c
index 909d508e262..fb3ea539df1 100644
--- a/source/blender/blenlib/intern/math_vector.c
+++ b/source/blender/blenlib/intern/math_vector.c
@@ -669,6 +669,15 @@ void project_v3_v3v3(float out[3], const float p[3], const float v_proj[3])
out[2] = mul * v_proj[2];
}
+void project_v3_v3v3_db(double out[3], const double p[3], const double v_proj[3])
+{
+ const double mul = dot_v3v3_db(p, v_proj) / dot_v3v3_db(v_proj, v_proj);
+
+ out[0] = mul * v_proj[0];
+ out[1] = mul * v_proj[1];
+ out[2] = mul * v_proj[2];
+}
+
/**
* Project \a p onto a unit length \a v_proj
*/
@@ -796,6 +805,17 @@ void reflect_v3_v3v3(float out[3], const float v[3], const float normal[3])
out[2] = v[2] - (dot2 * normal[2]);
}
+void reflect_v3_v3v3_db(double out[3], const double v[3], const double normal[3])
+{
+ const double dot2 = 2.0 * dot_v3v3_db(v, normal);
+
+ /* BLI_ASSERT_UNIT_V3_DB(normal); this assert is not known? */
+
+ out[0] = v[0] - (dot2 * normal[0]);
+ out[1] = v[1] - (dot2 * normal[1]);
+ out[2] = v[2] - (dot2 * normal[2]);
+}
+
/**
* Takes a vector and computes 2 orthogonal directions.
*
diff --git a/source/blender/blenlib/intern/math_vector_inline.c b/source/blender/blenlib/intern/math_vector_inline.c
index 1b47832589e..598a22f6aa3 100644
--- a/source/blender/blenlib/intern/math_vector_inline.c
+++ b/source/blender/blenlib/intern/math_vector_inline.c
@@ -571,6 +571,13 @@ MINLINE void mul_v3_v3fl(float r[3], const float a[3], float f)
r[2] = a[2] * f;
}
+MINLINE void mul_v3_v3db_db(double r[3], const double a[3], double f)
+{
+ r[0] = a[0] * f;
+ r[1] = a[1] * f;
+ r[2] = a[2] * f;
+}
+
MINLINE void mul_v2_v2(float r[2], const float a[2])
{
r[0] *= a[0];
@@ -988,6 +995,11 @@ MINLINE float len_squared_v3(const float v[3])
return v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
}
+MINLINE double len_squared_v3_db(const double v[3])
+{
+ return v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
+}
+
MINLINE float len_manhattan_v2(const float v[2])
{
return fabsf(v[0]) + fabsf(v[1]);
@@ -1045,6 +1057,11 @@ MINLINE float len_v3(const float a[3])
return sqrtf(dot_v3v3(a, a));
}
+MINLINE double len_v3_db(const double a[3])
+{
+ return sqrt(dot_v3v3_db(a, a));
+}
+
MINLINE float len_squared_v2v2(const float a[2], const float b[2])
{
float d[2];
@@ -1161,7 +1178,29 @@ MINLINE float normalize_v3_v3(float r[3], const float a[3])
return normalize_v3_v3_length(r, a, 1.0f);
}
-MINLINE double normalize_v3_length_d(double n[3], const double unit_length)
+MINLINE double normalize_v3_v3_length_db(double r[3], const double a[3], double unit_length)
+{
+ double d = dot_v3v3_db(a, a);
+
+ /* a larger value causes normalize errors in a
+ * scaled down models with camera extreme close */
+ if (d > 1.0e-70) {
+ d = sqrt(d);
+ mul_v3_v3db_db(r, a, unit_length / d);
+ }
+ else {
+ zero_v3_db(r);
+ d = 0.0;
+ }
+
+ return d;
+}
+MINLINE double normalize_v3_v3_db(double r[3], const double a[3])
+{
+ return normalize_v3_v3_length_db(r, a, 1.0);
+}
+
+MINLINE double normalize_v3_length_db(double n[3], const double unit_length)
{
double d = n[0] * n[0] + n[1] * n[1] + n[2] * n[2];
@@ -1184,9 +1223,9 @@ MINLINE double normalize_v3_length_d(double n[3], const double unit_length)
return d;
}
-MINLINE double normalize_v3_d(double n[3])
+MINLINE double normalize_v3_db(double n[3])
{
- return normalize_v3_length_d(n, 1.0);
+ return normalize_v3_length_db(n, 1.0);
}
MINLINE float normalize_v3_length(float n[3], const float unit_length)
diff --git a/source/blender/blenlib/intern/mesh_boolean.cc b/source/blender/blenlib/intern/mesh_boolean.cc
new file mode 100644
index 00000000000..bb8b14ebdc6
--- /dev/null
+++ b/source/blender/blenlib/intern/mesh_boolean.cc
@@ -0,0 +1,3382 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup bli
+ */
+
+#ifdef WITH_GMP
+
+# include <algorithm>
+# include <fstream>
+# include <iostream>
+
+# include "BLI_array.hh"
+# include "BLI_assert.h"
+# include "BLI_delaunay_2d.h"
+# include "BLI_hash.hh"
+# include "BLI_map.hh"
+# include "BLI_math.h"
+# include "BLI_math_boolean.hh"
+# include "BLI_math_mpq.hh"
+# include "BLI_mesh_intersect.hh"
+# include "BLI_mpq3.hh"
+# include "BLI_set.hh"
+# include "BLI_span.hh"
+# include "BLI_stack.hh"
+# include "BLI_vector.hh"
+# include "BLI_vector_set.hh"
+
+# include "BLI_mesh_boolean.hh"
+
+namespace blender::meshintersect {
+
+/**
+ * Edge as two `const` Vert *'s, in a canonical order (lower vert id first).
+ * We use the Vert id field for hashing to get algorithms
+ * that yield predictable results from run-to-run and machine-to-machine.
+ */
+class Edge {
+ const Vert *v_[2]{nullptr, nullptr};
+
+ public:
+ Edge() = default;
+ Edge(const Vert *v0, const Vert *v1)
+ {
+ if (v0->id <= v1->id) {
+ v_[0] = v0;
+ v_[1] = v1;
+ }
+ else {
+ v_[0] = v1;
+ v_[1] = v0;
+ }
+ }
+
+ const Vert *v0() const
+ {
+ return v_[0];
+ }
+
+ const Vert *v1() const
+ {
+ return v_[1];
+ }
+
+ const Vert *operator[](int i) const
+ {
+ return v_[i];
+ }
+
+ bool operator==(Edge other) const
+ {
+ return v_[0]->id == other.v_[0]->id && v_[1]->id == other.v_[1]->id;
+ }
+
+ uint64_t hash() const
+ {
+ constexpr uint64_t h1 = 33;
+ uint64_t v0hash = DefaultHash<int>{}(v_[0]->id);
+ uint64_t v1hash = DefaultHash<int>{}(v_[1]->id);
+ return v0hash ^ (v1hash * h1);
+ }
+};
+
+static std::ostream &operator<<(std::ostream &os, const Edge &e)
+{
+ if (e.v0() == nullptr) {
+ BLI_assert(e.v1() == nullptr);
+ os << "(null,null)";
+ }
+ else {
+ os << "(" << e.v0() << "," << e.v1() << ")";
+ }
+ return os;
+}
+
+static std::ostream &operator<<(std::ostream &os, const Span<int> &a)
+{
+ for (int i : a.index_range()) {
+ os << a[i];
+ if (i != a.size() - 1) {
+ os << " ";
+ }
+ }
+ return os;
+}
+
+static std::ostream &operator<<(std::ostream &os, const Array<int> &iarr)
+{
+ os << Span<int>(iarr);
+ return os;
+}
+
+/** Holds information about topology of an #IMesh that is all triangles. */
+class TriMeshTopology : NonCopyable {
+ /** Triangles that contain a given Edge (either order). */
+ Map<Edge, Vector<int> *> edge_tri_;
+ /** Edges incident on each vertex. */
+ Map<const Vert *, Vector<Edge>> vert_edges_;
+
+ public:
+ TriMeshTopology(const IMesh &tm);
+ ~TriMeshTopology();
+
+ /* If e is manifold, return index of the other triangle (not t) that has it.
+ * Else return NO_INDEX. */
+ int other_tri_if_manifold(Edge e, int t) const
+ {
+ if (edge_tri_.contains(e)) {
+ auto *p = edge_tri_.lookup(e);
+ if (p->size() == 2) {
+ return ((*p)[0] == t) ? (*p)[1] : (*p)[0];
+ }
+ }
+ return NO_INDEX;
+ }
+
+ /* Which triangles share edge e (in either orientation)? */
+ const Vector<int> *edge_tris(Edge e) const
+ {
+ return edge_tri_.lookup_default(e, nullptr);
+ }
+
+ /* Which edges are incident on the given vertex?
+ * We assume v has some incident edges. */
+ const Vector<Edge> &vert_edges(const Vert *v) const
+ {
+ return vert_edges_.lookup(v);
+ }
+
+ Map<Edge, Vector<int> *>::ItemIterator edge_tri_map_items() const
+ {
+ return edge_tri_.items();
+ }
+};
+
+TriMeshTopology::TriMeshTopology(const IMesh &tm)
+{
+ const int dbg_level = 0;
+ if (dbg_level > 0) {
+ std::cout << "TRIMESHTOPOLOGY CONSTRUCTION\n";
+ }
+ /* If everything were manifold, `F+V-E=2` and `E=3F/2`.
+ * So an likely overestimate, allowing for non-manifoldness, is `E=2F` and `V=F`. */
+ const int estimate_num_edges = 2 * tm.face_size();
+ const int estimate_num_verts = tm.face_size();
+ edge_tri_.reserve(estimate_num_edges);
+ vert_edges_.reserve(estimate_num_verts);
+ for (int t : tm.face_index_range()) {
+ const Face &tri = *tm.face(t);
+ BLI_assert(tri.is_tri());
+ for (int i = 0; i < 3; ++i) {
+ const Vert *v = tri[i];
+ const Vert *vnext = tri[(i + 1) % 3];
+ Edge e(v, vnext);
+ Vector<Edge> *edges = vert_edges_.lookup_ptr(v);
+ if (edges == nullptr) {
+ vert_edges_.add_new(v, Vector<Edge>());
+ edges = vert_edges_.lookup_ptr(v);
+ BLI_assert(edges != nullptr);
+ }
+ edges->append_non_duplicates(e);
+ auto createf = [t](Vector<int> **pvec) { *pvec = new Vector<int>{t}; };
+ auto modifyf = [t](Vector<int> **pvec) { (*pvec)->append_non_duplicates(t); };
+ this->edge_tri_.add_or_modify(Edge(v, vnext), createf, modifyf);
+ }
+ }
+ /* Debugging. */
+ if (dbg_level > 0) {
+ std::cout << "After TriMeshTopology construction\n";
+ for (auto item : edge_tri_.items()) {
+ std::cout << "tris for edge " << item.key << ": " << *item.value << "\n";
+ constexpr bool print_stats = false;
+ if (print_stats) {
+ edge_tri_.print_stats();
+ }
+ }
+ for (auto item : vert_edges_.items()) {
+ std::cout << "edges for vert " << item.key << ":\n";
+ for (const Edge &e : item.value) {
+ std::cout << " " << e << "\n";
+ }
+ std::cout << "\n";
+ }
+ }
+}
+
+TriMeshTopology::~TriMeshTopology()
+{
+ for (const Vector<int> *vec : edge_tri_.values()) {
+ delete vec;
+ }
+}
+
+/** A Patch is a maximal set of triangles that share manifold edges only. */
+class Patch {
+ Vector<int> tri_; /* Indices of triangles in the Patch. */
+
+ public:
+ Patch() = default;
+
+ void add_tri(int t)
+ {
+ tri_.append(t);
+ }
+
+ int tot_tri() const
+ {
+ return tri_.size();
+ }
+
+ int tri(int i) const
+ {
+ return tri_[i];
+ }
+
+ IndexRange tri_range() const
+ {
+ return IndexRange(tri_.size());
+ }
+
+ Span<int> tris() const
+ {
+ return Span<int>(tri_);
+ }
+
+ int cell_above{NO_INDEX};
+ int cell_below{NO_INDEX};
+ int component{NO_INDEX};
+};
+
+static std::ostream &operator<<(std::ostream &os, const Patch &patch)
+{
+ os << "Patch " << patch.tris();
+ if (patch.cell_above != NO_INDEX) {
+ os << " cell_above=" << patch.cell_above;
+ }
+ else {
+ os << " cell_above not set";
+ }
+ if (patch.cell_below != NO_INDEX) {
+ os << " cell_below=" << patch.cell_below;
+ }
+ else {
+ os << " cell_below not set";
+ }
+ return os;
+}
+
+class PatchesInfo {
+ /** All of the Patches for a #IMesh. */
+ Vector<Patch> patch_;
+ /** Patch index for corresponding triangle. */
+ Array<int> tri_patch_;
+ /** Shared edge for incident patches; (-1, -1) if none. */
+ Map<std::pair<int, int>, Edge> pp_edge_;
+
+ public:
+ explicit PatchesInfo(int ntri)
+ {
+ constexpr int max_expected_patch_patch_incidences = 100;
+ tri_patch_ = Array<int>(ntri, NO_INDEX);
+ pp_edge_.reserve(max_expected_patch_patch_incidences);
+ }
+
+ int tri_patch(int t) const
+ {
+ return tri_patch_[t];
+ }
+
+ int add_patch()
+ {
+ int patch_index = patch_.append_and_get_index(Patch());
+ return patch_index;
+ }
+
+ void grow_patch(int patch_index, int t)
+ {
+ tri_patch_[t] = patch_index;
+ patch_[patch_index].add_tri(t);
+ }
+
+ bool tri_is_assigned(int t) const
+ {
+ return tri_patch_[t] != NO_INDEX;
+ }
+
+ const Patch &patch(int patch_index) const
+ {
+ return patch_[patch_index];
+ }
+
+ Patch &patch(int patch_index)
+ {
+ return patch_[patch_index];
+ }
+
+ int tot_patch() const
+ {
+ return patch_.size();
+ }
+
+ IndexRange index_range() const
+ {
+ return IndexRange(patch_.size());
+ }
+
+ const Patch *begin() const
+ {
+ return patch_.begin();
+ }
+
+ const Patch *end() const
+ {
+ return patch_.end();
+ }
+
+ Patch *begin()
+ {
+ return patch_.begin();
+ }
+
+ Patch *end()
+ {
+ return patch_.end();
+ }
+
+ void add_new_patch_patch_edge(int p1, int p2, Edge e)
+ {
+ pp_edge_.add_new(std::pair<int, int>(p1, p2), e);
+ pp_edge_.add_new(std::pair<int, int>(p2, p1), e);
+ }
+
+ Edge patch_patch_edge(int p1, int p2)
+ {
+ return pp_edge_.lookup_default(std::pair<int, int>(p1, p2), Edge());
+ }
+};
+
+static bool apply_bool_op(BoolOpType bool_optype, const Array<int> &winding);
+
+/**
+ * A Cell is a volume of 3-space, surrounded by patches.
+ * We will partition all 3-space into Cells.
+ * One cell, the Ambient cell, contains all other cells.
+ */
+class Cell {
+ Vector<int> patches_;
+ Array<int> winding_;
+ int merged_to_{NO_INDEX};
+ bool winding_assigned_{false};
+ /* in_output_volume_ will be true when this cell should be in the output volume. */
+ bool in_output_volume_{false};
+ /* zero_volume_ will be true when this is a zero-volume cell (inside a stack of identical
+ * triangles). */
+ bool zero_volume_{false};
+
+ public:
+ Cell() = default;
+
+ void add_patch(int p)
+ {
+ patches_.append(p);
+ }
+
+ void add_patch_non_duplicates(int p)
+ {
+ patches_.append_non_duplicates(p);
+ }
+
+ const Span<int> patches() const
+ {
+ return Span<int>(patches_);
+ }
+
+ const Span<int> winding() const
+ {
+ return Span<int>(winding_);
+ }
+
+ void init_winding(int winding_len)
+ {
+ winding_ = Array<int>(winding_len);
+ }
+
+ void seed_ambient_winding()
+ {
+ winding_.fill(0);
+ winding_assigned_ = true;
+ }
+
+ void set_winding_and_in_output_volume(const Cell &from_cell,
+ int shape,
+ int delta,
+ BoolOpType bool_optype)
+ {
+ std::copy(from_cell.winding().begin(), from_cell.winding().end(), winding_.begin());
+ winding_[shape] += delta;
+ winding_assigned_ = true;
+ in_output_volume_ = apply_bool_op(bool_optype, winding_);
+ }
+
+ bool in_output_volume() const
+ {
+ return in_output_volume_;
+ }
+
+ bool winding_assigned() const
+ {
+ return winding_assigned_;
+ }
+
+ bool zero_volume() const
+ {
+ return zero_volume_;
+ }
+
+ int merged_to() const
+ {
+ return merged_to_;
+ }
+
+ void set_merged_to(int c)
+ {
+ merged_to_ = c;
+ }
+
+ /**
+ * Call this when it is possible that this Cell has zero volume,
+ * and if it does, set zero_volume_ to true.
+ */
+ void check_for_zero_volume(const PatchesInfo &pinfo, const IMesh &mesh);
+};
+
+static std::ostream &operator<<(std::ostream &os, const Cell &cell)
+{
+ os << "Cell patches " << cell.patches();
+ if (cell.winding().size() > 0) {
+ os << " winding=" << cell.winding();
+ os << " in_output_volume=" << cell.in_output_volume();
+ }
+ os << " zv=" << cell.zero_volume();
+ return os;
+}
+
+static bool tris_have_same_verts(const IMesh &mesh, int t1, int t2)
+{
+ const Face &tri1 = *mesh.face(t1);
+ const Face &tri2 = *mesh.face(t2);
+ BLI_assert(tri1.size() == 3 && tri2.size() == 3);
+ if (tri1.vert[0] == tri2.vert[0]) {
+ return ((tri1.vert[1] == tri2.vert[1] && tri1.vert[2] == tri2.vert[2]) ||
+ (tri1.vert[1] == tri2.vert[2] && tri1.vert[2] == tri2.vert[1]));
+ }
+ if (tri1.vert[0] == tri2.vert[1]) {
+ return ((tri1.vert[1] == tri2.vert[0] && tri1.vert[2] == tri2.vert[2]) ||
+ (tri1.vert[1] == tri2.vert[2] && tri1.vert[2] == tri2.vert[0]));
+ }
+ if (tri1.vert[0] == tri2.vert[2]) {
+ return ((tri1.vert[1] == tri2.vert[0] && tri1.vert[2] == tri2.vert[1]) ||
+ (tri1.vert[1] == tri2.vert[1] && tri1.vert[2] == tri2.vert[0]));
+ }
+ return false;
+}
+
+/**
+ * A Cell will have zero volume if it is bounded by exactly two patches and those
+ * patches are geometrically identical triangles (perhaps flipped versions of each other).
+ * If this Cell has zero volume, set its zero_volume_ member to true.
+ */
+void Cell::check_for_zero_volume(const PatchesInfo &pinfo, const IMesh &mesh)
+{
+ if (patches_.size() == 2) {
+ const Patch &p1 = pinfo.patch(patches_[0]);
+ const Patch &p2 = pinfo.patch(patches_[1]);
+ if (p1.tot_tri() == 1 && p2.tot_tri() == 1) {
+ if (tris_have_same_verts(mesh, p1.tri(0), p2.tri(0))) {
+ zero_volume_ = true;
+ }
+ }
+ }
+}
+
+/* Information about all the Cells. */
+class CellsInfo {
+ Vector<Cell> cell_;
+
+ public:
+ CellsInfo() = default;
+
+ int add_cell()
+ {
+ int index = cell_.append_and_get_index(Cell());
+ return index;
+ }
+
+ Cell &cell(int c)
+ {
+ return cell_[c];
+ }
+
+ const Cell &cell(int c) const
+ {
+ return cell_[c];
+ }
+
+ int tot_cell() const
+ {
+ return cell_.size();
+ }
+
+ IndexRange index_range() const
+ {
+ return cell_.index_range();
+ }
+
+ const Cell *begin() const
+ {
+ return cell_.begin();
+ }
+
+ const Cell *end() const
+ {
+ return cell_.end();
+ }
+
+ Cell *begin()
+ {
+ return cell_.begin();
+ }
+
+ Cell *end()
+ {
+ return cell_.end();
+ }
+
+ void init_windings(int winding_len)
+ {
+ for (Cell &cell : cell_) {
+ cell.init_winding(winding_len);
+ }
+ }
+};
+
+/**
+ * For Debugging: write a .obj file showing the patch/cell structure or just the cells.
+ */
+static void write_obj_cell_patch(const IMesh &m,
+ const CellsInfo &cinfo,
+ const PatchesInfo &pinfo,
+ bool cells_only,
+ const std::string &name)
+{
+ /* Would like to use #BKE_tempdir_base() here, but that brings in dependence on kernel library.
+ * This is just for developer debugging anyway,
+ * and should never be called in production Blender. */
+# ifdef _WIN_32
+ const char *objdir = BLI_getenv("HOME");
+# else
+ const char *objdir = "/tmp/";
+# endif
+
+ std::string fname = std::string(objdir) + name + std::string("_cellpatch.obj");
+ std::ofstream f;
+ f.open(fname);
+ if (!f) {
+ std::cout << "Could not open file " << fname << "\n";
+ return;
+ }
+
+ /* Copy IMesh so can populate verts. */
+ IMesh mm = m;
+ mm.populate_vert();
+ f << "o cellpatch\n";
+ for (const Vert *v : mm.vertices()) {
+ const double3 dv = v->co;
+ f << "v " << dv[0] << " " << dv[1] << " " << dv[2] << "\n";
+ }
+ if (!cells_only) {
+ for (int p : pinfo.index_range()) {
+ f << "g patch" << p << "\n";
+ const Patch &patch = pinfo.patch(p);
+ for (int t : patch.tris()) {
+ const Face &tri = *mm.face(t);
+ f << "f ";
+ for (const Vert *v : tri) {
+ f << mm.lookup_vert(v) + 1 << " ";
+ }
+ f << "\n";
+ }
+ }
+ }
+ for (int c : cinfo.index_range()) {
+ f << "g cell" << c << "\n";
+ const Cell &cell = cinfo.cell(c);
+ for (int p : cell.patches()) {
+ const Patch &patch = pinfo.patch(p);
+ for (int t : patch.tris()) {
+ const Face &tri = *mm.face(t);
+ f << "f ";
+ for (const Vert *v : tri) {
+ f << mm.lookup_vert(v) + 1 << " ";
+ }
+ f << "\n";
+ }
+ }
+ }
+ f.close();
+}
+
+static void merge_cells(int merge_to, int merge_from, CellsInfo &cinfo, PatchesInfo &pinfo)
+{
+ if (merge_to == merge_from) {
+ return;
+ }
+ Cell &merge_from_cell = cinfo.cell(merge_from);
+ Cell &merge_to_cell = cinfo.cell(merge_to);
+ int final_merge_to = merge_to;
+ while (merge_to_cell.merged_to() != NO_INDEX) {
+ final_merge_to = merge_to_cell.merged_to();
+ merge_to_cell = cinfo.cell(final_merge_to);
+ }
+ for (Patch &patch : pinfo) {
+ if (patch.cell_above == merge_from) {
+ patch.cell_above = final_merge_to;
+ }
+ if (patch.cell_below == merge_from) {
+ patch.cell_below = final_merge_to;
+ }
+ }
+ for (int cell_p : merge_from_cell.patches()) {
+ merge_to_cell.add_patch_non_duplicates(cell_p);
+ }
+ merge_from_cell.set_merged_to(final_merge_to);
+}
+
+/**
+ * Partition the triangles of \a tm into Patches.
+ */
+static PatchesInfo find_patches(const IMesh &tm, const TriMeshTopology &tmtopo)
+{
+ const int dbg_level = 0;
+ if (dbg_level > 0) {
+ std::cout << "\nFIND_PATCHES\n";
+ }
+ int ntri = tm.face_size();
+ PatchesInfo pinfo(ntri);
+ /* Algorithm: Grow patches across manifold edges as long as there are unassigned triangles. */
+ Stack<int> cur_patch_grow;
+ for (int t : tm.face_index_range()) {
+ if (pinfo.tri_patch(t) == -1) {
+ cur_patch_grow.push(t);
+ int cur_patch_index = pinfo.add_patch();
+ while (!cur_patch_grow.is_empty()) {
+ int tcand = cur_patch_grow.pop();
+ if (dbg_level > 1) {
+ std::cout << "pop tcand = " << tcand << "; assigned = " << pinfo.tri_is_assigned(tcand)
+ << "\n";
+ }
+ if (pinfo.tri_is_assigned(tcand)) {
+ continue;
+ }
+ if (dbg_level > 1) {
+ std::cout << "grow patch from seed tcand=" << tcand << "\n";
+ }
+ pinfo.grow_patch(cur_patch_index, tcand);
+ const Face &tri = *tm.face(tcand);
+ for (int i = 0; i < 3; ++i) {
+ Edge e(tri[i], tri[(i + 1) % 3]);
+ int t_other = tmtopo.other_tri_if_manifold(e, tcand);
+ if (dbg_level > 1) {
+ std::cout << " edge " << e << " generates t_other=" << t_other << "\n";
+ }
+ if (t_other != NO_INDEX) {
+ if (!pinfo.tri_is_assigned(t_other)) {
+ if (dbg_level > 1) {
+ std::cout << " push t_other = " << t_other << "\n";
+ }
+ cur_patch_grow.push(t_other);
+ }
+ }
+ else {
+ /* e is non-manifold. Set any patch-patch incidences we can. */
+ if (dbg_level > 1) {
+ std::cout << " e non-manifold case\n";
+ }
+ const Vector<int> *etris = tmtopo.edge_tris(e);
+ if (etris != nullptr) {
+ for (int i : etris->index_range()) {
+ int t_other = (*etris)[i];
+ if (t_other != tcand && pinfo.tri_is_assigned(t_other)) {
+ int p_other = pinfo.tri_patch(t_other);
+ if (p_other == cur_patch_index) {
+ continue;
+ }
+ if (pinfo.patch_patch_edge(cur_patch_index, p_other).v0() == nullptr) {
+ pinfo.add_new_patch_patch_edge(cur_patch_index, p_other, e);
+ if (dbg_level > 1) {
+ std::cout << "added patch_patch_edge (" << cur_patch_index << "," << p_other
+ << ") = " << e << "\n";
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if (dbg_level > 0) {
+ std::cout << "\nafter FIND_PATCHES: found " << pinfo.tot_patch() << " patches\n";
+ for (int p : pinfo.index_range()) {
+ std::cout << p << ": " << pinfo.patch(p) << "\n";
+ }
+ if (dbg_level > 1) {
+ std::cout << "\ntriangle map\n";
+ for (int t : tm.face_index_range()) {
+ std::cout << t << ": patch " << pinfo.tri_patch(t) << "\n";
+ }
+ }
+ std::cout << "\npatch-patch incidences\n";
+ for (int p1 : pinfo.index_range()) {
+ for (int p2 : pinfo.index_range()) {
+ Edge e = pinfo.patch_patch_edge(p1, p2);
+ if (e.v0() != nullptr) {
+ std::cout << "p" << p1 << " and p" << p2 << " share edge " << e << "\n";
+ }
+ }
+ }
+ }
+ return pinfo;
+}
+
+/**
+ * If e is an edge in tri, return the vertex that isn't part of tri,
+ * the "flap" vertex, or nullptr if e is not part of tri.
+ * Also, e may be reversed in tri.
+ * Set *r_rev to true if it is reversed, else false.
+ */
+static const Vert *find_flap_vert(const Face &tri, const Edge e, bool *r_rev)
+{
+ *r_rev = false;
+ const Vert *flapv;
+ if (tri[0] == e.v0()) {
+ if (tri[1] == e.v1()) {
+ *r_rev = false;
+ flapv = tri[2];
+ }
+ else {
+ if (tri[2] != e.v1()) {
+ return nullptr;
+ }
+ *r_rev = true;
+ flapv = tri[1];
+ }
+ }
+ else if (tri[1] == e.v0()) {
+ if (tri[2] == e.v1()) {
+ *r_rev = false;
+ flapv = tri[0];
+ }
+ else {
+ if (tri[0] != e.v1()) {
+ return nullptr;
+ }
+ *r_rev = true;
+ flapv = tri[2];
+ }
+ }
+ else {
+ if (tri[2] != e.v0()) {
+ return nullptr;
+ }
+ if (tri[0] == e.v1()) {
+ *r_rev = false;
+ flapv = tri[1];
+ }
+ else {
+ if (tri[1] != e.v1()) {
+ return nullptr;
+ }
+ *r_rev = true;
+ flapv = tri[0];
+ }
+ }
+ return flapv;
+}
+
+/**
+ * Triangle \a tri and tri0 share edge e.
+ * Classify \a tri with respect to tri0 as described in
+ * sort_tris_around_edge, and return 1, 2, 3, or 4 as \a tri is:
+ * (1) co-planar with tri0 and on same side of e
+ * (2) co-planar with tri0 and on opposite side of e
+ * (3) below plane of tri0
+ * (4) above plane of tri0
+ * For "above" and "below", we use the orientation of non-reversed
+ * orientation of tri0.
+ * Because of the way the intersect mesh was made, we can assume
+ * that if a triangle is in class 1 then it is has the same flap vert
+ * as tri0.
+ */
+static int sort_tris_class(const Face &tri, const Face &tri0, const Edge e)
+{
+ const int dbg_level = 0;
+ if (dbg_level > 0) {
+ std::cout << "classify e = " << e << "\n";
+ }
+ mpq3 a0 = tri0[0]->co_exact;
+ mpq3 a1 = tri0[1]->co_exact;
+ mpq3 a2 = tri0[2]->co_exact;
+ bool rev;
+ bool rev0;
+ const Vert *flapv0 = find_flap_vert(tri0, e, &rev0);
+ const Vert *flapv = find_flap_vert(tri, e, &rev);
+ if (dbg_level > 0) {
+ std::cout << " t0 = " << tri0[0] << " " << tri0[1] << " " << tri0[2];
+ std::cout << " rev0 = " << rev0 << " flapv0 = " << flapv0 << "\n";
+ std::cout << " t = " << tri[0] << " " << tri[1] << " " << tri[2];
+ std::cout << " rev = " << rev << " flapv = " << flapv << "\n";
+ }
+ BLI_assert(flapv != nullptr && flapv0 != nullptr);
+ const mpq3 flap = flapv->co_exact;
+ /* orient will be positive if flap is below oriented plane of a0,a1,a2. */
+ int orient = orient3d(a0, a1, a2, flap);
+ int ans;
+ if (orient > 0) {
+ ans = rev0 ? 4 : 3;
+ }
+ else if (orient < 0) {
+ ans = rev0 ? 3 : 4;
+ }
+ else {
+ ans = flapv == flapv0 ? 1 : 2;
+ }
+ if (dbg_level > 0) {
+ std::cout << " orient = " << orient << " ans = " << ans << "\n";
+ }
+ return ans;
+}
+
+constexpr int EXTRA_TRI_INDEX = INT_MAX;
+
+/**
+ * To ensure consistent ordering of co-planar triangles if they happen to be sorted around
+ * more than one edge, sort the triangle indices in g (in place) by their index -- but also apply
+ * a sign to the index: positive if the triangle has edge e in the same orientation,
+ * otherwise negative.
+ */
+static void sort_by_signed_triangle_index(Vector<int> &g,
+ const Edge e,
+ const IMesh &tm,
+ const Face *extra_tri)
+{
+ Array<int> signed_g(g.size());
+ for (int i : g.index_range()) {
+ const Face &tri = g[i] == EXTRA_TRI_INDEX ? *extra_tri : *tm.face(g[i]);
+ bool rev;
+ find_flap_vert(tri, e, &rev);
+ signed_g[i] = rev ? -g[i] : g[i];
+ }
+ std::sort(signed_g.begin(), signed_g.end());
+
+ for (int i : g.index_range()) {
+ g[i] = abs(signed_g[i]);
+ }
+}
+
+/**
+ * Sort the triangles \a tris, which all share edge e, as they appear
+ * geometrically clockwise when looking down edge e.
+ * Triangle t0 is the first triangle in the top-level call
+ * to this recursive routine. The merge step below differs
+ * for the top level call and all the rest, so this distinguishes those cases.
+ * Care is taken in the case of duplicate triangles to have
+ * an ordering that is consistent with that which would happen
+ * if another edge of the triangle were sorted around.
+ *
+ * We sometimes need to do this with an extra triangle that is not part of tm.
+ * To accommodate this:
+ * If extra_tri is non-null, then an index of EXTRA_TRI_INDEX should use it for the triangle.
+ */
+static Array<int> sort_tris_around_edge(const IMesh &tm,
+ const TriMeshTopology &tmtopo,
+ const Edge e,
+ const Span<int> tris,
+ const int t0,
+ const Face *extra_tri)
+{
+ /* Divide and conquer, quick-sort-like sort.
+ * Pick a triangle t0, then partition into groups:
+ * (1) co-planar with t0 and on same side of e
+ * (2) co-planar with t0 and on opposite side of e
+ * (3) below plane of t0
+ * (4) above plane of t0
+ * Each group is sorted and then the sorts are merged to give the answer.
+ * We don't expect the input array to be very large - should typically
+ * be only 3 or 4 - so OK to make copies of arrays instead of swapping
+ * around in a single array. */
+ const int dbg_level = 0;
+ if (tris.size() == 0) {
+ return Array<int>();
+ }
+ if (dbg_level > 0) {
+ if (t0 == tris[0]) {
+ std::cout << "\n";
+ }
+ std::cout << "sort_tris_around_edge " << e << "\n";
+ std::cout << "tris = " << tris << "\n";
+ std::cout << "t0 = " << t0 << "\n";
+ }
+ Vector<int> g1{tris[0]};
+ Vector<int> g2;
+ Vector<int> g3;
+ Vector<int> g4;
+ std::array<Vector<int> *, 4> groups = {&g1, &g2, &g3, &g4};
+ const Face &triref = *tm.face(tris[0]);
+ for (int i : tris.index_range()) {
+ if (i == 0) {
+ continue;
+ }
+ int t = tris[i];
+ BLI_assert(t < tm.face_size() || (t == EXTRA_TRI_INDEX && extra_tri != nullptr));
+ const Face &tri = (t == EXTRA_TRI_INDEX) ? *extra_tri : *tm.face(t);
+ if (dbg_level > 2) {
+ std::cout << "classifying tri " << t << " with respect to " << tris[0] << "\n";
+ }
+ int group_num = sort_tris_class(tri, triref, e);
+ if (dbg_level > 2) {
+ std::cout << " classify result : " << group_num << "\n";
+ }
+ groups[group_num - 1]->append(t);
+ }
+ if (dbg_level > 1) {
+ std::cout << "g1 = " << g1 << "\n";
+ std::cout << "g2 = " << g2 << "\n";
+ std::cout << "g3 = " << g3 << "\n";
+ std::cout << "g4 = " << g4 << "\n";
+ }
+ if (g1.size() > 1) {
+ sort_by_signed_triangle_index(g1, e, tm, extra_tri);
+ if (dbg_level > 1) {
+ std::cout << "g1 sorted: " << g1 << "\n";
+ }
+ }
+ if (g2.size() > 1) {
+ sort_by_signed_triangle_index(g2, e, tm, extra_tri);
+ if (dbg_level > 1) {
+ std::cout << "g2 sorted: " << g2 << "\n";
+ }
+ }
+ if (g3.size() > 1) {
+ Array<int> g3sorted = sort_tris_around_edge(tm, tmtopo, e, g3, t0, extra_tri);
+ std::copy(g3sorted.begin(), g3sorted.end(), g3.begin());
+ if (dbg_level > 1) {
+ std::cout << "g3 sorted: " << g3 << "\n";
+ }
+ }
+ if (g4.size() > 1) {
+ Array<int> g4sorted = sort_tris_around_edge(tm, tmtopo, e, g4, t0, extra_tri);
+ std::copy(g4sorted.begin(), g4sorted.end(), g4.begin());
+ if (dbg_level > 1) {
+ std::cout << "g4 sorted: " << g4 << "\n";
+ }
+ }
+ int group_tot_size = g1.size() + g2.size() + g3.size() + g4.size();
+ Array<int> ans(group_tot_size);
+ int *p = ans.begin();
+ if (tris[0] == t0) {
+ p = std::copy(g1.begin(), g1.end(), p);
+ p = std::copy(g4.begin(), g4.end(), p);
+ p = std::copy(g2.begin(), g2.end(), p);
+ std::copy(g3.begin(), g3.end(), p);
+ }
+ else {
+ p = std::copy(g3.begin(), g3.end(), p);
+ p = std::copy(g1.begin(), g1.end(), p);
+ p = std::copy(g4.begin(), g4.end(), p);
+ std::copy(g2.begin(), g2.end(), p);
+ }
+ if (dbg_level > 0) {
+ std::cout << "sorted tris = " << ans << "\n";
+ }
+ return ans;
+}
+
+/**
+ * Find the Cells around edge e.
+ * This possibly makes new cells in \a cinfo, and sets up the
+ * bipartite graph edges between cells and patches.
+ * Will modify \a pinfo and \a cinfo and the patches and cells they contain.
+ */
+static void find_cells_from_edge(const IMesh &tm,
+ const TriMeshTopology &tmtopo,
+ PatchesInfo &pinfo,
+ CellsInfo &cinfo,
+ const Edge e)
+{
+ const int dbg_level = 0;
+ if (dbg_level > 0) {
+ std::cout << "FIND_CELLS_FROM_EDGE " << e << "\n";
+ }
+ const Vector<int> *edge_tris = tmtopo.edge_tris(e);
+ BLI_assert(edge_tris != nullptr);
+ Array<int> sorted_tris = sort_tris_around_edge(
+ tm, tmtopo, e, Span<int>(*edge_tris), (*edge_tris)[0], nullptr);
+
+ int n_edge_tris = edge_tris->size();
+ Array<int> edge_patches(n_edge_tris);
+ for (int i = 0; i < n_edge_tris; ++i) {
+ edge_patches[i] = pinfo.tri_patch(sorted_tris[i]);
+ if (dbg_level > 1) {
+ std::cout << "edge_patches[" << i << "] = " << edge_patches[i] << "\n";
+ }
+ }
+ for (int i = 0; i < n_edge_tris; ++i) {
+ int inext = (i + 1) % n_edge_tris;
+ int r_index = edge_patches[i];
+ int rnext_index = edge_patches[inext];
+ Patch &r = pinfo.patch(r_index);
+ Patch &rnext = pinfo.patch(rnext_index);
+ bool r_flipped;
+ bool rnext_flipped;
+ find_flap_vert(*tm.face(sorted_tris[i]), e, &r_flipped);
+ find_flap_vert(*tm.face(sorted_tris[inext]), e, &rnext_flipped);
+ int *r_follow_cell = r_flipped ? &r.cell_below : &r.cell_above;
+ int *rnext_prev_cell = rnext_flipped ? &rnext.cell_above : &rnext.cell_below;
+ if (dbg_level > 0) {
+ std::cout << "process patch pair " << r_index << " " << rnext_index << "\n";
+ std::cout << " r_flipped = " << r_flipped << " rnext_flipped = " << rnext_flipped << "\n";
+ std::cout << " r_follow_cell (" << (r_flipped ? "below" : "above")
+ << ") = " << *r_follow_cell << "\n";
+ std::cout << " rnext_prev_cell (" << (rnext_flipped ? "above" : "below")
+ << ") = " << *rnext_prev_cell << "\n";
+ }
+ if (*r_follow_cell == NO_INDEX && *rnext_prev_cell == NO_INDEX) {
+ /* Neither is assigned: make a new cell. */
+ int c = cinfo.add_cell();
+ *r_follow_cell = c;
+ *rnext_prev_cell = c;
+ Cell &cell = cinfo.cell(c);
+ cell.add_patch(r_index);
+ cell.add_patch(rnext_index);
+ cell.check_for_zero_volume(pinfo, tm);
+ if (dbg_level > 0) {
+ std::cout << " made new cell " << c << "\n";
+ std::cout << " p" << r_index << "." << (r_flipped ? "cell_below" : "cell_above") << " = c"
+ << c << "\n";
+ std::cout << " p" << rnext_index << "." << (rnext_flipped ? "cell_above" : "cell_below")
+ << " = c" << c << "\n";
+ }
+ }
+ else if (*r_follow_cell != NO_INDEX && *rnext_prev_cell == NO_INDEX) {
+ int c = *r_follow_cell;
+ *rnext_prev_cell = c;
+ Cell &cell = cinfo.cell(c);
+ cell.add_patch(rnext_index);
+ cell.check_for_zero_volume(pinfo, tm);
+ if (dbg_level > 0) {
+ std::cout << " reuse r_follow: p" << rnext_index << "."
+ << (rnext_flipped ? "cell_above" : "cell_below") << " = c" << c << "\n";
+ }
+ }
+ else if (*r_follow_cell == NO_INDEX && *rnext_prev_cell != NO_INDEX) {
+ int c = *rnext_prev_cell;
+ *r_follow_cell = c;
+ Cell &cell = cinfo.cell(c);
+ cell.add_patch(r_index);
+ cell.check_for_zero_volume(pinfo, tm);
+ if (dbg_level > 0) {
+ std::cout << " reuse rnext prev: rprev_p" << r_index << "."
+ << (r_flipped ? "cell_below" : "cell_above") << " = c" << c << "\n";
+ }
+ }
+ else {
+ if (*r_follow_cell != *rnext_prev_cell) {
+ if (dbg_level > 0) {
+ std::cout << " merge cell " << *rnext_prev_cell << " into cell " << *r_follow_cell
+ << "\n";
+ }
+ merge_cells(*r_follow_cell, *rnext_prev_cell, cinfo, pinfo);
+ }
+ }
+ }
+}
+
+/**
+ * Find the partition of 3-space into Cells.
+ * This assigns the cell_above and cell_below for each Patch.
+ */
+static CellsInfo find_cells(const IMesh &tm, const TriMeshTopology &tmtopo, PatchesInfo &pinfo)
+{
+ const int dbg_level = 0;
+ if (dbg_level > 0) {
+ std::cout << "\nFIND_CELLS\n";
+ }
+ CellsInfo cinfo;
+ /* For each unique edge shared between patch pairs, process it. */
+ Set<Edge> processed_edges;
+ int np = pinfo.tot_patch();
+ for (int p = 0; p < np; ++p) {
+ for (int q = p + 1; q < np; ++q) {
+ Edge e = pinfo.patch_patch_edge(p, q);
+ if (e.v0() != nullptr) {
+ if (!processed_edges.contains(e)) {
+ processed_edges.add_new(e);
+ find_cells_from_edge(tm, tmtopo, pinfo, cinfo, e);
+ }
+ }
+ }
+ }
+ /* Some patches may have no cells at this point. These are either:
+ * (a) a closed manifold patch only incident on itself (sphere, torus, klein bottle, etc.).
+ * (b) an open manifold patch only incident on itself (has non-manifold boundaries).
+ * Make above and below cells for these patches. This will create a disconnected patch-cell
+ * bipartite graph, which will have to be fixed later. */
+ for (int p : pinfo.index_range()) {
+ Patch &patch = pinfo.patch(p);
+ if (patch.cell_above == NO_INDEX) {
+ int c = cinfo.add_cell();
+ patch.cell_above = c;
+ Cell &cell = cinfo.cell(c);
+ cell.add_patch(p);
+ }
+ if (patch.cell_below == NO_INDEX) {
+ int c = cinfo.add_cell();
+ patch.cell_below = c;
+ Cell &cell = cinfo.cell(c);
+ cell.add_patch(p);
+ }
+ }
+ if (dbg_level > 0) {
+ std::cout << "\nFIND_CELLS found " << cinfo.tot_cell() << " cells\nCells\n";
+ for (int i : cinfo.index_range()) {
+ std::cout << i << ": " << cinfo.cell(i) << "\n";
+ }
+ std::cout << "Patches\n";
+ for (int i : pinfo.index_range()) {
+ std::cout << i << ": " << pinfo.patch(i) << "\n";
+ }
+ if (dbg_level > 1) {
+ write_obj_cell_patch(tm, cinfo, pinfo, false, "postfindcells");
+ }
+ }
+ return cinfo;
+}
+
+/**
+ * Find the connected patch components (connects are via intermediate cells), and put
+ * component numbers in each patch.
+ * Return a Vector of components - each a Vector of the patch ids in the component.
+ */
+static Vector<Vector<int>> find_patch_components(const CellsInfo &cinfo, PatchesInfo &pinfo)
+{
+ constexpr int dbg_level = 0;
+ if (dbg_level > 0) {
+ std::cout << "FIND_PATCH_COMPONENTS\n";
+ }
+ if (pinfo.tot_patch() == 0) {
+ return Vector<Vector<int>>();
+ }
+ int current_component = 0;
+ Stack<int> stack; /* Patch indices to visit. */
+ Vector<Vector<int>> ans;
+ for (int pstart : pinfo.index_range()) {
+ Patch &patch_pstart = pinfo.patch(pstart);
+ if (patch_pstart.component != NO_INDEX) {
+ continue;
+ }
+ ans.append(Vector<int>());
+ ans[current_component].append(pstart);
+ stack.push(pstart);
+ patch_pstart.component = current_component;
+ while (!stack.is_empty()) {
+ int p = stack.pop();
+ Patch &patch = pinfo.patch(p);
+ BLI_assert(patch.component == current_component);
+ for (int c : {patch.cell_above, patch.cell_below}) {
+ for (int pn : cinfo.cell(c).patches()) {
+ Patch &patch_neighbor = pinfo.patch(pn);
+ if (patch_neighbor.component == NO_INDEX) {
+ patch_neighbor.component = current_component;
+ stack.push(pn);
+ ans[current_component].append(pn);
+ }
+ }
+ }
+ }
+ ++current_component;
+ }
+ if (dbg_level > 0) {
+ std::cout << "found " << ans.size() << " components\n";
+ for (int comp : ans.index_range()) {
+ std::cout << comp << ": " << ans[comp] << "\n";
+ }
+ }
+ return ans;
+}
+
+/**
+ * Do all patches have cell_above and cell_below set?
+ * Is the bipartite graph connected?
+ */
+static bool patch_cell_graph_ok(const CellsInfo &cinfo, const PatchesInfo &pinfo)
+{
+ for (int c : cinfo.index_range()) {
+ const Cell &cell = cinfo.cell(c);
+ if (cell.merged_to() != NO_INDEX) {
+ continue;
+ }
+ if (cell.patches().size() == 0) {
+ std::cout << "Patch/Cell graph disconnected at Cell " << c << " with no patches\n";
+ return false;
+ }
+ for (int p : cell.patches()) {
+ if (p >= pinfo.tot_patch()) {
+ std::cout << "Patch/Cell graph has bad patch index at Cell " << c << "\n";
+ return false;
+ }
+ }
+ }
+ for (int p : pinfo.index_range()) {
+ const Patch &patch = pinfo.patch(p);
+ if (patch.cell_above == NO_INDEX || patch.cell_below == NO_INDEX) {
+ std::cout << "Patch/Cell graph disconnected at Patch " << p
+ << " with one or two missing cells\n";
+ return false;
+ }
+ if (patch.cell_above >= cinfo.tot_cell() || patch.cell_below >= cinfo.tot_cell()) {
+ std::cout << "Patch/Cell graph has bad cell index at Patch " << p << "\n";
+ return false;
+ }
+ }
+ return true;
+}
+
+/**
+ * Is trimesh tm PWN ("Piece-wise constant Winding Number")?
+ * See Zhou et al. paper for exact definition, but roughly
+ * means that the faces connect so as to form closed volumes.
+ * The actual definition says that if you calculate the
+ * generalized winding number of every point not exactly on
+ * the mesh, it will always be an integer.
+ * Necessary (but not sufficient) conditions that a mesh be PWN:
+ * No edges with a non-zero sum of incident face directions.
+ * I think that cases like Klein bottles are likely to satisfy
+ * this without being PWN. So this routine will be only
+ * approximately right.
+ */
+static bool is_pwn(const IMesh &tm, const TriMeshTopology &tmtopo)
+{
+ constexpr int dbg_level = 0;
+ for (auto item : tmtopo.edge_tri_map_items()) {
+ const Edge &edge = item.key;
+ int tot_orient = 0;
+ /* For each face t attached to edge, add +1 if the edge
+ * is positively in t, and -1 if negatively in t. */
+ for (int t : *item.value) {
+ const Face &face = *tm.face(t);
+ BLI_assert(face.size() == 3);
+ for (int i : face.index_range()) {
+ if (face[i] == edge.v0()) {
+ if (face[(i + 1) % 3] == edge.v1()) {
+ ++tot_orient;
+ }
+ else {
+ BLI_assert(face[(i + 3 - 1) % 3] == edge.v1());
+ --tot_orient;
+ }
+ }
+ }
+ }
+ if (tot_orient != 0) {
+ if (dbg_level > 0) {
+ std::cout << "edge causing non-pwn: " << edge << "\n";
+ }
+ return false;
+ }
+ }
+ return true;
+}
+
+/**
+ * Find which of the cells around edge e contains point p.
+ * Do this by inserting a dummy triangle containing v and sorting the
+ * triangles around the edge to find out where in the sort order
+ * the dummy triangle lies, then finding which cell is between
+ * the two triangles on either side of the dummy.
+ */
+static int find_cell_for_point_near_edge(mpq3 p,
+ const Edge &e,
+ const IMesh &tm,
+ const TriMeshTopology &tmtopo,
+ const PatchesInfo &pinfo,
+ IMeshArena *arena)
+{
+ constexpr int dbg_level = 0;
+ if (dbg_level > 0) {
+ std::cout << "FIND_CELL_FOR_POINT_NEAR_EDGE, p=" << p << " e=" << e << "\n";
+ }
+ const Vector<int> *etris = tmtopo.edge_tris(e);
+ const Vert *dummy_vert = arena->add_or_find_vert(p, NO_INDEX);
+ const Face *dummy_tri = arena->add_face({e.v0(), e.v1(), dummy_vert},
+ NO_INDEX,
+ {NO_INDEX, NO_INDEX, NO_INDEX},
+ {false, false, false});
+ BLI_assert(etris != nullptr);
+ Array<int> edge_tris(etris->size() + 1);
+ std::copy(etris->begin(), etris->end(), edge_tris.begin());
+ edge_tris[edge_tris.size() - 1] = EXTRA_TRI_INDEX;
+ Array<int> sorted_tris = sort_tris_around_edge(
+ tm, tmtopo, e, edge_tris, edge_tris[0], dummy_tri);
+ if (dbg_level > 0) {
+ std::cout << "sorted tris = " << sorted_tris << "\n";
+ }
+ int *p_sorted_dummy = std::find(sorted_tris.begin(), sorted_tris.end(), EXTRA_TRI_INDEX);
+ BLI_assert(p_sorted_dummy != sorted_tris.end());
+ int dummy_index = p_sorted_dummy - sorted_tris.begin();
+ int prev_tri = (dummy_index == 0) ? sorted_tris[sorted_tris.size() - 1] :
+ sorted_tris[dummy_index - 1];
+ int next_tri = (dummy_index == sorted_tris.size() - 1) ? sorted_tris[0] :
+ sorted_tris[dummy_index + 1];
+ if (dbg_level > 0) {
+ std::cout << "prev tri to dummy = " << prev_tri << "; next tri to dummy = " << next_tri
+ << "\n";
+ }
+ const Patch &prev_patch = pinfo.patch(pinfo.tri_patch(prev_tri));
+ if (dbg_level > 0) {
+ std::cout << "prev_patch = " << prev_patch << "\n";
+ }
+ bool prev_flipped;
+ find_flap_vert(*tm.face(prev_tri), e, &prev_flipped);
+ int c = prev_flipped ? prev_patch.cell_below : prev_patch.cell_above;
+ if (dbg_level > 0) {
+ std::cout << "find_cell_for_point_near_edge returns " << c << "\n";
+ }
+ return c;
+}
+
+/**
+ * Find the ambient cell -- that is, the cell that is outside
+ * all other cells.
+ * If component_patches != nullptr, restrict consideration to patches
+ * in that vector.
+ *
+ * The method is to find an edge known to be on the convex hull
+ * of the mesh, then insert a dummy triangle that has that edge
+ * and a point known to be outside the whole mesh. Then sorting
+ * the triangles around the edge will reveal where the dummy triangle
+ * fits in that sorting order, and hence, the two adjacent patches
+ * to the dummy triangle - thus revealing the cell that the point
+ * known to be outside the whole mesh is in.
+ */
+static int find_ambient_cell(const IMesh &tm,
+ const Vector<int> *component_patches,
+ const TriMeshTopology &tmtopo,
+ const PatchesInfo &pinfo,
+ IMeshArena *arena)
+{
+ int dbg_level = 0;
+ if (dbg_level > 0) {
+ std::cout << "FIND_AMBIENT_CELL\n";
+ }
+ /* First find a vertex with the maximum x value. */
+ /* Prefer not to populate the verts in the #IMesh just for this. */
+ const Vert *v_extreme;
+ mpq_class extreme_x;
+ if (component_patches == nullptr) {
+ v_extreme = (*tm.face(0))[0];
+ extreme_x = v_extreme->co_exact.x;
+ for (const Face *f : tm.faces()) {
+ for (const Vert *v : *f) {
+ const mpq_class &x = v->co_exact.x;
+ if (x > extreme_x) {
+ v_extreme = v;
+ extreme_x = x;
+ }
+ }
+ }
+ }
+ else {
+ if (dbg_level > 0) {
+ std::cout << "restrict to patches " << *component_patches << "\n";
+ }
+ int p0 = (*component_patches)[0];
+ v_extreme = (*tm.face(pinfo.patch(p0).tri(0)))[0];
+ extreme_x = v_extreme->co_exact.x;
+ for (int p : *component_patches) {
+ for (int t : pinfo.patch(p).tris()) {
+ const Face *f = tm.face(t);
+ for (const Vert *v : *f) {
+ const mpq_class &x = v->co_exact.x;
+ if (x > extreme_x) {
+ v_extreme = v;
+ extreme_x = x;
+ }
+ }
+ }
+ }
+ }
+ if (dbg_level > 0) {
+ std::cout << "v_extreme = " << v_extreme << "\n";
+ }
+ /* Find edge attached to v_extreme with max absolute slope
+ * when projected onto the XY plane. That edge is guaranteed to
+ * be on the convex hull of the mesh. */
+ const Vector<Edge> &edges = tmtopo.vert_edges(v_extreme);
+ const mpq_class extreme_y = v_extreme->co_exact.y;
+ Edge ehull;
+ mpq_class max_abs_slope = -1;
+ for (Edge e : edges) {
+ const Vert *v_other = (e.v0() == v_extreme) ? e.v1() : e.v0();
+ const mpq3 &co_other = v_other->co_exact;
+ mpq_class delta_x = co_other.x - extreme_x;
+ if (delta_x == 0) {
+ /* Vertical slope. */
+ ehull = e;
+ break;
+ }
+ mpq_class abs_slope = abs((co_other.y - extreme_y) / delta_x);
+ if (abs_slope > max_abs_slope) {
+ ehull = e;
+ max_abs_slope = abs_slope;
+ }
+ }
+ if (dbg_level > 0) {
+ std::cout << "ehull = " << ehull << " slope = " << max_abs_slope << "\n";
+ }
+ /* Sort triangles around ehull, including a dummy triangle that include a known point in ambient
+ * cell. */
+ mpq3 p_in_ambient = v_extreme->co_exact;
+ p_in_ambient.x += 1;
+ int c_ambient = find_cell_for_point_near_edge(p_in_ambient, ehull, tm, tmtopo, pinfo, arena);
+ if (dbg_level > 0) {
+ std::cout << "FIND_AMBIENT_CELL returns " << c_ambient << "\n";
+ }
+ return c_ambient;
+}
+
+/**
+ * We need an edge on the convex hull of the edges incident on \a closestp
+ * in order to sort around, including a dummy triangle that has \a testp and
+ * the sorting edge vertices. So we don't want an edge that is co-linear
+ * with the line through \a testp and \a closestp.
+ * The method is to project onto a plane that contains `testp-closestp`,
+ * and then choose the edge that, when projected, has the maximum absolute
+ * slope (regarding the line `testp-closestp` as the x-axis for slope computation).
+ */
+static Edge find_good_sorting_edge(const Vert *testp,
+ const Vert *closestp,
+ const TriMeshTopology &tmtopo)
+{
+ constexpr int dbg_level = 0;
+ if (dbg_level > 0) {
+ std::cout << "FIND_GOOD_SORTING_EDGE testp = " << testp << ", closestp = " << closestp << "\n";
+ }
+ /* We want to project the edges incident to closestp onto a plane
+ * whose ordinate direction will be regarded as going from closetp to testp,
+ * and whose abscissa direction is some perpendicular to that.
+ * A perpendicular direction can be found by swapping two coordinates
+ * and negating one, and zeroing out the third, being careful that one
+ * of the swapped vertices is non-zero. */
+ const mpq3 &co_closest = closestp->co_exact;
+ const mpq3 &co_test = testp->co_exact;
+ BLI_assert(co_test != co_closest);
+ mpq3 abscissa = co_test - co_closest;
+ /* Find a non-zero-component axis of abscissa. */
+ int axis;
+ for (axis = 0; axis < 3; ++axis) {
+ if (abscissa[axis] != 0) {
+ break;
+ }
+ }
+ BLI_assert(axis < 3);
+ int axis_next = (axis + 1) % 3;
+ int axis_next_next = (axis_next + 1) % 3;
+ mpq3 ordinate;
+ ordinate[axis] = abscissa[axis_next];
+ ordinate[axis_next] = -abscissa[axis];
+ ordinate[axis_next_next] = 0;
+ /* By construction, dot(abscissa, ordinate) == 0, so they are perpendicular. */
+ mpq3 normal = mpq3::cross(abscissa, ordinate);
+ if (dbg_level > 0) {
+ std::cout << "abscissa = " << abscissa << "\n";
+ std::cout << "ordinate = " << ordinate << "\n";
+ std::cout << "normal = " << normal << "\n";
+ }
+ mpq_class nlen2 = normal.length_squared();
+ mpq_class max_abs_slope = -1;
+ Edge esort;
+ const Vector<Edge> &edges = tmtopo.vert_edges(closestp);
+ for (Edge e : edges) {
+ const Vert *v_other = (e.v0() == closestp) ? e.v1() : e.v0();
+ const mpq3 &co_other = v_other->co_exact;
+ mpq3 evec = co_other - co_closest;
+ /* Get projection of evec onto plane of abscissa and ordinate. */
+ mpq3 proj_evec = evec - (mpq3::dot(evec, normal) / nlen2) * normal;
+ /* The projection calculations along the abscissa and ordinate should
+ * be scaled by 1/abscissa and 1/ordinate respectively,
+ * but we can skip: it won't affect which `evec` has the maximum slope. */
+ mpq_class evec_a = mpq3::dot(proj_evec, abscissa);
+ mpq_class evec_o = mpq3::dot(proj_evec, ordinate);
+ if (dbg_level > 0) {
+ std::cout << "e = " << e << "\n";
+ std::cout << "v_other = " << v_other << "\n";
+ std::cout << "evec = " << evec << ", proj_evec = " << proj_evec << "\n";
+ std::cout << "evec_a = " << evec_a << ", evec_o = " << evec_o << "\n";
+ }
+ if (evec_a == 0) {
+ /* evec is perpendicular to abscissa. */
+ esort = e;
+ if (dbg_level > 0) {
+ std::cout << "perpendicular esort is " << esort << "\n";
+ }
+ break;
+ }
+ mpq_class abs_slope = abs(evec_o / evec_a);
+ if (abs_slope > max_abs_slope) {
+ esort = e;
+ max_abs_slope = abs_slope;
+ if (dbg_level > 0) {
+ std::cout << "with abs_slope " << abs_slope << " new esort is " << esort << "\n";
+ }
+ }
+ }
+ return esort;
+}
+
+/**
+ * Find the cell that contains v. Consider the cells adjacent to triangle t.
+ * The close_edge and close_vert values are what were returned by
+ * #closest_on_tri_to_point when determining that v was close to t.
+ * They will indicate whether the point of closest approach to t is to
+ * an edge of t, a vertex of t, or somewhere inside t.
+ *
+ * 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.
+ */
+static int find_containing_cell(const Vert *v,
+ int t,
+ int close_edge,
+ int close_vert,
+ const PatchesInfo &pinfo,
+ const IMesh &tm,
+ const TriMeshTopology &tmtopo,
+ IMeshArena *arena)
+{
+ constexpr int dbg_level = 0;
+ if (dbg_level > 0) {
+ std::cout << "FIND_CONTAINING_CELL v=" << v << ", t=" << t << "\n";
+ }
+ const Face &tri = *tm.face(t);
+ Edge etest;
+ if (close_edge == -1 && close_vert == -1) {
+ /* Choose any edge if closest point is inside the triangle. */
+ close_edge = 0;
+ }
+ if (close_edge != -1) {
+ const Vert *v0 = tri[close_edge];
+ const Vert *v1 = tri[(close_edge + 1) % 3];
+ const Vector<Edge> &edges = tmtopo.vert_edges(v0);
+ if (dbg_level > 0) {
+ std::cout << "look for edge containing " << v0 << " and " << v1 << "\n";
+ std::cout << " in edges: ";
+ for (Edge e : edges) {
+ std::cout << e << " ";
+ }
+ std::cout << "\n";
+ }
+ for (Edge e : edges) {
+ if ((e.v0() == v0 && e.v1() == v1) || (e.v0() == v1 && e.v1() == v0)) {
+ etest = e;
+ break;
+ }
+ }
+ }
+ else {
+ int cv = close_vert;
+ const Vert *vert_cv = tri[cv];
+ if (vert_cv == v) {
+ /* Need to use another one to find sorting edge. */
+ vert_cv = tri[(cv + 1) % 3];
+ BLI_assert(vert_cv != v);
+ }
+ etest = find_good_sorting_edge(v, vert_cv, tmtopo);
+ }
+ BLI_assert(etest.v0() != nullptr);
+ if (dbg_level > 0) {
+ std::cout << "etest = " << etest << "\n";
+ }
+ int c = find_cell_for_point_near_edge(v->co_exact, etest, tm, tmtopo, pinfo, arena);
+ if (dbg_level > 0) {
+ std::cout << "find_containing_cell returns " << c << "\n";
+ }
+ return c;
+}
+
+/**
+ * Find the closest point in triangle (a, b, c) to point p.
+ * Return the distance squared to that point.
+ * Also, if the closest point in the triangle is on a vertex,
+ * return 0, 1, or 2 for a, b, c in *r_vert; else -1.
+ * If the closest point is on an edge, return 0, 1, or 2
+ * for edges ab, bc, or ca in *r_edge; else -1.
+ * (Adapted from #closest_on_tri_to_point_v3()).
+ */
+static mpq_class closest_on_tri_to_point(
+ const mpq3 &p, const mpq3 &a, const mpq3 &b, const mpq3 &c, int *r_edge, int *r_vert)
+{
+ constexpr int dbg_level = 0;
+ if (dbg_level > 0) {
+ std::cout << "CLOSEST_ON_TRI_TO_POINT p = " << p << "\n";
+ std::cout << " a = " << a << ", b = " << b << ", c = " << c << "\n";
+ }
+ /* Check if p in vertex region outside a. */
+ mpq3 ab = b - a;
+ mpq3 ac = c - a;
+ mpq3 ap = p - a;
+ mpq_class d1 = mpq3::dot(ab, ap);
+ mpq_class d2 = mpq3::dot(ac, ap);
+ if (d1 <= 0 && d2 <= 0) {
+ /* Barycentric coordinates (1,0,0). */
+ *r_edge = -1;
+ *r_vert = 0;
+ if (dbg_level > 0) {
+ std::cout << " answer = a\n";
+ }
+ return mpq3::distance_squared(p, a);
+ }
+ /* Check if p in vertex region outside b. */
+ mpq3 bp = p - b;
+ mpq_class d3 = mpq3::dot(ab, bp);
+ mpq_class d4 = mpq3::dot(ac, bp);
+ if (d3 >= 0 && d4 <= d3) {
+ /* Barycentric coordinates (0,1,0). */
+ *r_edge = -1;
+ *r_vert = 1;
+ if (dbg_level > 0) {
+ std::cout << " answer = b\n";
+ }
+ return mpq3::distance_squared(p, b);
+ }
+ /* Check if p in region of ab. */
+ mpq_class vc = d1 * d4 - d3 * d2;
+ if (vc <= 0 && d1 >= 0 && d3 <= 0) {
+ mpq_class v = d1 / (d1 - d3);
+ /* Barycentric coordinates (1-v,v,0). */
+ mpq3 r = a + v * ab;
+ *r_vert = -1;
+ *r_edge = 0;
+ if (dbg_level > 0) {
+ std::cout << " answer = on ab at " << r << "\n";
+ }
+ return mpq3::distance_squared(p, r);
+ }
+ /* Check if p in vertex region outside c. */
+ mpq3 cp = p - c;
+ mpq_class d5 = mpq3::dot(ab, cp);
+ mpq_class d6 = mpq3::dot(ac, cp);
+ if (d6 >= 0 && d5 <= d6) {
+ /* Barycentric coordinates (0,0,1). */
+ *r_edge = -1;
+ *r_vert = 2;
+ if (dbg_level > 0) {
+ std::cout << " answer = c\n";
+ }
+ return mpq3::distance_squared(p, c);
+ }
+ /* Check if p in edge region of ac. */
+ mpq_class vb = d5 * d2 - d1 * d6;
+ if (vb <= 0 && d2 >= 0 && d6 <= 0) {
+ mpq_class w = d2 / (d2 - d6);
+ /* Barycentric coordinates (1-w,0,w). */
+ mpq3 r = a + w * ac;
+ *r_vert = -1;
+ *r_edge = 2;
+ if (dbg_level > 0) {
+ std::cout << " answer = on ac at " << r << "\n";
+ }
+ return mpq3::distance_squared(p, r);
+ }
+ /* Check if p in edge region of bc. */
+ mpq_class va = d3 * d6 - d5 * d4;
+ if (va <= 0 && (d4 - d3) >= 0 && (d5 - d6) >= 0) {
+ mpq_class w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
+ /* Barycentric coordinates (0,1-w,w). */
+ mpq3 r = c - b;
+ r = w * r;
+ r = r + b;
+ *r_vert = -1;
+ *r_edge = 1;
+ if (dbg_level > 0) {
+ std::cout << " answer = on bc at " << r << "\n";
+ }
+ return mpq3::distance_squared(p, r);
+ }
+ /* p inside face region. Compute barycentric coordinates (u,v,w). */
+ mpq_class denom = 1 / (va + vb + vc);
+ mpq_class v = vb * denom;
+ mpq_class w = vc * denom;
+ ac = w * ac;
+ mpq3 r = a + v * ab;
+ r = r + ac;
+ *r_vert = -1;
+ *r_edge = -1;
+ if (dbg_level > 0) {
+ std::cout << " answer = inside at " << r << "\n";
+ }
+ return mpq3::distance_squared(p, r);
+}
+
+struct ComponentContainer {
+ int containing_component{NO_INDEX};
+ int nearest_cell{NO_INDEX};
+ mpq_class dist_to_cell;
+
+ ComponentContainer(int cc, int cell, mpq_class d)
+ : containing_component(cc), nearest_cell(cell), dist_to_cell(d)
+ {
+ }
+};
+
+/**
+ * Find out all the components, not equal to comp, that contain a point
+ * in comp in a non-ambient cell of those components.
+ * In other words, find the components that comp is nested inside
+ * (maybe not directly nested, which is why there can be more than one).
+ */
+static Vector<ComponentContainer> find_component_containers(int comp,
+ const Vector<Vector<int>> &components,
+ const Array<int> &ambient_cell,
+ const IMesh &tm,
+ const PatchesInfo &pinfo,
+ const TriMeshTopology &tmtopo,
+ IMeshArena *arena)
+{
+ constexpr int dbg_level = 0;
+ if (dbg_level > 0) {
+ std::cout << "FIND_COMPONENT_CONTAINERS for comp " << comp << "\n";
+ }
+ Vector<ComponentContainer> ans;
+ int test_p = components[comp][0];
+ int test_t = pinfo.patch(test_p).tri(0);
+ const Vert *test_v = tm.face(test_t)[0].vert[0];
+ if (dbg_level > 0) {
+ std::cout << "test vertex in comp: " << test_v << "\n";
+ }
+ for (int comp_other : components.index_range()) {
+ if (comp == comp_other) {
+ continue;
+ }
+ if (dbg_level > 0) {
+ std::cout << "comp_other = " << comp_other << "\n";
+ }
+ int nearest_tri = NO_INDEX;
+ int nearest_tri_close_vert = -1;
+ int nearest_tri_close_edge = -1;
+ mpq_class nearest_tri_dist_squared;
+ for (int p : components[comp_other]) {
+ const Patch &patch = pinfo.patch(p);
+ for (int t : patch.tris()) {
+ const Face &tri = *tm.face(t);
+ if (dbg_level > 1) {
+ std::cout << "tri " << t << " = " << &tri << "\n";
+ }
+ int close_vert;
+ int close_edge;
+ mpq_class d2 = closest_on_tri_to_point(test_v->co_exact,
+ tri[0]->co_exact,
+ tri[1]->co_exact,
+ tri[2]->co_exact,
+ &close_edge,
+ &close_vert);
+ if (dbg_level > 1) {
+ std::cout << " close_edge=" << close_edge << " close_vert=" << close_vert
+ << " dsquared=" << d2.get_d() << "\n";
+ }
+ if (nearest_tri == NO_INDEX || d2 < nearest_tri_dist_squared) {
+ nearest_tri = t;
+ nearest_tri_close_edge = close_edge;
+ nearest_tri_close_vert = close_vert;
+ nearest_tri_dist_squared = d2;
+ }
+ }
+ }
+ if (dbg_level > 0) {
+ std::cout << "closest tri to comp=" << comp << " in comp_other=" << comp_other << " is t"
+ << nearest_tri << "\n";
+ const Face *tn = tm.face(nearest_tri);
+ std::cout << "tri = " << tn << "\n";
+ std::cout << " (" << tn->vert[0]->co << "," << tn->vert[1]->co << "," << tn->vert[2]->co
+ << ")\n";
+ }
+ int containing_cell = find_containing_cell(test_v,
+ nearest_tri,
+ nearest_tri_close_edge,
+ nearest_tri_close_vert,
+
+ pinfo,
+ tm,
+ tmtopo,
+ arena);
+ if (dbg_level > 0) {
+ std::cout << "containing cell = " << containing_cell << "\n";
+ }
+ if (containing_cell != ambient_cell[comp_other]) {
+ ans.append(ComponentContainer(comp_other, containing_cell, nearest_tri_dist_squared));
+ }
+ }
+ return ans;
+}
+
+/**
+ * The cells and patches are supposed to form a bipartite graph.
+ * The graph may be disconnected (if parts of meshes are nested or side-by-side
+ * without intersection with other each other).
+ * Connect the bipartite graph. This involves discovering the connected components
+ * of the patches, then the nesting structure of those components.
+ */
+static void finish_patch_cell_graph(const IMesh &tm,
+ CellsInfo &cinfo,
+ PatchesInfo &pinfo,
+ const TriMeshTopology &tmtopo,
+ IMeshArena *arena)
+{
+ constexpr int dbg_level = 0;
+ if (dbg_level > 0) {
+ std::cout << "FINISH_PATCH_CELL_GRAPH\n";
+ }
+ Vector<Vector<int>> components = find_patch_components(cinfo, pinfo);
+ if (components.size() <= 1) {
+ if (dbg_level > 0) {
+ std::cout << "one component so finish_patch_cell_graph does no work\n";
+ }
+ return;
+ }
+ if (dbg_level > 0) {
+ std::cout << "components:\n";
+ for (int comp : components.index_range()) {
+ std::cout << comp << ": " << components[comp] << "\n";
+ }
+ }
+ Array<int> ambient_cell(components.size());
+ for (int comp : components.index_range()) {
+ ambient_cell[comp] = find_ambient_cell(tm, &components[comp], tmtopo, pinfo, arena);
+ }
+ if (dbg_level > 0) {
+ std::cout << "ambient cells:\n";
+ for (int comp : ambient_cell.index_range()) {
+ std::cout << comp << ": " << ambient_cell[comp] << "\n";
+ }
+ }
+ int tot_components = components.size();
+ Array<Vector<ComponentContainer>> comp_cont(tot_components);
+ for (int comp : components.index_range()) {
+ comp_cont[comp] = find_component_containers(
+ comp, components, ambient_cell, tm, pinfo, tmtopo, arena);
+ }
+ if (dbg_level > 0) {
+ std::cout << "component containers:\n";
+ for (int comp : comp_cont.index_range()) {
+ std::cout << comp << ": ";
+ for (const ComponentContainer &cc : comp_cont[comp]) {
+ std::cout << "[containing_comp=" << cc.containing_component
+ << ", nearest_cell=" << cc.nearest_cell << ", d2=" << cc.dist_to_cell << "] ";
+ }
+ std::cout << "\n";
+ }
+ }
+ if (dbg_level > 1) {
+ write_obj_cell_patch(tm, cinfo, pinfo, false, "beforemerge");
+ }
+ /* For nested components, merge their ambient cell with the nearest containing cell. */
+ Vector<int> outer_components;
+ for (int comp : comp_cont.index_range()) {
+ if (comp_cont[comp].size() == 0) {
+ outer_components.append(comp);
+ }
+ else {
+ ComponentContainer &closest = comp_cont[comp][0];
+ for (int i = 1; i < comp_cont[comp].size(); ++i) {
+ if (comp_cont[comp][i].dist_to_cell < closest.dist_to_cell) {
+ closest = comp_cont[comp][i];
+ }
+ }
+ int comp_ambient = ambient_cell[comp];
+ int cont_cell = closest.nearest_cell;
+ if (dbg_level > 0) {
+ std::cout << "merge comp " << comp << "'s ambient cell=" << comp_ambient << " to cell "
+ << cont_cell << "\n";
+ }
+ merge_cells(cont_cell, comp_ambient, cinfo, pinfo);
+ }
+ }
+ /* For outer components (not nested in any other component), merge their ambient cells. */
+ if (outer_components.size() > 1) {
+ int merged_ambient = ambient_cell[outer_components[0]];
+ for (int i = 1; i < outer_components.size(); ++i) {
+ if (dbg_level > 0) {
+ std::cout << "merge comp " << outer_components[i]
+ << "'s ambient cell=" << ambient_cell[outer_components[i]] << " to cell "
+ << merged_ambient << "\n";
+ }
+ merge_cells(merged_ambient, ambient_cell[outer_components[i]], cinfo, pinfo);
+ }
+ }
+ if (dbg_level > 0) {
+ std::cout << "after FINISH_PATCH_CELL_GRAPH\nCells\n";
+ for (int i : cinfo.index_range()) {
+ if (cinfo.cell(i).merged_to() == NO_INDEX) {
+ std::cout << i << ": " << cinfo.cell(i) << "\n";
+ }
+ }
+ std::cout << "Patches\n";
+ for (int i : pinfo.index_range()) {
+ std::cout << i << ": " << pinfo.patch(i) << "\n";
+ }
+ if (dbg_level > 1) {
+ write_obj_cell_patch(tm, cinfo, pinfo, false, "finished");
+ }
+ }
+}
+
+/**
+ * Starting with ambient cell c_ambient, with all zeros for winding numbers,
+ * propagate winding numbers to all the other cells.
+ * There will be a vector of \a nshapes winding numbers in each cell, one per
+ * input shape.
+ * As one crosses a patch into a new cell, the original shape (mesh part)
+ * that that patch was part of dictates which winding number changes.
+ * The shape_fn(triangle_number) function should return the shape that the
+ * triangle is part of.
+ * Also, as soon as the winding numbers for a cell are set, use bool_optype
+ * to decide whether that cell is included or excluded from the boolean output.
+ * If included, the cell's in_output_volume will be set to true.
+ */
+static void propagate_windings_and_in_output_volume(PatchesInfo &pinfo,
+ CellsInfo &cinfo,
+ int c_ambient,
+ BoolOpType op,
+ int nshapes,
+ std::function<int(int)> shape_fn)
+{
+ int dbg_level = 0;
+ if (dbg_level > 0) {
+ std::cout << "PROPAGATE_WINDINGS, ambient cell = " << c_ambient << "\n";
+ }
+ Cell &cell_ambient = cinfo.cell(c_ambient);
+ cell_ambient.seed_ambient_winding();
+ /* Use a vector as a queue. It can't grow bigger than number of cells. */
+ Vector<int> queue;
+ queue.reserve(cinfo.tot_cell());
+ int queue_head = 0;
+ queue.append(c_ambient);
+ while (queue_head < queue.size()) {
+ int c = queue[queue_head++];
+ if (dbg_level > 1) {
+ std::cout << "process cell " << c << "\n";
+ }
+ Cell &cell = cinfo.cell(c);
+ for (int p : cell.patches()) {
+ Patch &patch = pinfo.patch(p);
+ bool p_above_c = patch.cell_below == c;
+ int c_neighbor = p_above_c ? patch.cell_above : patch.cell_below;
+ if (dbg_level > 1) {
+ std::cout << " patch " << p << " p_above_c = " << p_above_c << "\n";
+ std::cout << " c_neighbor = " << c_neighbor << "\n";
+ }
+ Cell &cell_neighbor = cinfo.cell(c_neighbor);
+ if (!cell_neighbor.winding_assigned()) {
+ int winding_delta = p_above_c ? -1 : 1;
+ int t = patch.tri(0);
+ int shape = shape_fn(t);
+ BLI_assert(shape < nshapes);
+ UNUSED_VARS_NDEBUG(nshapes);
+ if (dbg_level > 1) {
+ std::cout << " representative tri " << t << ": in shape " << shape << "\n";
+ }
+ cell_neighbor.set_winding_and_in_output_volume(cell, shape, winding_delta, op);
+ if (dbg_level > 1) {
+ std::cout << " now cell_neighbor = " << cell_neighbor << "\n";
+ }
+ queue.append(c_neighbor);
+ BLI_assert(queue.size() <= cinfo.tot_cell());
+ }
+ }
+ }
+ if (dbg_level > 0) {
+ std::cout << "\nPROPAGATE_WINDINGS result\n";
+ for (int i = 0; i < cinfo.tot_cell(); ++i) {
+ std::cout << i << ": " << cinfo.cell(i) << "\n";
+ }
+ }
+}
+
+/**
+ * Given an array of winding numbers, where the ith entry is a cell's winding
+ * number with respect to input shape (mesh part) i, return true if the
+ * cell should be included in the output of the boolean operation.
+ * Intersection: all the winding numbers must be nonzero.
+ * Union: at least one winding number must be nonzero.
+ * Difference (first shape minus the rest): first winding number must be nonzero
+ * and the rest must have at least one zero winding number.
+ */
+static bool apply_bool_op(BoolOpType bool_optype, const Array<int> &winding)
+{
+ int nw = winding.size();
+ BLI_assert(nw > 0);
+ switch (bool_optype) {
+ case BoolOpType::Intersect: {
+ for (int i = 0; i < nw; ++i) {
+ if (winding[i] == 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+ case BoolOpType::Union: {
+ for (int i = 0; i < nw; ++i) {
+ if (winding[i] != 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+ case BoolOpType::Difference: {
+ /* if nw > 2, make it shape 0 minus the union of the rest. */
+ if (winding[0] == 0) {
+ return false;
+ }
+ if (nw == 1) {
+ return true;
+ }
+ for (int i = 1; i < nw; ++i) {
+ if (winding[i] == 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+ default:
+ return false;
+ }
+}
+
+/**
+ * Special processing for extract_from_in_output_volume_diffs to handle
+ * triangles that are part of stacks of geometrically identical
+ * triangles enclosing zero volume cells.
+ */
+static void extract_zero_volume_cell_tris(Vector<Face *> &r_tris,
+ const IMesh &tm_subdivided,
+ const PatchesInfo &pinfo,
+ const CellsInfo &cinfo,
+ IMeshArena *arena)
+{
+ int dbg_level = 0;
+ if (dbg_level > 0) {
+ std::cout << "extract_zero_volume_cell_tris\n";
+ }
+ /* Find patches that are adjacent to zero-volume cells. */
+ Array<bool> adj_to_zv(pinfo.tot_patch());
+ for (int p : pinfo.index_range()) {
+ const Patch &patch = pinfo.patch(p);
+ if (cinfo.cell(patch.cell_above).zero_volume() || cinfo.cell(patch.cell_below).zero_volume()) {
+ adj_to_zv[p] = true;
+ }
+ else {
+ adj_to_zv[p] = false;
+ }
+ }
+ /* Partition the adj_to_zv patches into stacks. */
+ Vector<Vector<int>> patch_stacks;
+ Array<bool> allocated_to_stack(pinfo.tot_patch(), false);
+ for (int p : pinfo.index_range()) {
+ if (!adj_to_zv[p] || allocated_to_stack[p]) {
+ continue;
+ }
+ int stack_index = patch_stacks.size();
+ patch_stacks.append(Vector<int>{p});
+ Vector<int> &stack = patch_stacks[stack_index];
+ Vector<bool> flipped{false};
+ allocated_to_stack[p] = true;
+ /* We arbitrarily choose p's above and below directions as above and below for whole stack.
+ * Triangles in the stack that don't follow that convention are marked with flipped = true.
+ * The non-zero-volume cell above the whole stack, following this convention, is
+ * above_stack_cell. The non-zero-volume cell below the whole stack is #below_stack_cell. */
+ /* First, walk in the above_cell direction from p. */
+ int pwalk = p;
+ const Patch *pwalk_patch = &pinfo.patch(pwalk);
+ int c = pwalk_patch->cell_above;
+ const Cell *cell = &cinfo.cell(c);
+ while (cell->zero_volume()) {
+ /* In zero-volume cells, the cell should have exactly two patches. */
+ BLI_assert(cell->patches().size() == 2);
+ int pother = cell->patches()[0] == pwalk ? cell->patches()[1] : cell->patches()[0];
+ bool flip = pinfo.patch(pother).cell_above == c;
+ flipped.append(flip);
+ stack.append(pother);
+ allocated_to_stack[pother] = true;
+ pwalk = pother;
+ pwalk_patch = &pinfo.patch(pwalk);
+ c = flip ? pwalk_patch->cell_below : pwalk_patch->cell_above;
+ cell = &cinfo.cell(c);
+ }
+ const Cell *above_stack_cell = cell;
+ /* Now walk in the below_cell direction from p. */
+ pwalk = p;
+ pwalk_patch = &pinfo.patch(pwalk);
+ c = pwalk_patch->cell_below;
+ cell = &cinfo.cell(c);
+ while (cell->zero_volume()) {
+ BLI_assert(cell->patches().size() == 2);
+ int pother = cell->patches()[0] == pwalk ? cell->patches()[1] : cell->patches()[0];
+ bool flip = pinfo.patch(pother).cell_below == c;
+ flipped.append(flip);
+ stack.append(pother);
+ allocated_to_stack[pother] = true;
+ pwalk = pother;
+ pwalk_patch = &pinfo.patch(pwalk);
+ c = flip ? pwalk_patch->cell_above : pwalk_patch->cell_below;
+ cell = &cinfo.cell(c);
+ }
+ const Cell *below_stack_cell = cell;
+ if (dbg_level > 0) {
+ std::cout << "process zero-volume patch stack " << stack << "\n";
+ std::cout << "flipped = ";
+ for (bool b : flipped) {
+ std::cout << b << " ";
+ }
+ std::cout << "\n";
+ }
+ if (above_stack_cell->in_output_volume() ^ below_stack_cell->in_output_volume()) {
+ bool need_flipped_tri = above_stack_cell->in_output_volume();
+ if (dbg_level > 0) {
+ std::cout << "need tri " << (need_flipped_tri ? "flipped" : "") << "\n";
+ }
+ int t_to_add = NO_INDEX;
+ for (int i : stack.index_range()) {
+ if (flipped[i] == need_flipped_tri) {
+ t_to_add = pinfo.patch(stack[i]).tri(0);
+ if (dbg_level > 0) {
+ std::cout << "using tri " << t_to_add << "\n";
+ }
+ r_tris.append(tm_subdivided.face(t_to_add));
+ break;
+ }
+ }
+ if (t_to_add == NO_INDEX) {
+ const Face *f = tm_subdivided.face(pinfo.patch(p).tri(0));
+ const Face &tri = *f;
+ /* We need flipped version or else we would have found it above. */
+ std::array<const Vert *, 3> flipped_vs = {tri[0], tri[2], tri[1]};
+ std::array<int, 3> flipped_e_origs = {
+ tri.edge_orig[2], tri.edge_orig[1], tri.edge_orig[0]};
+ std::array<bool, 3> flipped_is_intersect = {
+ tri.is_intersect[2], tri.is_intersect[1], tri.is_intersect[0]};
+ Face *flipped_f = arena->add_face(
+ flipped_vs, f->orig, flipped_e_origs, flipped_is_intersect);
+ r_tris.append(flipped_f);
+ }
+ }
+ }
+}
+
+/**
+ * Extract the output mesh from tm_subdivided and return it as a new mesh.
+ * The cells in \a cinfo must have cells-to-be-retained with in_output_volume set.
+ * We keep only triangles between those in the output volume and those not in.
+ * We flip the normals of any triangle that has an in_output_volume cell above
+ * and a not-in_output_volume cell below.
+ * For all stacks of exact duplicate co-planar triangles, we want to
+ * include either one version of the triangle or none, depending on
+ * whether the in_output_volume in_output_volumes on either side of the stack are
+ * different or the same.
+ */
+static IMesh extract_from_in_output_volume_diffs(const IMesh &tm_subdivided,
+ const PatchesInfo &pinfo,
+ const CellsInfo &cinfo,
+ IMeshArena *arena)
+{
+ constexpr int dbg_level = 0;
+ if (dbg_level > 0) {
+ std::cout << "\nEXTRACT_FROM_FLAG_DIFFS\n";
+ }
+ Vector<Face *> out_tris;
+ out_tris.reserve(tm_subdivided.face_size());
+ bool any_zero_volume_cell = false;
+ for (int t : tm_subdivided.face_index_range()) {
+ int p = pinfo.tri_patch(t);
+ const Patch &patch = pinfo.patch(p);
+ const Cell &cell_above = cinfo.cell(patch.cell_above);
+ const Cell &cell_below = cinfo.cell(patch.cell_below);
+ if (dbg_level > 0) {
+ std::cout << "tri " << t << ": cell_above=" << patch.cell_above
+ << " cell_below=" << patch.cell_below << "\n";
+ std::cout << " in_output_volume_above=" << cell_above.in_output_volume()
+ << " in_output_volume_below=" << cell_below.in_output_volume() << "\n";
+ }
+ bool adjacent_zero_volume_cell = cell_above.zero_volume() || cell_below.zero_volume();
+ any_zero_volume_cell |= adjacent_zero_volume_cell;
+ if (cell_above.in_output_volume() ^ cell_below.in_output_volume() &&
+ !adjacent_zero_volume_cell) {
+ bool flip = cell_above.in_output_volume();
+ if (dbg_level > 0) {
+ std::cout << "need tri " << t << " flip=" << flip << "\n";
+ }
+ Face *f = tm_subdivided.face(t);
+ if (flip) {
+ Face &tri = *f;
+ std::array<const Vert *, 3> flipped_vs = {tri[0], tri[2], tri[1]};
+ std::array<int, 3> flipped_e_origs = {
+ tri.edge_orig[2], tri.edge_orig[1], tri.edge_orig[0]};
+ std::array<bool, 3> flipped_is_intersect = {
+ tri.is_intersect[2], tri.is_intersect[1], tri.is_intersect[0]};
+ Face *flipped_f = arena->add_face(
+ flipped_vs, f->orig, flipped_e_origs, flipped_is_intersect);
+ out_tris.append(flipped_f);
+ }
+ else {
+ out_tris.append(f);
+ }
+ }
+ }
+ if (any_zero_volume_cell) {
+ extract_zero_volume_cell_tris(out_tris, tm_subdivided, pinfo, cinfo, arena);
+ }
+ return IMesh(out_tris);
+}
+
+static const char *bool_optype_name(BoolOpType op)
+{
+ switch (op) {
+ case BoolOpType::None:
+ return "none";
+ case BoolOpType::Intersect:
+ return "intersect";
+ case BoolOpType::Union:
+ return "union";
+ case BoolOpType::Difference:
+ return "difference";
+ default:
+ return "<unknown>";
+ }
+}
+
+static mpq3 calc_point_inside_tri(const Face &tri)
+{
+ const Vert *v0 = tri.vert[0];
+ const Vert *v1 = tri.vert[1];
+ const Vert *v2 = tri.vert[2];
+ mpq3 ans = v0->co_exact / 3 + v1->co_exact / 3 + v2->co_exact / 3;
+ return ans;
+}
+
+/**
+ * Return the Generalized Winding Number of point \a testp with respect to the
+ * volume implied by the faces for which shape_fn returns the value shape.
+ * See "Robust Inside-Outside Segmentation using Generalized Winding Numbers"
+ * by Jacobson, Kavan, and Sorkine-Hornung.
+ * This is like a winding number in that if it is positive, the point
+ * is inside the volume. But it is tolerant of not-completely-watertight
+ * volumes, still doing a passable job of classifying inside/outside
+ * as we intuitively understand that to mean.
+ *
+ * TOOD: speed up this calculation using the hierarchical algorithm in that paper.
+ */
+static double generalized_winding_number(const IMesh &tm,
+ std::function<int(int)> shape_fn,
+ const double3 &testp,
+ int shape)
+{
+ constexpr int dbg_level = 0;
+ if (dbg_level > 0) {
+ std::cout << "GENERALIZED_WINDING_NUMBER testp = " << testp << ", shape = " << shape << "\n";
+ }
+ double gwn = 0;
+ for (int t : tm.face_index_range()) {
+ const Face *f = tm.face(t);
+ const Face &tri = *f;
+ if (shape_fn(tri.orig) == shape) {
+ if (dbg_level > 0) {
+ std::cout << "accumulate for tri t = " << t << " = " << f << "\n";
+ }
+ const Vert *v0 = tri.vert[0];
+ const Vert *v1 = tri.vert[1];
+ const Vert *v2 = tri.vert[2];
+ double3 a = v0->co - testp;
+ double3 b = v1->co - testp;
+ double3 c = v2->co - testp;
+ /* Calculate the solid angle of abc relative to origin.
+ * See "The Solid Angle of a Plane Triangle" by Oosterom and Strackee
+ * for the derivation of the formula. */
+ double alen = a.length();
+ double blen = b.length();
+ double clen = c.length();
+ double3 bxc = double3::cross_high_precision(b, c);
+ double num = double3::dot(a, bxc);
+ double denom = alen * blen * clen + double3::dot(a, b) * clen + double3::dot(a, c) * blen +
+ double3::dot(b, c) * alen;
+ if (denom == 0.0) {
+ if (dbg_level > 0) {
+ std::cout << "denom == 0, skipping this tri\n";
+ }
+ continue;
+ }
+ double x = atan2(num, denom);
+ double fgwn = 2.0 * x;
+ if (dbg_level > 0) {
+ std::cout << "tri contributes " << fgwn << "\n";
+ }
+ gwn += fgwn;
+ }
+ }
+ gwn = gwn / (M_PI * 4.0);
+ if (dbg_level > 0) {
+ std::cout << "final gwn = " << gwn << "\n";
+ }
+ return gwn;
+}
+
+/**
+ * Return true if point \a testp is inside the volume implied by the
+ * faces for which the shape_fn returns the value shape.
+ */
+static bool point_is_inside_shape(const IMesh &tm,
+ std::function<int(int)> shape_fn,
+ const double3 &testp,
+ int shape)
+{
+ double gwn = generalized_winding_number(tm, shape_fn, testp, shape);
+ /* Due to floating point error, an outside point should get a value
+ * of zero for gwn, but may have a very slightly positive value instead.
+ * It is not important to get this epsilon very small, because practical
+ * cases of interest will have gwn at least 0.2 if it is not zero. */
+ return (gwn > 0.01);
+}
+
+/**
+ * Use the Generalized Winding Number method for deciding if a patch of the
+ * mesh is supposed to be included or excluded in the boolean result,
+ * and return the mesh that is the boolean result.
+ */
+static IMesh gwn_boolean(const IMesh &tm,
+ BoolOpType op,
+ int nshapes,
+ std::function<int(int)> shape_fn,
+ const PatchesInfo &pinfo,
+ IMeshArena *arena)
+{
+ constexpr int dbg_level = 0;
+ if (dbg_level > 0) {
+ std::cout << "GWN_BOOLEAN\n";
+ }
+ IMesh ans;
+ Vector<Face *> out_faces;
+ out_faces.reserve(tm.face_size());
+ BLI_assert(nshapes == 2); /* TODO: generalize. */
+ UNUSED_VARS_NDEBUG(nshapes);
+ for (int p : pinfo.index_range()) {
+ const Patch &patch = pinfo.patch(p);
+ /* For test triangle, choose one in the middle of patch list
+ * as the ones near the beginning may be very near other patches. */
+ int test_t_index = patch.tri(patch.tot_tri() / 2);
+ Face &tri_test = *tm.face(test_t_index);
+ /* Assume all triangles in a patch are in the same shape. */
+ int shape = shape_fn(tri_test.orig);
+ if (dbg_level > 0) {
+ std::cout << "process patch " << p << " = " << patch << "\n";
+ std::cout << "test tri = " << test_t_index << " = " << &tri_test << "\n";
+ std::cout << "shape = " << shape << "\n";
+ }
+ if (shape == -1) {
+ continue;
+ }
+ mpq3 test_point = calc_point_inside_tri(tri_test);
+ double3 test_point_db(test_point[0].get_d(), test_point[1].get_d(), test_point[2].get_d());
+ if (dbg_level > 0) {
+ std::cout << "test point = " << test_point_db << "\n";
+ }
+ int other_shape = 1 - shape;
+ bool inside = point_is_inside_shape(tm, shape_fn, test_point_db, other_shape);
+ if (dbg_level > 0) {
+ std::cout << "test point is " << (inside ? "inside\n" : "outside\n");
+ }
+ bool do_remove;
+ bool do_flip;
+ switch (op) {
+ case BoolOpType::Intersect:
+ do_remove = !inside;
+ do_flip = false;
+ break;
+ case BoolOpType::Union:
+ do_remove = inside;
+ do_flip = false;
+ break;
+ case BoolOpType::Difference:
+ do_remove = (shape == 0) ? inside : !inside;
+ do_flip = (shape == 1);
+ break;
+ default:
+ do_remove = false;
+ do_flip = false;
+ BLI_assert(false);
+ }
+ if (dbg_level > 0) {
+ std::cout << "result for patch " << p << ": remove=" << do_remove << ", flip=" << do_flip
+ << "\n";
+ }
+ if (!do_remove) {
+ for (int t : patch.tris()) {
+ Face *f = tm.face(t);
+ if (!do_flip) {
+ out_faces.append(f);
+ }
+ else {
+ Face &tri = *f;
+ /* We need flipped version of f. */
+ Array<const Vert *> flipped_vs = {tri[0], tri[2], tri[1]};
+ Array<int> flipped_e_origs = {tri.edge_orig[2], tri.edge_orig[1], tri.edge_orig[0]};
+ Array<bool> flipped_is_intersect = {
+ tri.is_intersect[2], tri.is_intersect[1], tri.is_intersect[0]};
+ Face *flipped_f = arena->add_face(
+ flipped_vs, f->orig, flipped_e_origs, flipped_is_intersect);
+ out_faces.append(flipped_f);
+ }
+ }
+ }
+ }
+ ans.set_faces(out_faces);
+ return ans;
+}
+
+/**
+ * Which CDT output edge index is for an edge between output verts
+ * v1 and v2 (in either order)?
+ * \return -1 if none.
+ */
+static int find_cdt_edge(const CDT_result<mpq_class> &cdt_out, int v1, int v2)
+{
+ for (int e : cdt_out.edge.index_range()) {
+ const std::pair<int, int> &edge = cdt_out.edge[e];
+ if ((edge.first == v1 && edge.second == v2) || (edge.first == v2 && edge.second == v1)) {
+ return e;
+ }
+ }
+ return -1;
+}
+
+/**
+ * Tessellate face f into triangles and return an array of `const Face *`
+ * giving that triangulation.
+ * Care is taken so that the original edge index associated with
+ * each edge in the output triangles either matches the original edge
+ * for the (identical) edge of f, or else is -1. So diagonals added
+ * for triangulation can later be identified by having #NO_INDEX for original.
+ */
+static Array<Face *> triangulate_poly(Face *f, IMeshArena *arena)
+{
+ int flen = f->size();
+ CDT_input<mpq_class> cdt_in;
+ cdt_in.vert = Array<mpq2>(flen);
+ cdt_in.face = Array<Vector<int>>(1);
+ cdt_in.face[0].reserve(flen);
+ for (int i : f->index_range()) {
+ cdt_in.face[0].append(i);
+ }
+ /* Project poly along dominant axis of normal to get 2d coords. */
+ if (!f->plane_populated()) {
+ f->populate_plane(false);
+ }
+ const double3 &poly_normal = f->plane->norm;
+ int axis = double3::dominant_axis(poly_normal);
+ /* If project down y axis as opposed to x or z, the orientation
+ * of the polygon will be reversed.
+ * Yet another reversal happens if the poly normal in the dominant
+ * direction is opposite that of the positive dominant axis. */
+ bool rev1 = (axis == 1);
+ bool rev2 = poly_normal[axis] < 0;
+ bool rev = rev1 ^ rev2;
+ for (int i = 0; i < flen; ++i) {
+ int ii = rev ? flen - i - 1 : i;
+ mpq2 &p2d = cdt_in.vert[ii];
+ int k = 0;
+ for (int j = 0; j < 3; ++j) {
+ if (j != axis) {
+ p2d[k++] = (*f)[ii]->co_exact[j];
+ }
+ }
+ }
+ CDT_result<mpq_class> cdt_out = delaunay_2d_calc(cdt_in, CDT_INSIDE);
+ int n_tris = cdt_out.face.size();
+ Array<Face *> ans(n_tris);
+ for (int t = 0; t < n_tris; ++t) {
+ int i_v_out[3];
+ const Vert *v[3];
+ int eo[3];
+ for (int i = 0; i < 3; ++i) {
+ i_v_out[i] = cdt_out.face[t][i];
+ v[i] = (*f)[cdt_out.vert_orig[i_v_out[i]][0]];
+ }
+ for (int i = 0; i < 3; ++i) {
+ int e_out = find_cdt_edge(cdt_out, i_v_out[i], i_v_out[(i + 1) % 3]);
+ BLI_assert(e_out != -1);
+ eo[i] = NO_INDEX;
+ for (int orig : cdt_out.edge_orig[e_out]) {
+ if (orig != NO_INDEX) {
+ eo[i] = orig;
+ break;
+ }
+ }
+ }
+ if (rev) {
+ ans[t] = arena->add_face(
+ {v[0], v[2], v[1]}, f->orig, {eo[2], eo[1], eo[0]}, {false, false, false});
+ }
+ else {
+ ans[t] = arena->add_face(
+ {v[0], v[1], v[2]}, f->orig, {eo[0], eo[1], eo[2]}, {false, false, false});
+ }
+ }
+ return ans;
+}
+
+/**
+ * Return an #IMesh that is a triangulation of a mesh with general
+ * polygonal faces, #imesh.
+ * Added diagonals will be distinguishable by having edge original
+ * indices of #NO_INDEX.
+ */
+static IMesh triangulate_polymesh(IMesh &imesh, IMeshArena *arena)
+{
+ Vector<Face *> face_tris;
+ constexpr int estimated_tris_per_face = 3;
+ face_tris.reserve(estimated_tris_per_face * imesh.face_size());
+ for (Face *f : imesh.faces()) {
+ /* Tessellate face f, following plan similar to #BM_face_calc_tesselation. */
+ int flen = f->size();
+ if (flen == 3) {
+ face_tris.append(f);
+ }
+ else if (flen == 4) {
+ const Vert *v0 = (*f)[0];
+ const Vert *v1 = (*f)[1];
+ const Vert *v2 = (*f)[2];
+ const Vert *v3 = (*f)[3];
+ int eo_01 = f->edge_orig[0];
+ int eo_12 = f->edge_orig[1];
+ int eo_23 = f->edge_orig[2];
+ int eo_30 = f->edge_orig[3];
+ Face *f0 = arena->add_face({v0, v1, v2}, f->orig, {eo_01, eo_12, -1}, {false, false, false});
+ Face *f1 = arena->add_face({v0, v2, v3}, f->orig, {-1, eo_23, eo_30}, {false, false, false});
+ face_tris.append(f0);
+ face_tris.append(f1);
+ }
+ else {
+ Array<Face *> tris = triangulate_poly(f, arena);
+ for (Face *tri : tris) {
+ face_tris.append(tri);
+ }
+ }
+ }
+ return IMesh(face_tris);
+}
+
+/**
+ * If \a tri1 and \a tri2 have a common edge (in opposite orientation),
+ * return the indices into \a tri1 and \a tri2 where that common edge starts. Else return (-1,-1).
+ */
+static std::pair<int, int> find_tris_common_edge(const Face &tri1, const Face &tri2)
+{
+ for (int i = 0; i < 3; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ if (tri1[(i + 1) % 3] == tri2[j] && tri1[i] == tri2[(j + 1) % 3]) {
+ return std::pair<int, int>(i, j);
+ }
+ }
+ }
+ return std::pair<int, int>(-1, -1);
+}
+
+struct MergeEdge {
+ /** Length (squared) of the edge, used for sorting. */
+ double len_squared = 0.0;
+ /* v1 and v2 are the ends of the edge, ordered so that `v1->id < v2->id` */
+ const Vert *v1 = nullptr;
+ const Vert *v2 = nullptr;
+ /* left_face and right_face are indices into #FaceMergeState.face. */
+ int left_face = -1;
+ int right_face = -1;
+ int orig = -1; /* An edge orig index that can be used for this edge. */
+ /** Is it allowed to dissolve this edge? */
+ bool dissolvable = false;
+ /** Is this an intersect edge? */
+ bool is_intersect = false;
+
+ MergeEdge() = default;
+
+ MergeEdge(const Vert *va, const Vert *vb)
+ {
+ if (va->id < vb->id) {
+ this->v1 = va;
+ this->v2 = vb;
+ }
+ else {
+ this->v1 = vb;
+ this->v2 = va;
+ }
+ };
+};
+
+struct MergeFace {
+ /** The current sequence of Verts forming this face. */
+ Vector<const Vert *> vert;
+ /** For each position in face, what is index in #FaceMergeState of edge for that position? */
+ Vector<int> edge;
+ /** If not -1, merge_to gives a face index in #FaceMergeState that this is merged to. */
+ int merge_to = -1;
+ /** A face->orig that can be used for the merged face. */
+ int orig = -1;
+};
+struct FaceMergeState {
+ /**
+ * The faces being considered for merging. Some will already have been merge (merge_to != -1).
+ */
+ Vector<MergeFace> face;
+ /**
+ * The edges that are part of the faces in face[], together with current topological
+ * information (their left and right faces) and whether or not they are dissolvable.
+ */
+ Vector<MergeEdge> edge;
+ /**
+ * `edge_map` maps a pair of `const Vert *` ids (in canonical order: smaller id first)
+ * to the index in the above edge vector in which to find the corresponding #MergeEdge.
+ */
+ Map<std::pair<int, int>, int> edge_map;
+};
+
+static std::ostream &operator<<(std::ostream &os, const FaceMergeState &fms)
+{
+ os << "faces:\n";
+ for (int f : fms.face.index_range()) {
+ const MergeFace &mf = fms.face[f];
+ std::cout << f << ": orig=" << mf.orig << " verts ";
+ for (const Vert *v : mf.vert) {
+ std::cout << v << " ";
+ }
+ std::cout << "\n";
+ std::cout << " edges " << mf.edge << "\n";
+ std::cout << " merge_to = " << mf.merge_to << "\n";
+ }
+ os << "\nedges:\n";
+ for (int e : fms.edge.index_range()) {
+ const MergeEdge &me = fms.edge[e];
+ std::cout << e << ": (" << me.v1 << "," << me.v2 << ") left=" << me.left_face
+ << " right=" << me.right_face << " dis=" << me.dissolvable << " orig=" << me.orig
+ << " is_int=" << me.is_intersect << "\n";
+ }
+ return os;
+}
+
+/**
+ * \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.
+ */
+static void init_face_merge_state(FaceMergeState *fms,
+ const Vector<int> &tris,
+ const IMesh &tm,
+ const double3 &norm)
+{
+ constexpr int dbg_level = 0;
+ /* Reserve enough faces and edges so that neither will have to resize. */
+ fms->face.reserve(tris.size() + 1);
+ fms->edge.reserve(3 * tris.size());
+ fms->edge_map.reserve(3 * tris.size());
+ if (dbg_level > 0) {
+ std::cout << "\nINIT_FACE_MERGE_STATE\n";
+ }
+ for (int t : tris.index_range()) {
+ MergeFace mf;
+ const Face &tri = *tm.face(tris[t]);
+ if (dbg_level > 0) {
+ std::cout << "process tri = " << &tri << "\n";
+ }
+ BLI_assert(tri.plane_populated());
+ if (double3::dot(norm, tri.plane->norm) <= 0.0) {
+ if (dbg_level > 0) {
+ std::cout << "triangle has wrong orientation, skipping\n";
+ }
+ continue;
+ }
+ mf.vert.append(tri[0]);
+ mf.vert.append(tri[1]);
+ mf.vert.append(tri[2]);
+ mf.orig = tri.orig;
+ int f = fms->face.append_and_get_index(mf);
+ if (dbg_level > 1) {
+ std::cout << "appended MergeFace for tri at f = " << f << "\n";
+ }
+ for (int i = 0; i < 3; ++i) {
+ int inext = (i + 1) % 3;
+ MergeEdge new_me(mf.vert[i], mf.vert[inext]);
+ std::pair<int, int> canon_vs(new_me.v1->id, new_me.v2->id);
+ int me_index = fms->edge_map.lookup_default(canon_vs, -1);
+ if (dbg_level > 1) {
+ std::cout << "new_me = canon_vs = " << new_me.v1 << ", " << new_me.v2 << "\n";
+ std::cout << "me_index lookup = " << me_index << "\n";
+ }
+ if (me_index == -1) {
+ double3 vec = new_me.v2->co - new_me.v1->co;
+ new_me.len_squared = vec.length_squared();
+ new_me.orig = tri.edge_orig[i];
+ new_me.is_intersect = tri.is_intersect[i];
+ new_me.dissolvable = (new_me.orig == NO_INDEX && !new_me.is_intersect);
+ fms->edge.append(new_me);
+ me_index = fms->edge.size() - 1;
+ fms->edge_map.add_new(canon_vs, me_index);
+ if (dbg_level > 1) {
+ std::cout << "added new me with me_index = " << me_index << "\n";
+ std::cout << " len_squared = " << new_me.len_squared << " orig = " << new_me.orig
+ << ", is_intersect" << new_me.is_intersect
+ << ", dissolvable = " << new_me.dissolvable << "\n";
+ }
+ }
+ MergeEdge &me = fms->edge[me_index];
+ if (dbg_level > 1) {
+ std::cout << "retrieved me at index " << me_index << ":\n";
+ std::cout << " v1 = " << me.v1 << " v2 = " << me.v2 << "\n";
+ std::cout << " dis = " << me.dissolvable << " int = " << me.is_intersect << "\n";
+ std::cout << " left_face = " << me.left_face << " right_face = " << me.right_face << "\n";
+ }
+ if (me.dissolvable && tri.edge_orig[i] != NO_INDEX) {
+ if (dbg_level > 1) {
+ std::cout << "reassigning orig to " << tri.edge_orig[i] << ", dissolvable = false\n";
+ }
+ me.dissolvable = false;
+ me.orig = tri.edge_orig[i];
+ }
+ if (me.dissolvable && tri.is_intersect[i]) {
+ if (dbg_level > 1) {
+ std::cout << "reassigning dissolvable = false, is_intersect = true\n";
+ }
+ me.dissolvable = false;
+ me.is_intersect = true;
+ }
+ /* This face is left or right depending on orientation of edge. */
+ if (me.v1 == mf.vert[i]) {
+ if (dbg_level > 1) {
+ 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;
+ }
+ 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;
+ }
+ fms->face[f].edge.append(me_index);
+ }
+ }
+ if (dbg_level > 0) {
+ std::cout << *fms;
+ }
+}
+
+/**
+ * To have a valid #BMesh, there are constraints on what edges can be removed.
+ * We cannot remove an edge if (a) it would create two disconnected boundary parts
+ * (which will happen if there's another edge sharing the same two faces);
+ * or (b) it would create a face with a repeated vertex.
+ */
+static bool dissolve_leaves_valid_bmesh(FaceMergeState *fms,
+ const MergeEdge &me,
+ int me_index,
+ const MergeFace &mf_left,
+ const MergeFace &mf_right)
+{
+ int a_edge_start = mf_left.edge.first_index_of_try(me_index);
+ BLI_assert(a_edge_start != -1);
+ int alen = mf_left.vert.size();
+ int blen = mf_right.vert.size();
+ int b_left_face = me.right_face;
+ bool ok = true;
+ /* Is there another edge, not me, in A's face, whose right face is B's left? */
+ for (int a_e_index = (a_edge_start + 1) % alen; ok && a_e_index != a_edge_start;
+ a_e_index = (a_e_index + 1) % alen) {
+ const MergeEdge &a_me_cur = fms->edge[mf_left.edge[a_e_index]];
+ if (a_me_cur.right_face == b_left_face) {
+ ok = false;
+ }
+ }
+ /* Is there a vert in A, not me.v1 or me.v2, that is also in B?
+ * One could avoid this O(n^2) algorithm if had a structure
+ * saying which faces a vertex touches. */
+ for (int a_v_index = 0; ok && a_v_index < alen; ++a_v_index) {
+ const Vert *a_v = mf_left.vert[a_v_index];
+ if (a_v != me.v1 && a_v != me.v2) {
+ for (int b_v_index = 0; b_v_index < blen; ++b_v_index) {
+ const Vert *b_v = mf_right.vert[b_v_index];
+ if (a_v == b_v) {
+ ok = false;
+ }
+ }
+ }
+ }
+ return ok;
+}
+
+/**
+ * mf_left and mf_right should share a #MergeEdge me, having index me_index.
+ * We change mf_left to remove edge me and insert the appropriate edges of
+ * mf_right in between the start and end vertices of that edge.
+ * We change the left face of the spliced-in edges to be mf_left's index.
+ * We mark the merge_to property of mf_right, which is now in essence deleted.
+ */
+static void splice_faces(
+ FaceMergeState *fms, MergeEdge &me, int me_index, MergeFace &mf_left, MergeFace &mf_right)
+{
+ int a_edge_start = mf_left.edge.first_index_of_try(me_index);
+ int b_edge_start = mf_right.edge.first_index_of_try(me_index);
+ BLI_assert(a_edge_start != -1 && b_edge_start != -1);
+ int alen = mf_left.vert.size();
+ int blen = mf_right.vert.size();
+ Vector<const Vert *> splice_vert;
+ Vector<int> splice_edge;
+ splice_vert.reserve(alen + blen - 2);
+ splice_edge.reserve(alen + blen - 2);
+ int ai = 0;
+ while (ai < a_edge_start) {
+ splice_vert.append(mf_left.vert[ai]);
+ splice_edge.append(mf_left.edge[ai]);
+ ++ai;
+ }
+ int bi = b_edge_start + 1;
+ while (bi != b_edge_start) {
+ if (bi >= blen) {
+ bi = 0;
+ if (bi == b_edge_start) {
+ break;
+ }
+ }
+ splice_vert.append(mf_right.vert[bi]);
+ splice_edge.append(mf_right.edge[bi]);
+ if (mf_right.vert[bi] == fms->edge[mf_right.edge[bi]].v1) {
+ fms->edge[mf_right.edge[bi]].left_face = me.left_face;
+ }
+ else {
+ fms->edge[mf_right.edge[bi]].right_face = me.left_face;
+ }
+ ++bi;
+ }
+ ai = a_edge_start + 1;
+ while (ai < alen) {
+ splice_vert.append(mf_left.vert[ai]);
+ splice_edge.append(mf_left.edge[ai]);
+ ++ai;
+ }
+ mf_right.merge_to = me.left_face;
+ mf_left.vert = splice_vert;
+ mf_left.edge = splice_edge;
+ me.left_face = -1;
+ me.right_face = -1;
+}
+
+/**
+ * Given that fms has been properly initialized to contain a set of faces that
+ * together form a face or part of a face of the original #IMesh, and that
+ * it has properly recorded with faces are dissolvable, dissolve as many edges as possible.
+ * We try to dissolve in decreasing order of edge length, so that it is more likely
+ * that the final output doesn't have awkward looking long edges with extreme angles.
+ */
+static void do_dissolve(FaceMergeState *fms)
+{
+ const int dbg_level = 0;
+ if (dbg_level > 1) {
+ std::cout << "\nDO_DISSOLVE\n";
+ }
+ Vector<int> dissolve_edges;
+ for (int e : fms->edge.index_range()) {
+ if (fms->edge[e].dissolvable) {
+ dissolve_edges.append(e);
+ }
+ }
+ if (dissolve_edges.size() == 0) {
+ return;
+ }
+ /* Things look nicer if we dissolve the longer edges first. */
+ std::sort(
+ dissolve_edges.begin(), dissolve_edges.end(), [fms](const int &a, const int &b) -> bool {
+ return (fms->edge[a].len_squared > fms->edge[b].len_squared);
+ });
+ if (dbg_level > 0) {
+ std::cout << "Sorted dissolvable edges: " << dissolve_edges << "\n";
+ }
+ for (int me_index : dissolve_edges) {
+ MergeEdge &me = fms->edge[me_index];
+ if (me.left_face == -1 || me.right_face == -1) {
+ continue;
+ }
+ MergeFace &mf_left = fms->face[me.left_face];
+ MergeFace &mf_right = fms->face[me.right_face];
+ if (!dissolve_leaves_valid_bmesh(fms, me, me_index, mf_left, mf_right)) {
+ continue;
+ }
+ if (dbg_level > 0) {
+ std::cout << "Removing edge " << me_index << "\n";
+ }
+ splice_faces(fms, me, me_index, mf_left, mf_right);
+ if (dbg_level > 1) {
+ std::cout << "state after removal:\n";
+ std::cout << *fms;
+ }
+ }
+}
+
+/**
+ * Given that \a tris form a triangulation of a face or part of a face that was in \a imesh_in,
+ * merge as many of the triangles together as possible, by dissolving the edges between them.
+ * We can only dissolve triangulation edges that don't overlap real input edges, and we
+ * can only dissolve them if doing so leaves the remaining faces able to create valid #BMesh.
+ * We can tell edges that don't overlap real input edges because they will have an
+ * "original edge" that is different from #NO_INDEX.
+ * \note it is possible that some of the triangles in \a tris have reversed orientation
+ * to the rest, so we have to handle the two cases separately.
+ */
+static Vector<Face *> merge_tris_for_face(Vector<int> tris,
+ const IMesh &tm,
+ const IMesh &imesh_in,
+ IMeshArena *arena)
+{
+ constexpr int dbg_level = 0;
+ if (dbg_level > 0) {
+ std::cout << "merge_tris_for_face\n";
+ std::cout << "tris: " << tris << "\n";
+ }
+ Vector<Face *> ans;
+ if (tris.size() <= 1) {
+ if (tris.size() == 1) {
+ ans.append(tm.face(tris[0]));
+ }
+ return ans;
+ }
+ bool done = false;
+ double3 first_tri_normal = tm.face(tris[0])->plane->norm;
+ double3 second_tri_normal = tm.face(tris[1])->plane->norm;
+ if (tris.size() == 2 && double3::dot(first_tri_normal, second_tri_normal) > 0.0) {
+ /* Is this a case where quad with one diagonal remained unchanged?
+ * Worth special handling because this case will be very common. */
+ Face &tri1 = *tm.face(tris[0]);
+ Face &tri2 = *tm.face(tris[1]);
+ Face *in_face = imesh_in.face(tri1.orig);
+ if (in_face->size() == 4) {
+ std::pair<int, int> estarts = find_tris_common_edge(tri1, tri2);
+ if (estarts.first != -1 && tri1.edge_orig[estarts.first] == NO_INDEX) {
+ if (dbg_level > 0) {
+ std::cout << "try recovering orig quad case\n";
+ std::cout << "tri1 = " << &tri1 << "\n";
+ std::cout << "tri1 = " << &tri2 << "\n";
+ }
+ int i0 = estarts.first;
+ int i1 = (i0 + 1) % 3;
+ int i2 = (i0 + 2) % 3;
+ int j2 = (estarts.second + 2) % 3;
+ Face tryface({tri1[i1], tri1[i2], tri1[i0], tri2[j2]}, -1, -1, {}, {});
+ if (tryface.cyclic_equal(*in_face)) {
+ if (dbg_level > 0) {
+ std::cout << "inface = " << in_face << "\n";
+ std::cout << "quad recovery worked\n";
+ }
+ ans.append(in_face);
+ done = true;
+ }
+ }
+ }
+ }
+ if (done) {
+ return ans;
+ }
+
+ double3 first_tri_normal_rev = -first_tri_normal;
+ for (const double3 &norm : {first_tri_normal, first_tri_normal_rev}) {
+ FaceMergeState fms;
+ init_face_merge_state(&fms, tris, tm, norm);
+ do_dissolve(&fms);
+ if (dbg_level > 0) {
+ std::cout << "faces in merged result:\n";
+ }
+ for (const MergeFace &mf : fms.face) {
+ if (mf.merge_to == -1) {
+ Array<int> e_orig(mf.edge.size());
+ Array<bool> is_intersect(mf.edge.size());
+ for (int i : mf.edge.index_range()) {
+ e_orig[i] = fms.edge[mf.edge[i]].orig;
+ is_intersect[i] = fms.edge[mf.edge[i]].is_intersect;
+ }
+ Face *facep = arena->add_face(mf.vert, mf.orig, e_orig, is_intersect);
+ ans.append(facep);
+ if (dbg_level > 0) {
+ std::cout << " " << facep << "\n";
+ }
+ }
+ }
+ }
+ return ans;
+}
+
+/**
+ * Return an array, paralleling imesh_out.vert, saying which vertices can be dissolved.
+ * A vertex v can be dissolved if (a) it is not an input vertex; (b) it has valence 2;
+ * and (c) if v's two neighboring vertices are u and w, then (u,v,w) forms a straight line.
+ * Return the number of dissolvable vertices in r_count_dissolve.
+ */
+static Array<bool> find_dissolve_verts(IMesh &imesh_out, int *r_count_dissolve)
+{
+ imesh_out.populate_vert();
+ /* dissolve[i] will say whether imesh_out.vert(i) can be dissolved. */
+ Array<bool> dissolve(imesh_out.vert_size());
+ for (int v_index : imesh_out.vert_index_range()) {
+ const Vert &vert = *imesh_out.vert(v_index);
+ dissolve[v_index] = (vert.orig == NO_INDEX);
+ }
+ /* neighbors[i] will be a pair giving the up-to-two neighboring vertices
+ * of the vertex v in position i of imesh_out.vert.
+ * If we encounter a third, then v will not be dissolvable. */
+ Array<std::pair<const Vert *, const Vert *>> neighbors(
+ imesh_out.vert_size(), std::pair<const Vert *, const Vert *>(nullptr, nullptr));
+ for (int f : imesh_out.face_index_range()) {
+ const Face &face = *imesh_out.face(f);
+ for (int i : face.index_range()) {
+ const Vert *v = face[i];
+ int v_index = imesh_out.lookup_vert(v);
+ BLI_assert(v_index != NO_INDEX);
+ if (dissolve[v_index]) {
+ const Vert *n1 = face[face.next_pos(i)];
+ const Vert *n2 = face[face.prev_pos(i)];
+ const Vert *f_n1 = neighbors[v_index].first;
+ const Vert *f_n2 = neighbors[v_index].second;
+ if (f_n1 != nullptr) {
+ /* Already has a neighbor in another face; can't dissolve unless they are the same. */
+ if (!((n1 == f_n2 && n2 == f_n1) || (n1 == f_n1 && n2 == f_n2))) {
+ /* Different neighbors, so can't dissolve. */
+ dissolve[v_index] = false;
+ }
+ }
+ else {
+ /* These are the first-seen neighbors. */
+ neighbors[v_index] = std::pair<const Vert *, const Vert *>(n1, n2);
+ }
+ }
+ }
+ }
+ int count = 0;
+ for (int v_out : imesh_out.vert_index_range()) {
+ if (dissolve[v_out]) {
+ dissolve[v_out] = false; /* Will set back to true if final condition is satisfied. */
+ const std::pair<const Vert *, const Vert *> &nbrs = neighbors[v_out];
+ if (nbrs.first != nullptr) {
+ BLI_assert(nbrs.second != nullptr);
+ const mpq3 &co1 = nbrs.first->co_exact;
+ const mpq3 &co2 = nbrs.second->co_exact;
+ const mpq3 &co = imesh_out.vert(v_out)->co_exact;
+ mpq3 dir1 = co - co1;
+ mpq3 dir2 = co2 - co;
+ mpq3 cross = mpq3::cross(dir1, dir2);
+ if (cross[0] == 0 && cross[1] == 0 && cross[2] == 0) {
+ dissolve[v_out] = true;
+ ++count;
+ }
+ }
+ }
+ }
+ if (r_count_dissolve != nullptr) {
+ *r_count_dissolve = count;
+ }
+ return dissolve;
+}
+
+/**
+ * The dissolve array parallels the `imesh.vert` array. Wherever it is true,
+ * remove the corresponding vertex from the vertices in the faces of
+ * `imesh.faces` to account for the close-up of the gaps in `imesh.vert`.
+ */
+static void dissolve_verts(IMesh *imesh, const Array<bool> dissolve, IMeshArena *arena)
+{
+ constexpr int inline_face_size = 100;
+ Vector<bool, inline_face_size> face_pos_erase;
+ for (int f : imesh->face_index_range()) {
+ const Face &face = *imesh->face(f);
+ face_pos_erase.clear();
+ int num_erase = 0;
+ for (const Vert *v : face) {
+ int v_index = imesh->lookup_vert(v);
+ BLI_assert(v_index != NO_INDEX);
+ if (dissolve[v_index]) {
+ face_pos_erase.append(true);
+ ++num_erase;
+ }
+ else {
+ face_pos_erase.append(false);
+ }
+ }
+ if (num_erase > 0) {
+ imesh->erase_face_positions(f, face_pos_erase, arena);
+ }
+ }
+ imesh->set_dirty_verts();
+}
+
+/**
+ * The main boolean function operates on a triangle #IMesh and produces a
+ * Triangle #IMesh as output.
+ * This function converts back into a general polygonal mesh by removing
+ * any possible triangulation edges (which can be identified because they
+ * will have an original edge that is NO_INDEX.
+ * Not all triangulation edges can be removed: if they ended up non-trivially overlapping a real
+ * input edge, then we need to keep it. Also, some are necessary to make the output satisfy
+ * the "valid #BMesh" property: we can't produce output faces that have repeated vertices in them,
+ * or have several disconnected boundaries (e.g., faces with holes).
+ */
+static IMesh polymesh_from_trimesh_with_dissolve(const IMesh &tm_out,
+ const IMesh &imesh_in,
+ IMeshArena *arena)
+{
+ const int dbg_level = 0;
+ if (dbg_level > 1) {
+ std::cout << "\nPOLYMESH_FROM_TRIMESH_WITH_DISSOLVE\n";
+ }
+ /* For now: need plane normals for all triangles. */
+ for (Face *tri : tm_out.faces()) {
+ tri->populate_plane(false);
+ }
+ /* Gather all output triangles that are part of each input face.
+ * face_output_tris[f] will be indices of triangles in tm_out
+ * that have f as their original face. */
+ int tot_in_face = imesh_in.face_size();
+ Array<Vector<int>> face_output_tris(tot_in_face);
+ for (int t : tm_out.face_index_range()) {
+ const Face &tri = *tm_out.face(t);
+ int in_face = tri.orig;
+ face_output_tris[in_face].append(t);
+ }
+ if (dbg_level > 1) {
+ std::cout << "face_output_tris:\n";
+ for (int f : face_output_tris.index_range()) {
+ std::cout << f << ": " << face_output_tris[f] << "\n";
+ }
+ }
+
+ /* Merge triangles that we can from face_output_tri to make faces for output.
+ * face_output_face[f] will be new original const Face *'s that
+ * make up whatever part of the boolean output remains of input face f. */
+ Array<Vector<Face *>> face_output_face(tot_in_face);
+ int tot_out_face = 0;
+ for (int in_f : imesh_in.face_index_range()) {
+ if (dbg_level > 1) {
+ std::cout << "merge tris for face " << in_f << "\n";
+ }
+ int num_out_tris_for_face = face_output_tris.size();
+ if (num_out_tris_for_face == 0) {
+ continue;
+ }
+ face_output_face[in_f] = merge_tris_for_face(face_output_tris[in_f], tm_out, imesh_in, arena);
+ tot_out_face += face_output_face[in_f].size();
+ }
+ Array<Face *> face(tot_out_face);
+ int out_f_index = 0;
+ for (int in_f : imesh_in.face_index_range()) {
+ const Vector<Face *> &f_faces = face_output_face[in_f];
+ if (f_faces.size() > 0) {
+ std::copy(f_faces.begin(), f_faces.end(), &face[out_f_index]);
+ out_f_index += f_faces.size();
+ }
+ }
+ IMesh imesh_out(face);
+ /* Dissolve vertices that were (a) not original; and (b) now have valence 2 and
+ * are between two other vertices that are exactly in line with them.
+ * These were created because of triangulation edges that have been dissolved. */
+ int count_dissolve;
+ Array<bool> v_dissolve = find_dissolve_verts(imesh_out, &count_dissolve);
+ if (count_dissolve > 0) {
+ dissolve_verts(&imesh_out, v_dissolve, arena);
+ }
+ if (dbg_level > 1) {
+ write_obj_mesh(imesh_out, "boolean_post_dissolve");
+ }
+
+ return imesh_out;
+}
+
+/**
+ * This function does a boolean operation on a TriMesh with nshapes inputs.
+ * All the shapes are combined in tm_in.
+ * The shape_fn function should take a triangle index in tm_in and return
+ * a number in the range 0 to `nshapes-1`, to say which shape that triangle is in.
+ */
+IMesh boolean_trimesh(IMesh &tm_in,
+ BoolOpType op,
+ int nshapes,
+ std::function<int(int)> shape_fn,
+ bool use_self,
+ IMeshArena *arena)
+{
+ constexpr int dbg_level = 0;
+ if (dbg_level > 0) {
+ std::cout << "BOOLEAN of " << nshapes << " operand" << (nshapes == 1 ? "" : "s")
+ << " op=" << bool_optype_name(op) << "\n";
+ if (dbg_level > 1) {
+ tm_in.populate_vert();
+ std::cout << "boolean_trimesh input:\n" << tm_in;
+ write_obj_mesh(tm_in, "boolean_in");
+ }
+ }
+ if (tm_in.face_size() == 0) {
+ return IMesh(tm_in);
+ }
+ IMesh tm_si;
+ if (use_self) {
+ tm_si = trimesh_self_intersect(tm_in, arena);
+ }
+ else {
+ tm_si = trimesh_nary_intersect(tm_in, nshapes, shape_fn, use_self, arena);
+ }
+ if (dbg_level > 1) {
+ write_obj_mesh(tm_si, "boolean_tm_si");
+ std::cout << "\nboolean_tm_input after intersection:\n" << tm_si;
+ }
+ /* It is possible for tm_si to be empty if all the input triangles are bogus/degenerate. */
+ if (tm_si.face_size() == 0 || op == BoolOpType::None) {
+ return tm_si;
+ }
+ auto si_shape_fn = [shape_fn, tm_si](int t) { return shape_fn(tm_si.face(t)->orig); };
+ TriMeshTopology tm_si_topo(tm_si);
+ PatchesInfo pinfo = find_patches(tm_si, tm_si_topo);
+ IMesh tm_out;
+ if (!is_pwn(tm_si, tm_si_topo)) {
+ if (dbg_level > 0) {
+ std::cout << "Input is not PWN, using gwn method\n";
+ }
+ tm_out = gwn_boolean(tm_si, op, nshapes, shape_fn, pinfo, arena);
+ }
+ else {
+ CellsInfo cinfo = find_cells(tm_si, tm_si_topo, pinfo);
+ if (dbg_level > 0) {
+ std::cout << "Input is PWN\n";
+ }
+ finish_patch_cell_graph(tm_si, cinfo, pinfo, tm_si_topo, arena);
+ bool pc_ok = patch_cell_graph_ok(cinfo, pinfo);
+ if (!pc_ok) {
+ /* TODO: if bad input can lead to this, diagnose the problem. */
+ std::cout << "Something funny about input or a bug in boolean\n";
+ return IMesh(tm_in);
+ }
+ cinfo.init_windings(nshapes);
+ int c_ambient = find_ambient_cell(tm_si, nullptr, tm_si_topo, pinfo, arena);
+ if (c_ambient == NO_INDEX) {
+ /* TODO: find a way to propagate this error to user properly. */
+ std::cout << "Could not find an ambient cell; input not valid?\n";
+ return IMesh(tm_si);
+ }
+ propagate_windings_and_in_output_volume(pinfo, cinfo, c_ambient, op, nshapes, si_shape_fn);
+ tm_out = extract_from_in_output_volume_diffs(tm_si, pinfo, cinfo, arena);
+ if (dbg_level > 0) {
+ /* Check if output is PWN. */
+ TriMeshTopology tm_out_topo(tm_out);
+ if (!is_pwn(tm_out, tm_out_topo)) {
+ std::cout << "OUTPUT IS NOT PWN!\n";
+ }
+ }
+ }
+ if (dbg_level > 1) {
+ write_obj_mesh(tm_out, "boolean_tm_output");
+ std::cout << "boolean tm output:\n" << tm_out;
+ }
+ return tm_out;
+}
+
+static void dump_test_spec(IMesh &imesh)
+{
+ std::cout << "test spec = " << imesh.vert_size() << " " << imesh.face_size() << "\n";
+ for (const Vert *v : imesh.vertices()) {
+ std::cout << v->co_exact[0] << " " << v->co_exact[1] << " " << v->co_exact[2] << " # "
+ << v->co[0] << " " << v->co[1] << " " << v->co[2] << "\n";
+ }
+ for (const Face *f : imesh.faces()) {
+ for (const Vert *fv : *f) {
+ std::cout << imesh.lookup_vert(fv) << " ";
+ }
+ std::cout << "\n";
+ }
+}
+
+/**
+ * Do the boolean operation op on the polygon mesh imesh_in.
+ * See the header file for a complete description.
+ */
+IMesh boolean_mesh(IMesh &imesh,
+ BoolOpType op,
+ int nshapes,
+ std::function<int(int)> shape_fn,
+ bool use_self,
+ IMesh *imesh_triangulated,
+ IMeshArena *arena)
+{
+ constexpr int dbg_level = 0;
+ if (dbg_level > 0) {
+ std::cout << "\nBOOLEAN_MESH\n"
+ << nshapes << " operand" << (nshapes == 1 ? "" : "s")
+ << " op=" << bool_optype_name(op) << "\n";
+ if (dbg_level > 1) {
+ write_obj_mesh(imesh, "boolean_mesh_in");
+ std::cout << imesh;
+ if (dbg_level > 2) {
+ dump_test_spec(imesh);
+ }
+ }
+ }
+ IMesh *tm_in = imesh_triangulated;
+ IMesh our_triangulation;
+ if (tm_in == nullptr) {
+ our_triangulation = triangulate_polymesh(imesh, arena);
+ tm_in = &our_triangulation;
+ }
+ if (dbg_level > 1) {
+ write_obj_mesh(*tm_in, "boolean_tm_in");
+ }
+ IMesh tm_out = boolean_trimesh(*tm_in, op, nshapes, shape_fn, use_self, arena);
+ if (dbg_level > 1) {
+ std::cout << "bool_trimesh_output:\n" << tm_out;
+ write_obj_mesh(tm_out, "bool_trimesh_output");
+ }
+ IMesh ans = polymesh_from_trimesh_with_dissolve(tm_out, imesh, arena);
+ if (dbg_level > 0) {
+ std::cout << "boolean_mesh output:\n" << ans;
+ }
+ return ans;
+}
+
+} // namespace blender::meshintersect
+
+#endif // WITH_GMP
diff --git a/source/blender/blenlib/intern/mesh_intersect.cc b/source/blender/blenlib/intern/mesh_intersect.cc
new file mode 100644
index 00000000000..d973775a797
--- /dev/null
+++ b/source/blender/blenlib/intern/mesh_intersect.cc
@@ -0,0 +1,3304 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup bli
+ */
+
+/* The #blender::meshintersect API needs GMP. */
+#ifdef WITH_GMP
+
+# include <algorithm>
+# include <fstream>
+# include <iostream>
+
+# include "BLI_allocator.hh"
+# include "BLI_array.hh"
+# include "BLI_assert.h"
+# include "BLI_delaunay_2d.h"
+# include "BLI_double3.hh"
+# include "BLI_float3.hh"
+# include "BLI_hash.hh"
+# include "BLI_kdopbvh.h"
+# include "BLI_map.hh"
+# include "BLI_math_boolean.hh"
+# include "BLI_math_mpq.hh"
+# include "BLI_mpq2.hh"
+# include "BLI_mpq3.hh"
+# include "BLI_span.hh"
+# include "BLI_task.h"
+# include "BLI_threads.h"
+# include "BLI_vector.hh"
+# include "BLI_vector_set.hh"
+
+# include "PIL_time.h"
+
+# include "BLI_mesh_intersect.hh"
+
+// # define PERFDEBUG
+
+namespace blender::meshintersect {
+
+# ifdef PERFDEBUG
+static void perfdata_init(void);
+static void incperfcount(int countnum);
+static void bumpperfcount(int countnum, int amt);
+static void doperfmax(int maxnum, int val);
+static void dump_perfdata(void);
+# endif
+
+/** For debugging, can disable threading in intersect code with this static constant. */
+static constexpr bool intersect_use_threading = true;
+
+Vert::Vert(const mpq3 &mco, const double3 &dco, int id, int orig)
+ : co_exact(mco), co(dco), id(id), orig(orig)
+{
+}
+
+bool Vert::operator==(const Vert &other) const
+{
+ return this->co_exact == other.co_exact;
+}
+
+uint64_t Vert::hash() const
+{
+ return co_exact.hash();
+}
+
+std::ostream &operator<<(std::ostream &os, const Vert *v)
+{
+ os << "v" << v->id;
+ if (v->orig != NO_INDEX) {
+ os << "o" << v->orig;
+ }
+ os << v->co;
+ return os;
+}
+
+bool Plane::operator==(const Plane &other) const
+{
+ return norm_exact == other.norm_exact && d_exact == other.d_exact;
+}
+
+void Plane::make_canonical()
+{
+ if (norm_exact[0] != 0) {
+ mpq_class den = norm_exact[0];
+ norm_exact = mpq3(1, norm_exact[1] / den, norm_exact[2] / den);
+ d_exact = d_exact / den;
+ }
+ else if (norm_exact[1] != 0) {
+ mpq_class den = norm_exact[1];
+ norm_exact = mpq3(0, 1, norm_exact[2] / den);
+ d_exact = d_exact / den;
+ }
+ else {
+ if (norm_exact[2] != 0) {
+ mpq_class den = norm_exact[2];
+ norm_exact = mpq3(0, 0, 1);
+ d_exact = d_exact / den;
+ }
+ else {
+ /* A degenerate plane. */
+ d_exact = 0;
+ }
+ }
+ norm = double3(norm_exact[0].get_d(), norm_exact[1].get_d(), norm_exact[2].get_d());
+ d = d_exact.get_d();
+}
+
+Plane::Plane(const mpq3 &norm_exact, const mpq_class &d_exact)
+ : norm_exact(norm_exact), d_exact(d_exact)
+{
+ norm = double3(norm_exact[0].get_d(), norm_exact[1].get_d(), norm_exact[2].get_d());
+ d = d_exact.get_d();
+}
+
+Plane::Plane(const double3 &norm, const double d) : norm(norm), d(d)
+{
+ norm_exact = mpq3(0, 0, 0); /* Marks as "exact not yet populated". */
+}
+
+/** This is wrong for degenerate planes, but we don't expect to call it on those. */
+bool Plane::exact_populated() const
+{
+ return norm_exact[0] != 0 || norm_exact[1] != 0 || norm_exact[2] != 0;
+}
+
+uint64_t Plane::hash() const
+{
+ constexpr uint64_t h1 = 33;
+ constexpr uint64_t h2 = 37;
+ constexpr uint64_t h3 = 39;
+ uint64_t hashx = hash_mpq_class(this->norm_exact.x);
+ uint64_t hashy = hash_mpq_class(this->norm_exact.y);
+ uint64_t hashz = hash_mpq_class(this->norm_exact.z);
+ uint64_t hashd = hash_mpq_class(this->d_exact);
+ uint64_t ans = hashx ^ (hashy * h1) ^ (hashz * h1 * h2) ^ (hashd * h1 * h2 * h3);
+ return ans;
+}
+
+std::ostream &operator<<(std::ostream &os, const Plane *plane)
+{
+ os << "[" << plane->norm << ";" << plane->d << "]";
+ return os;
+}
+
+Face::Face(
+ Span<const Vert *> verts, int id, int orig, Span<int> edge_origs, Span<bool> is_intersect)
+ : vert(verts), edge_orig(edge_origs), is_intersect(is_intersect), id(id), orig(orig)
+{
+}
+
+Face::Face(Span<const Vert *> verts, int id, int orig) : vert(verts), id(id), orig(orig)
+{
+}
+
+void Face::populate_plane(bool need_exact)
+{
+ if (plane != nullptr) {
+ if (!need_exact || plane->exact_populated()) {
+ return;
+ }
+ }
+ if (need_exact) {
+ mpq3 normal_exact;
+ if (vert.size() > 3) {
+ Array<mpq3> co(vert.size());
+ for (int i : index_range()) {
+ co[i] = vert[i]->co_exact;
+ }
+ normal_exact = mpq3::cross_poly(co);
+ }
+ else {
+ mpq3 tr02 = vert[0]->co_exact - vert[2]->co_exact;
+ mpq3 tr12 = vert[1]->co_exact - vert[2]->co_exact;
+ normal_exact = mpq3::cross(tr02, tr12);
+ }
+ mpq_class d_exact = -mpq3::dot(normal_exact, vert[0]->co_exact);
+ plane = new Plane(normal_exact, d_exact);
+ }
+ else {
+ double3 normal;
+ if (vert.size() > 3) {
+ Array<double3> co(vert.size());
+ for (int i : index_range()) {
+ co[i] = vert[i]->co;
+ }
+ normal = double3::cross_poly(co);
+ }
+ else {
+ double3 tr02 = vert[0]->co - vert[2]->co;
+ double3 tr12 = vert[1]->co - vert[2]->co;
+ normal = double3::cross_high_precision(tr02, tr12);
+ }
+ double d = -double3::dot(normal, vert[0]->co);
+ plane = new Plane(normal, d);
+ }
+}
+
+Face::~Face()
+{
+ if (plane != nullptr) {
+ delete plane;
+ }
+}
+
+bool Face::operator==(const Face &other) const
+{
+ if (this->size() != other.size()) {
+ return false;
+ }
+ for (FacePos i : index_range()) {
+ /* Can test pointer equality since we will have
+ * unique vert pointers for unique co_equal's. */
+ if (this->vert[i] != other.vert[i]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool Face::cyclic_equal(const Face &other) const
+{
+ if (this->size() != other.size()) {
+ return false;
+ }
+ int flen = this->size();
+ for (FacePos start : index_range()) {
+ for (FacePos start_other : index_range()) {
+ bool ok = true;
+ for (int i = 0; ok && i < flen; ++i) {
+ FacePos p = (start + i) % flen;
+ FacePos p_other = (start_other + i) % flen;
+ if (this->vert[p] != other.vert[p_other]) {
+ ok = false;
+ }
+ }
+ if (ok) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+std::ostream &operator<<(std::ostream &os, const Face *f)
+{
+ os << "f" << f->id << "o" << f->orig << "[";
+ for (const Vert *v : *f) {
+ os << "v" << v->id;
+ if (v->orig != NO_INDEX) {
+ os << "o" << v->orig;
+ }
+ if (v != f->vert[f->size() - 1]) {
+ os << " ";
+ }
+ }
+ os << "]";
+ if (f->orig != NO_INDEX) {
+ os << "o" << f->orig;
+ }
+ os << " e_orig[";
+ for (int i : f->index_range()) {
+ os << f->edge_orig[i];
+ if (f->is_intersect[i]) {
+ os << "#";
+ }
+ if (i != f->size() - 1) {
+ os << " ";
+ }
+ }
+ os << "]";
+ return os;
+}
+
+/**
+ * Un-comment the following to try using a spin-lock instead of
+ * a mutex in the arena allocation routines.
+ * Initial tests showed that it doesn't seem to help very much,
+ * if at all, to use a spin-lock.
+ */
+// #define USE_SPINLOCK
+
+/**
+ * #IMeshArena is the owner of the Vert and Face resources used
+ * during a run of one of the mesh-intersect main functions.
+ * It also keeps has a hash table of all Verts created so that it can
+ * ensure that only one instance of a Vert with a given co_exact will
+ * exist. I.e., it de-duplicates the vertices.
+ */
+class IMeshArena::IMeshArenaImpl : NonCopyable, NonMovable {
+
+ /**
+ * Don't use Vert itself as key since resizing may move
+ * pointers to the Vert around, and we need to have those pointers
+ * stay the same throughout the lifetime of the #IMeshArena.
+ */
+ struct VSetKey {
+ Vert *vert;
+
+ VSetKey(Vert *p) : vert(p)
+ {
+ }
+
+ uint32_t hash() const
+ {
+ return vert->hash();
+ }
+
+ bool operator==(const VSetKey &other) const
+ {
+ return *this->vert == *other.vert;
+ }
+ };
+
+ VectorSet<VSetKey> vset_; /* TODO: replace with Set */
+
+ /**
+ * Ownership of the Vert memory is here, so destroying this reclaims that memory.
+ *
+ * TODO: replace these with pooled allocation, and just destroy the pools at the end.
+ */
+ Vector<std::unique_ptr<Vert>> allocated_verts_;
+ Vector<std::unique_ptr<Face>> allocated_faces_;
+
+ /* Use these to allocate ids when Verts and Faces are allocated. */
+ int next_vert_id_ = 0;
+ int next_face_id_ = 0;
+
+ /* Need a lock when multi-threading to protect allocation of new elements. */
+# ifdef USE_SPINLOCK
+ SpinLock lock_;
+# else
+ ThreadMutex *mutex_;
+# endif
+
+ public:
+ IMeshArenaImpl()
+ {
+ if (intersect_use_threading) {
+# ifdef USE_SPINLOCK
+ BLI_spin_init(&lock_);
+# else
+ mutex_ = BLI_mutex_alloc();
+# endif
+ }
+ }
+ ~IMeshArenaImpl()
+ {
+ if (intersect_use_threading) {
+# ifdef USE_SPINLOCK
+ BLI_spin_end(&lock_);
+# else
+ BLI_mutex_free(mutex_);
+# endif
+ }
+ }
+
+ void reserve(int vert_num_hint, int face_num_hint)
+ {
+ vset_.reserve(vert_num_hint);
+ allocated_verts_.reserve(vert_num_hint);
+ allocated_faces_.reserve(face_num_hint);
+ }
+
+ int tot_allocated_verts() const
+ {
+ return allocated_verts_.size();
+ }
+
+ int tot_allocated_faces() const
+ {
+ return allocated_faces_.size();
+ }
+
+ const Vert *add_or_find_vert(const mpq3 &co, int orig)
+ {
+ double3 dco(co[0].get_d(), co[1].get_d(), co[2].get_d());
+ return add_or_find_vert(co, dco, orig);
+ }
+
+ const Vert *add_or_find_vert(const double3 &co, int orig)
+ {
+ mpq3 mco(co[0], co[1], co[2]);
+ return add_or_find_vert(mco, co, orig);
+ }
+
+ Face *add_face(Span<const Vert *> verts, int orig, Span<int> edge_origs, Span<bool> is_intersect)
+ {
+ Face *f = new Face(verts, next_face_id_++, orig, edge_origs, is_intersect);
+ if (intersect_use_threading) {
+# ifdef USE_SPINLOCK
+ BLI_spin_lock(&lock_);
+# else
+ BLI_mutex_lock(mutex_);
+# endif
+ }
+ allocated_faces_.append(std::unique_ptr<Face>(f));
+ if (intersect_use_threading) {
+# ifdef USE_SPINLOCK
+ BLI_spin_unlock(&lock_);
+# else
+ BLI_mutex_unlock(mutex_);
+# endif
+ }
+ return f;
+ }
+
+ Face *add_face(Span<const Vert *> verts, int orig, Span<int> edge_origs)
+ {
+ Array<bool> is_intersect(verts.size(), false);
+ return add_face(verts, orig, edge_origs, is_intersect);
+ }
+
+ Face *add_face(Span<const Vert *> verts, int orig)
+ {
+ Array<int> edge_origs(verts.size(), NO_INDEX);
+ Array<bool> is_intersect(verts.size(), false);
+ return add_face(verts, orig, edge_origs, is_intersect);
+ }
+
+ const Vert *find_vert(const mpq3 &co)
+ {
+ const Vert *ans;
+ Vert vtry(co, double3(), NO_INDEX, NO_INDEX);
+ VSetKey vskey(&vtry);
+ if (intersect_use_threading) {
+# ifdef USE_SPINLOCK
+ BLI_spin_lock(&lock_);
+# else
+ BLI_mutex_lock(mutex_);
+# endif
+ }
+ int i = vset_.index_of_try(vskey);
+ if (i == -1) {
+ ans = nullptr;
+ }
+ else {
+ ans = vset_[i].vert;
+ }
+ if (intersect_use_threading) {
+# ifdef USE_SPINLOCK
+ BLI_spin_unlock(&lock_);
+# else
+ BLI_mutex_unlock(mutex_);
+# endif
+ }
+ return ans;
+ }
+
+ /**
+ * This is slow. Only used for unit tests right now.
+ * Since it is only used for that purpose, access is not lock-protected.
+ * The argument vs can be a cyclic shift of the actual stored Face.
+ */
+ const Face *find_face(Span<const Vert *> vs)
+ {
+ Array<int> eorig(vs.size(), NO_INDEX);
+ Array<bool> is_intersect(vs.size(), false);
+ Face ftry(vs, NO_INDEX, NO_INDEX, eorig, is_intersect);
+ for (const int i : allocated_faces_.index_range()) {
+ if (ftry.cyclic_equal(*allocated_faces_[i])) {
+ return allocated_faces_[i].get();
+ }
+ }
+ return nullptr;
+ }
+
+ private:
+ const Vert *add_or_find_vert(const mpq3 &mco, const double3 &dco, int orig)
+ {
+ /* Don't allocate Vert yet, in case it is already there. */
+ Vert vtry(mco, dco, NO_INDEX, NO_INDEX);
+ const Vert *ans;
+ VSetKey vskey(&vtry);
+ if (intersect_use_threading) {
+# ifdef USE_SPINLOCK
+ BLI_spin_lock(&lock_);
+# else
+ BLI_mutex_lock(mutex_);
+# endif
+ }
+ int i = vset_.index_of_try(vskey);
+ if (i == -1) {
+ vskey.vert = new Vert(mco, dco, next_vert_id_++, orig);
+ vset_.add_new(vskey);
+ allocated_verts_.append(std::unique_ptr<Vert>(vskey.vert));
+ ans = vskey.vert;
+ }
+ else {
+ /* It was a duplicate, so return the existing one.
+ * Note that the returned Vert may have a different orig.
+ * This is the intended semantics: if the Vert already
+ * exists then we are merging verts and using the first-seen
+ * one as the canonical one. */
+ ans = vset_[i].vert;
+ }
+ if (intersect_use_threading) {
+# ifdef USE_SPINLOCK
+ BLI_spin_unlock(&lock_);
+# else
+ BLI_mutex_unlock(mutex_);
+# endif
+ }
+ return ans;
+ };
+};
+
+IMeshArena::IMeshArena()
+{
+ pimpl_ = std::unique_ptr<IMeshArenaImpl>(new IMeshArenaImpl());
+}
+
+IMeshArena::~IMeshArena()
+{
+}
+
+void IMeshArena::reserve(int vert_num_hint, int face_num_hint)
+{
+ pimpl_->reserve(vert_num_hint, face_num_hint);
+}
+
+int IMeshArena::tot_allocated_verts() const
+{
+ return pimpl_->tot_allocated_verts();
+}
+
+int IMeshArena::tot_allocated_faces() const
+{
+ return pimpl_->tot_allocated_faces();
+}
+
+const Vert *IMeshArena::add_or_find_vert(const mpq3 &co, int orig)
+{
+ return pimpl_->add_or_find_vert(co, orig);
+}
+
+Face *IMeshArena::add_face(Span<const Vert *> verts,
+ int orig,
+ Span<int> edge_origs,
+ Span<bool> is_intersect)
+{
+ return pimpl_->add_face(verts, orig, edge_origs, is_intersect);
+}
+
+Face *IMeshArena::add_face(Span<const Vert *> verts, int orig, Span<int> edge_origs)
+{
+ return pimpl_->add_face(verts, orig, edge_origs);
+}
+
+Face *IMeshArena::add_face(Span<const Vert *> verts, int orig)
+{
+ return pimpl_->add_face(verts, orig);
+}
+
+const Vert *IMeshArena::add_or_find_vert(const double3 &co, int orig)
+{
+ return pimpl_->add_or_find_vert(co, orig);
+}
+
+const Vert *IMeshArena::find_vert(const mpq3 &co) const
+{
+ return pimpl_->find_vert(co);
+}
+
+const Face *IMeshArena::find_face(Span<const Vert *> verts) const
+{
+ return pimpl_->find_face(verts);
+}
+
+void IMesh::set_faces(Span<Face *> faces)
+{
+ face_ = faces;
+}
+
+int IMesh::lookup_vert(const Vert *v) const
+{
+ BLI_assert(vert_populated_);
+ return vert_to_index_.lookup_default(v, NO_INDEX);
+}
+
+void IMesh::populate_vert()
+{
+ /* This is likely an overestimate, since verts are shared between
+ * faces. It is ok if estimate is over or even under. */
+ constexpr int ESTIMATE_VERTS_PER_FACE = 4;
+ int estimate_num_verts = ESTIMATE_VERTS_PER_FACE * face_.size();
+ populate_vert(estimate_num_verts);
+}
+
+void IMesh::populate_vert(int max_verts)
+{
+ if (vert_populated_) {
+ return;
+ }
+ vert_to_index_.reserve(max_verts);
+ int next_allocate_index = 0;
+ for (const Face *f : face_) {
+ for (const Vert *v : *f) {
+ if (v->id == 1) {
+ }
+ int index = vert_to_index_.lookup_default(v, NO_INDEX);
+ if (index == NO_INDEX) {
+ BLI_assert(next_allocate_index < UINT_MAX - 2);
+ vert_to_index_.add(v, next_allocate_index++);
+ }
+ }
+ }
+ int tot_v = next_allocate_index;
+ vert_ = Array<const Vert *>(tot_v);
+ for (auto item : vert_to_index_.items()) {
+ int index = item.value;
+ BLI_assert(index < tot_v);
+ vert_[index] = item.key;
+ }
+ /* Easier debugging (at least when there are no merged input verts)
+ * if output vert order is same as input, with new verts at the end.
+ * TODO: when all debugged, set fix_order = false. */
+ const bool fix_order = true;
+ if (fix_order) {
+ std::sort(vert_.begin(), vert_.end(), [](const Vert *a, const Vert *b) {
+ if (a->orig != NO_INDEX && b->orig != NO_INDEX) {
+ return a->orig < b->orig;
+ }
+ if (a->orig != NO_INDEX) {
+ return true;
+ }
+ if (b->orig != NO_INDEX) {
+ return false;
+ }
+ return a->id < b->id;
+ });
+ for (int i : vert_.index_range()) {
+ const Vert *v = vert_[i];
+ vert_to_index_.add_overwrite(v, i);
+ }
+ }
+ vert_populated_ = true;
+}
+
+void IMesh::erase_face_positions(int f_index, Span<bool> face_pos_erase, IMeshArena *arena)
+{
+ const Face *cur_f = this->face(f_index);
+ int cur_len = cur_f->size();
+ int num_to_erase = 0;
+ for (int i : cur_f->index_range()) {
+ if (face_pos_erase[i]) {
+ ++num_to_erase;
+ }
+ }
+ if (num_to_erase == 0) {
+ return;
+ }
+ int new_len = cur_len - num_to_erase;
+ if (new_len < 3) {
+ /* Invalid erase. Don't do anything. */
+ return;
+ }
+ Array<const Vert *> new_vert(new_len);
+ Array<int> new_edge_orig(new_len);
+ Array<bool> new_is_intersect(new_len);
+ int new_index = 0;
+ for (int i : cur_f->index_range()) {
+ if (!face_pos_erase[i]) {
+ new_vert[new_index] = (*cur_f)[i];
+ new_edge_orig[new_index] = cur_f->edge_orig[i];
+ new_is_intersect[new_index] = cur_f->is_intersect[i];
+ ++new_index;
+ }
+ }
+ BLI_assert(new_index == new_len);
+ this->face_[f_index] = arena->add_face(new_vert, cur_f->orig, new_edge_orig, new_is_intersect);
+}
+
+std::ostream &operator<<(std::ostream &os, const IMesh &mesh)
+{
+ if (mesh.has_verts()) {
+ os << "Verts:\n";
+ int i = 0;
+ for (const Vert *v : mesh.vertices()) {
+ os << i << ": " << v << "\n";
+ ++i;
+ }
+ }
+ os << "\nFaces:\n";
+ int i = 0;
+ for (const Face *f : mesh.faces()) {
+ os << i << ": " << f << "\n";
+ if (f->plane != nullptr) {
+ os << " plane=" << f->plane << " eorig=[";
+ for (Face::FacePos p = 0; p < f->size(); ++p) {
+ os << f->edge_orig[p] << " ";
+ }
+ os << "]\n";
+ }
+ ++i;
+ }
+ return os;
+}
+
+struct BoundingBox {
+ float3 min{FLT_MAX, FLT_MAX, FLT_MAX};
+ float3 max{-FLT_MAX, -FLT_MAX, -FLT_MAX};
+
+ BoundingBox() = default;
+ BoundingBox(const float3 &min, const float3 &max) : min(min), max(max)
+ {
+ }
+ BoundingBox(const BoundingBox &other) : min(other.min), max(other.max)
+ {
+ }
+ BoundingBox(BoundingBox &&other) noexcept : min(std::move(other.min)), max(std::move(other.max))
+ {
+ }
+ ~BoundingBox() = default;
+ BoundingBox operator=(const BoundingBox &other)
+ {
+ if (this != &other) {
+ min = other.min;
+ max = other.max;
+ }
+ return *this;
+ }
+ BoundingBox operator=(BoundingBox &&other) noexcept
+ {
+ min = std::move(other.min);
+ max = std::move(other.max);
+ return *this;
+ }
+
+ void combine(const float3 &p)
+ {
+ min.x = min_ff(min.x, p.x);
+ min.y = min_ff(min.y, p.y);
+ min.z = min_ff(min.z, p.z);
+ max.x = max_ff(max.x, p.x);
+ max.y = max_ff(max.y, p.y);
+ max.z = max_ff(max.z, p.z);
+ }
+
+ void combine(const double3 &p)
+ {
+ min.x = min_ff(min.x, static_cast<float>(p.x));
+ min.y = min_ff(min.y, static_cast<float>(p.y));
+ min.z = min_ff(min.z, static_cast<float>(p.z));
+ max.x = max_ff(max.x, static_cast<float>(p.x));
+ max.y = max_ff(max.y, static_cast<float>(p.y));
+ max.z = max_ff(max.z, static_cast<float>(p.z));
+ }
+
+ void combine(const BoundingBox &bb)
+ {
+ min.x = min_ff(min.x, bb.min.x);
+ min.y = min_ff(min.y, bb.min.y);
+ min.z = min_ff(min.z, bb.min.z);
+ max.x = max_ff(max.x, bb.max.x);
+ max.y = max_ff(max.y, bb.max.y);
+ max.z = max_ff(max.z, bb.max.z);
+ }
+
+ void expand(float pad)
+ {
+ min.x -= pad;
+ min.y -= pad;
+ min.z -= pad;
+ max.x += pad;
+ max.y += pad;
+ max.z += pad;
+ }
+};
+
+/**
+ * Assume bounding boxes have been expanded by a sufficient epsilon on all sides
+ * so that the comparisons against the bb bounds are sufficient to guarantee that
+ * if an overlap or even touching could happen, this will return true.
+ */
+static bool bbs_might_intersect(const BoundingBox &bb_a, const BoundingBox &bb_b)
+{
+ return isect_aabb_aabb_v3(bb_a.min, bb_a.max, bb_b.min, bb_b.max);
+}
+
+/**
+ * Data and functions to calculate bounding boxes and pad them, in parallel.
+ * The bounding box calculation has the additional task of calculating the maximum
+ * absolute value of any coordinate in the mesh, which will be used to calculate
+ * the pad value.
+ */
+struct BBChunkData {
+ double max_abs_val = 0.0;
+};
+
+struct BBCalcData {
+ const IMesh &im;
+ Array<BoundingBox> *face_bounding_box;
+
+ BBCalcData(const IMesh &im, Array<BoundingBox> *fbb) : im(im), face_bounding_box(fbb){};
+};
+
+static void calc_face_bb_range_func(void *__restrict userdata,
+ const int iter,
+ const TaskParallelTLS *__restrict tls)
+{
+ BBCalcData *bbdata = static_cast<BBCalcData *>(userdata);
+ double max_abs = 0.0;
+ const Face &face = *bbdata->im.face(iter);
+ BoundingBox &bb = (*bbdata->face_bounding_box)[iter];
+ for (const Vert *v : face) {
+ bb.combine(v->co);
+ for (int i = 0; i < 3; ++i) {
+ max_abs = max_dd(max_abs, fabs(v->co[i]));
+ }
+ }
+ BBChunkData *chunk = static_cast<BBChunkData *>(tls->userdata_chunk);
+ chunk->max_abs_val = max_dd(max_abs, chunk->max_abs_val);
+}
+
+struct BBPadData {
+ Array<BoundingBox> *face_bounding_box;
+ double pad;
+
+ BBPadData(Array<BoundingBox> *fbb, double pad) : face_bounding_box(fbb), pad(pad){};
+};
+
+static void pad_face_bb_range_func(void *__restrict userdata,
+ const int iter,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ BBPadData *pad_data = static_cast<BBPadData *>(userdata);
+ (*pad_data->face_bounding_box)[iter].expand(pad_data->pad);
+}
+
+static void calc_face_bb_reduce(const void *__restrict UNUSED(userdata),
+ void *__restrict chunk_join,
+ void *__restrict chunk)
+{
+ BBChunkData *bbchunk_join = static_cast<BBChunkData *>(chunk_join);
+ BBChunkData *bbchunk = static_cast<BBChunkData *>(chunk);
+ bbchunk_join->max_abs_val = max_dd(bbchunk_join->max_abs_val, bbchunk->max_abs_val);
+}
+
+/**
+ * We will expand the bounding boxes by an epsilon on all sides so that
+ * the "less than" tests in isect_aabb_aabb_v3 are sufficient to detect
+ * touching or overlap.
+ */
+static Array<BoundingBox> calc_face_bounding_boxes(const IMesh &m)
+{
+ int n = m.face_size();
+ Array<BoundingBox> ans(n);
+ TaskParallelSettings settings;
+ BBCalcData data(m, &ans);
+ BBChunkData chunk_data;
+ BLI_parallel_range_settings_defaults(&settings);
+ settings.userdata_chunk = &chunk_data;
+ settings.userdata_chunk_size = sizeof(chunk_data);
+ settings.func_reduce = calc_face_bb_reduce;
+ settings.min_iter_per_thread = 1000;
+ settings.use_threading = intersect_use_threading;
+ BLI_task_parallel_range(0, n, &data, calc_face_bb_range_func, &settings);
+ double max_abs_val = chunk_data.max_abs_val;
+ constexpr float pad_factor = 10.0f;
+ float pad = max_abs_val == 0.0f ? FLT_EPSILON : 2 * FLT_EPSILON * max_abs_val;
+ pad *= pad_factor; /* For extra safety. */
+ TaskParallelSettings pad_settings;
+ BLI_parallel_range_settings_defaults(&pad_settings);
+ settings.min_iter_per_thread = 1000;
+ settings.use_threading = intersect_use_threading;
+ BBPadData pad_data(&ans, pad);
+ BLI_task_parallel_range(0, n, &pad_data, pad_face_bb_range_func, &pad_settings);
+ return ans;
+}
+
+/**
+ * A cluster of co-planar triangles, by index.
+ * A pair of triangles T0 and T1 is said to "non-trivially co-planar-intersect"
+ * if they are co-planar, intersect, and their intersection is not just existing
+ * elements (verts, edges) of both triangles.
+ * A co-planar cluster is said to be "nontrivial" if it has more than one triangle
+ * and every triangle in it non-trivially co-planar-intersects with at least one other
+ * triangle in the cluster.
+ */
+class CoplanarCluster {
+ Vector<int> tris_;
+ BoundingBox bb_;
+
+ public:
+ CoplanarCluster() = default;
+ CoplanarCluster(int t, const BoundingBox &bb)
+ {
+ this->add_tri(t, bb);
+ }
+ CoplanarCluster(const CoplanarCluster &other) : tris_(other.tris_), bb_(other.bb_)
+ {
+ }
+ CoplanarCluster(CoplanarCluster &&other) noexcept
+ : tris_(std::move(other.tris_)), bb_(std::move(other.bb_))
+ {
+ }
+ ~CoplanarCluster() = default;
+ CoplanarCluster &operator=(const CoplanarCluster &other)
+ {
+ if (this != &other) {
+ tris_ = other.tris_;
+ bb_ = other.bb_;
+ }
+ return *this;
+ }
+ CoplanarCluster &operator=(CoplanarCluster &&other) noexcept
+ {
+ tris_ = std::move(other.tris_);
+ bb_ = std::move(other.bb_);
+ return *this;
+ }
+
+ /* Assume that caller knows this will not be a duplicate. */
+ void add_tri(int t, const BoundingBox &bb)
+ {
+ tris_.append(t);
+ bb_ = bb;
+ }
+ int tot_tri() const
+ {
+ return tris_.size();
+ }
+ int tri(int index) const
+ {
+ return tris_[index];
+ }
+ const int *begin() const
+ {
+ return tris_.begin();
+ }
+ const int *end() const
+ {
+ return tris_.end();
+ }
+
+ const BoundingBox &bounding_box() const
+ {
+ return bb_;
+ }
+};
+
+/**
+ * Maintains indexed set of #CoplanarCluster, with the added ability
+ * to efficiently find the cluster index of any given triangle
+ * (the max triangle index needs to be given in the initializer).
+ * The #tri_cluster(t) function returns -1 if t is not part of any cluster.
+ */
+class CoplanarClusterInfo {
+ Vector<CoplanarCluster> clusters_;
+ Array<int> tri_cluster_;
+
+ public:
+ CoplanarClusterInfo() = default;
+ explicit CoplanarClusterInfo(int numtri) : tri_cluster_(Array<int>(numtri))
+ {
+ tri_cluster_.fill(-1);
+ }
+
+ int tri_cluster(int t) const
+ {
+ BLI_assert(t < tri_cluster_.size());
+ return tri_cluster_[t];
+ }
+
+ int add_cluster(CoplanarCluster cl)
+ {
+ int c_index = clusters_.append_and_get_index(cl);
+ for (int t : cl) {
+ BLI_assert(t < tri_cluster_.size());
+ tri_cluster_[t] = c_index;
+ }
+ return c_index;
+ }
+
+ int tot_cluster() const
+ {
+ return clusters_.size();
+ }
+
+ const CoplanarCluster *begin()
+ {
+ return clusters_.begin();
+ }
+
+ const CoplanarCluster *end()
+ {
+ return clusters_.end();
+ }
+
+ IndexRange index_range() const
+ {
+ return clusters_.index_range();
+ }
+
+ const CoplanarCluster &cluster(int index) const
+ {
+ BLI_assert(index < clusters_.size());
+ return clusters_[index];
+ }
+};
+
+static std::ostream &operator<<(std::ostream &os, const CoplanarCluster &cl);
+
+static std::ostream &operator<<(std::ostream &os, const CoplanarClusterInfo &clinfo);
+
+enum ITT_value_kind { INONE, IPOINT, ISEGMENT, ICOPLANAR };
+
+struct ITT_value {
+ enum ITT_value_kind kind;
+ mpq3 p1; /* Only relevant for IPOINT and ISEGMENT kind. */
+ mpq3 p2; /* Only relevant for ISEGMENT kind. */
+ int t_source; /* Index of the source triangle that intersected the target one. */
+
+ ITT_value() : kind(INONE), t_source(-1)
+ {
+ }
+ ITT_value(ITT_value_kind k) : kind(k), t_source(-1)
+ {
+ }
+ ITT_value(ITT_value_kind k, int tsrc) : kind(k), t_source(tsrc)
+ {
+ }
+ ITT_value(ITT_value_kind k, const mpq3 &p1) : kind(k), p1(p1), t_source(-1)
+ {
+ }
+ ITT_value(ITT_value_kind k, const mpq3 &p1, const mpq3 &p2)
+ : kind(k), p1(p1), p2(p2), t_source(-1)
+ {
+ }
+ ITT_value(const ITT_value &other)
+ : kind(other.kind), p1(other.p1), p2(other.p2), t_source(other.t_source)
+ {
+ }
+ ITT_value(ITT_value &&other) noexcept
+ : kind(other.kind),
+ p1(std::move(other.p1)),
+ p2(std::move(other.p2)),
+ t_source(other.t_source)
+ {
+ }
+ ~ITT_value()
+ {
+ }
+ ITT_value &operator=(const ITT_value &other)
+ {
+ if (this != &other) {
+ kind = other.kind;
+ p1 = other.p1;
+ p2 = other.p2;
+ t_source = other.t_source;
+ }
+ return *this;
+ }
+ ITT_value &operator=(ITT_value &&other) noexcept
+ {
+ kind = other.kind;
+ p1 = std::move(other.p1);
+ p2 = std::move(other.p2);
+ t_source = other.t_source;
+ return *this;
+ }
+};
+
+static std::ostream &operator<<(std::ostream &os, const ITT_value &itt);
+
+/**
+ * Project a 3d vert to a 2d one by eliding proj_axis. This does not create
+ * degeneracies as long as the projection axis is one where the corresponding
+ * component of the originating plane normal is non-zero.
+ */
+static mpq2 project_3d_to_2d(const mpq3 &p3d, int proj_axis)
+{
+ mpq2 p2d;
+ switch (proj_axis) {
+ case (0): {
+ p2d[0] = p3d[1];
+ p2d[1] = p3d[2];
+ break;
+ }
+ case (1): {
+ p2d[0] = p3d[0];
+ p2d[1] = p3d[2];
+ break;
+ }
+ case (2): {
+ p2d[0] = p3d[0];
+ p2d[1] = p3d[1];
+ break;
+ }
+ default:
+ BLI_assert(false);
+ }
+ return p2d;
+}
+
+/**
+ Is a point in the interior of a 2d triangle or on one of its
+ * edges but not either endpoint of the edge?
+ * orient[pi][i] is the orientation test of the point pi against
+ * the side of the triangle starting at index i.
+ * Assume the triangle is non-degenerate and CCW-oriented.
+ * Then answer is true if p is left of or on all three of triangle a's edges,
+ * and strictly left of at least on of them.
+ */
+static bool non_trivially_2d_point_in_tri(const int orients[3][3], int pi)
+{
+ int p_left_01 = orients[pi][0];
+ int p_left_12 = orients[pi][1];
+ int p_left_20 = orients[pi][2];
+ return (p_left_01 >= 0 && p_left_12 >= 0 && p_left_20 >= 0 &&
+ (p_left_01 + p_left_12 + p_left_20) >= 2);
+}
+
+/**
+ * Given orients as defined in non_trivially_2d_intersect, do the triangles
+ * overlap in a "hex" pattern? That is, the overlap region is a hexagon, which
+ * one gets by having, each point of one triangle being strictly right-of one
+ * edge of the other and strictly left of the other two edges; and vice versa.
+ */
+static bool non_trivially_2d_hex_overlap(int orients[2][3][3])
+{
+ for (int ab = 0; ab < 2; ++ab) {
+ for (int i = 0; i < 3; ++i) {
+ bool ok = orients[ab][i][0] + orients[ab][i][1] + orients[ab][i][2] == 1 &&
+ orients[ab][i][0] != 0 && orients[ab][i][1] != 0 && orients[i][2] != 0;
+ if (!ok) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+/**
+ * Given orients as defined in non_trivially_2d_intersect, do the triangles
+ * have one shared edge in a "folded-over" configuration?
+ * As well as a shared edge, the third vertex of one triangle needs to be
+ * right-of one and left-of the other two edges of the other triangle.
+ */
+static bool non_trivially_2d_shared_edge_overlap(int orients[2][3][3],
+ const mpq2 *a[3],
+ const mpq2 *b[3])
+{
+ for (int i = 0; i < 3; ++i) {
+ int in = (i + 1) % 3;
+ int inn = (i + 2) % 3;
+ for (int j = 0; j < 3; ++j) {
+ int jn = (j + 1) % 3;
+ int jnn = (j + 2) % 3;
+ if (*a[i] == *b[j] && *a[in] == *b[jn]) {
+ /* Edge from a[i] is shared with edge from b[j]. */
+ /* See if a[inn] is right-of or on one of the other edges of b.
+ * If it is on, then it has to be right-of or left-of the shared edge,
+ * depending on which edge it is. */
+ if (orients[0][inn][jn] < 0 || orients[0][inn][jnn] < 0) {
+ return true;
+ }
+ if (orients[0][inn][jn] == 0 && orients[0][inn][j] == 1) {
+ return true;
+ }
+ if (orients[0][inn][jnn] == 0 && orients[0][inn][j] == -1) {
+ return true;
+ }
+ /* Similarly for `b[jnn]`. */
+ if (orients[1][jnn][in] < 0 || orients[1][jnn][inn] < 0) {
+ return true;
+ }
+ if (orients[1][jnn][in] == 0 && orients[1][jnn][i] == 1) {
+ return true;
+ }
+ if (orients[1][jnn][inn] == 0 && orients[1][jnn][i] == -1) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+/**
+ * Are the triangles the same, perhaps with some permutation of vertices?
+ */
+static bool same_triangles(const mpq2 *a[3], const mpq2 *b[3])
+{
+ for (int i = 0; i < 3; ++i) {
+ if (a[0] == b[i] && a[1] == b[(i + 1) % 3] && a[2] == b[(i + 2) % 3]) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Do 2d triangles (a[0], a[1], a[2]) and (b[0], b[1], b2[2]) intersect at more than just shared
+ * vertices or a shared edge? This is true if any point of one triangle is non-trivially inside the
+ * other. NO: that isn't quite sufficient: there is also the case where the verts are all mutually
+ * outside the other's triangle, but there is a hexagonal overlap region where they overlap.
+ */
+static bool non_trivially_2d_intersect(const mpq2 *a[3], const mpq2 *b[3])
+{
+ /* TODO: Could experiment with trying bounding box tests before these.
+ * TODO: Find a less expensive way than 18 orient tests to do this. */
+
+ /* `orients[0][ai][bi]` is orient of point `a[ai]` compared to segment starting at `b[bi]`.
+ * `orients[1][bi][ai]` is orient of point `b[bi]` compared to segment starting at `a[ai]`. */
+ int orients[2][3][3];
+ for (int ab = 0; ab < 2; ++ab) {
+ for (int ai = 0; ai < 3; ++ai) {
+ for (int bi = 0; bi < 3; ++bi) {
+ if (ab == 0) {
+ orients[0][ai][bi] = orient2d(*b[bi], *b[(bi + 1) % 3], *a[ai]);
+ }
+ else {
+ orients[1][bi][ai] = orient2d(*a[ai], *a[(ai + 1) % 3], *b[bi]);
+ }
+ }
+ }
+ }
+ return non_trivially_2d_point_in_tri(orients[0], 0) ||
+ non_trivially_2d_point_in_tri(orients[0], 1) ||
+ non_trivially_2d_point_in_tri(orients[0], 2) ||
+ non_trivially_2d_point_in_tri(orients[1], 0) ||
+ non_trivially_2d_point_in_tri(orients[1], 1) ||
+ non_trivially_2d_point_in_tri(orients[1], 2) || non_trivially_2d_hex_overlap(orients) ||
+ non_trivially_2d_shared_edge_overlap(orients, a, b) || same_triangles(a, b);
+ return true;
+}
+
+/**
+ * Does triangle t in tm non-trivially non-co-planar intersect any triangle
+ * in `CoplanarCluster cl`? Assume t is known to be in the same plane as all
+ * the triangles in cl, and that proj_axis is a good axis to project down
+ * to solve this problem in 2d.
+ */
+static bool non_trivially_coplanar_intersects(const IMesh &tm,
+ int t,
+ const CoplanarCluster &cl,
+ int proj_axis)
+{
+ const Face &tri = *tm.face(t);
+ mpq2 v0 = project_3d_to_2d(tri[0]->co_exact, proj_axis);
+ mpq2 v1 = project_3d_to_2d(tri[1]->co_exact, proj_axis);
+ mpq2 v2 = project_3d_to_2d(tri[2]->co_exact, proj_axis);
+ if (orient2d(v0, v1, v2) != 1) {
+ mpq2 tmp = v1;
+ v1 = v2;
+ v2 = tmp;
+ }
+ for (const int cl_t : cl) {
+ const Face &cl_tri = *tm.face(cl_t);
+ mpq2 ctv0 = project_3d_to_2d(cl_tri[0]->co_exact, proj_axis);
+ mpq2 ctv1 = project_3d_to_2d(cl_tri[1]->co_exact, proj_axis);
+ mpq2 ctv2 = project_3d_to_2d(cl_tri[2]->co_exact, proj_axis);
+ if (orient2d(ctv0, ctv1, ctv2) != 1) {
+ mpq2 tmp = ctv1;
+ ctv1 = ctv2;
+ ctv2 = tmp;
+ }
+ const mpq2 *v[] = {&v0, &v1, &v2};
+ const mpq2 *ctv[] = {&ctv0, &ctv1, &ctv2};
+ if (non_trivially_2d_intersect(v, ctv)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/* Keeping this code for a while, but for now, almost all
+ * trivial intersects are found before calling intersect_tri_tri now.
+ */
+# if 0
+/**
+ * Do tri1 and tri2 intersect at all, and if so, is the intersection
+ * something other than a common vertex or a common edge?
+ * The \a itt value is the result of calling intersect_tri_tri on tri1, tri2.
+ */
+static bool non_trivial_intersect(const ITT_value &itt, const Face * tri1, const Face * tri2)
+{
+ if (itt.kind == INONE) {
+ return false;
+ }
+ const Face * tris[2] = {tri1, tri2};
+ if (itt.kind == IPOINT) {
+ bool has_p_as_vert[2] {false, false};
+ for (int i = 0; i < 2; ++i) {
+ for (const Vert * v : *tris[i]) {
+ if (itt.p1 == v->co_exact) {
+ has_p_as_vert[i] = true;
+ break;
+ }
+ }
+ }
+ return !(has_p_as_vert[0] && has_p_as_vert[1]);
+ }
+ if (itt.kind == ISEGMENT) {
+ bool has_seg_as_edge[2] = {false, false};
+ for (int i = 0; i < 2; ++i) {
+ const Face &t = *tris[i];
+ for (int pos : t.index_range()) {
+ int nextpos = t.next_pos(pos);
+ if ((itt.p1 == t[pos]->co_exact && itt.p2 == t[nextpos]->co_exact) ||
+ (itt.p2 == t[pos]->co_exact && itt.p1 == t[nextpos]->co_exact)) {
+ has_seg_as_edge[i] = true;
+ break;
+ }
+ }
+ }
+ return !(has_seg_as_edge[0] && has_seg_as_edge[1]);
+ }
+ BLI_assert(itt.kind == ICOPLANAR);
+ /* TODO: refactor this common code with code above. */
+ int proj_axis = mpq3::dominant_axis(tri1->plane.norm_exact);
+ mpq2 tri_2d[2][3];
+ for (int i = 0; i < 2; ++i) {
+ mpq2 v0 = project_3d_to_2d((*tris[i])[0]->co_exact, proj_axis);
+ mpq2 v1 = project_3d_to_2d((*tris[i])[1]->co_exact, proj_axis);
+ mpq2 v2 = project_3d_to_2d((*tris[i])[2]->co_exact, proj_axis);
+ if (mpq2::orient2d(v0, v1, v2) != 1) {
+ mpq2 tmp = v1;
+ v1 = v2;
+ v2 = tmp;
+ }
+ tri_2d[i][0] = v0;
+ tri_2d[i][1] = v1;
+ tri_2d[i][2] = v2;
+ }
+ const mpq2 *va[] = {&tri_2d[0][0], &tri_2d[0][1], &tri_2d[0][2]};
+ const mpq2 *vb[] = {&tri_2d[1][0], &tri_2d[1][1], &tri_2d[1][2]};
+ return non_trivially_2d_intersect(va, vb);
+}
+# endif
+
+/**
+ * The sup and index functions are defined in the paper:
+ * EXACT GEOMETRIC COMPUTATION USING CASCADING, by
+ * Burnikel, Funke, and Seel. They are used to find absolute
+ * bounds on the error due to doing a calculation in double
+ * instead of exactly. For calculations involving only +, -, and *,
+ * the supremum is the same function except using absolute values
+ * on inputs and using + instead of -.
+ * The index function follows these rules:
+ * index(x op y) = 1 + max(index(x), index(y)) for op + or -
+ * index(x * y) = 1 + index(x) + index(y)
+ * index(x) = 0 if input x can be represented exactly as a double
+ * index(x) = 1 otherwise.
+ *
+ * With these rules in place, we know an absolute error bound:
+ *
+ * |E_exact - E| <= supremum(E) * index(E) * DBL_EPSILON
+ *
+ * where E_exact is what would have been the exact value of the
+ * expression and E is the one calculated with doubles.
+ *
+ * So the sign of E is the same as the sign of E_exact if
+ * |E| > supremum(E) * index(E) * DBL_EPSILON
+ *
+ * Note: a possible speedup would be to have a simple function
+ * that calculates the error bound if one knows that all values
+ * are less than some global maximum - most of the function would
+ * be calculated ahead of time. The global max could be passed
+ * from above.
+ */
+static double supremum_dot_cross(const double3 &a, const double3 &b)
+{
+ double3 abs_a = double3::abs(a);
+ double3 abs_b = double3::abs(b);
+ double3 c;
+ /* This is dot(cross(a, b), cross(a,b)) but using absolute values for a and b
+ * and always using + when operation is + or -. */
+ c[0] = abs_a[1] * abs_b[2] + abs_a[2] * abs_b[1];
+ c[1] = abs_a[2] * abs_b[0] + abs_a[0] * abs_b[2];
+ c[2] = abs_a[0] * abs_b[1] + abs_a[1] * abs_b[0];
+ return double3::dot(c, c);
+}
+
+/**
+ * Used with supremum to get error bound. See Burnikel et al paper.
+ * index_plane_coord is the index of a plane coordinate calculated
+ * for a triangle using the usual formula, assuming the input
+ * coordinates have index 1.
+ * index_cross is the index of each coordinate of the cross product.
+ * It is actually 2 + 2 * (max index of input coords).
+ * index_dot_cross is the index of the dot product of two cross products.
+ * It is actually 7 + 4 * (max index of input coords)
+ */
+constexpr int index_dot_cross = 11;
+
+static double supremum_dot(const double3 &a, const double3 &b)
+{
+ double3 abs_a = double3::abs(a);
+ double3 abs_b = double3::abs(b);
+ return double3::dot(abs_a, abs_b);
+}
+
+/* Actually index_dot = 3 + 2 * (max index of input coordinates). */
+/* The index of dot when inputs are plane_coords with index 1 is much higher.
+ * Plane coords have index 6.
+ */
+constexpr int index_dot_plane_coords = 15;
+
+static double supremum_orient3d(const double3 &a,
+ const double3 &b,
+ const double3 &c,
+ const double3 &d)
+{
+ double3 abs_a = double3::abs(a);
+ double3 abs_b = double3::abs(b);
+ double3 abs_c = double3::abs(c);
+ double3 abs_d = double3::abs(d);
+ double adx = abs_a[0] + abs_d[0];
+ double bdx = abs_b[0] + abs_d[0];
+ double cdx = abs_c[0] + abs_d[0];
+ double ady = abs_a[1] + abs_d[1];
+ double bdy = abs_b[1] + abs_d[1];
+ double cdy = abs_c[1] + abs_d[1];
+ double adz = abs_a[2] + abs_d[2];
+ double bdz = abs_b[2] + abs_d[2];
+ double cdz = abs_c[2] + abs_d[2];
+
+ double bdxcdy = bdx * cdy;
+ double cdxbdy = cdx * bdy;
+
+ double cdxady = cdx * ady;
+ double adxcdy = adx * cdy;
+
+ double adxbdy = adx * bdy;
+ double bdxady = bdx * ady;
+
+ double det = adz * (bdxcdy + cdxbdy) + bdz * (cdxady + adxcdy) + cdz * (adxbdy + bdxady);
+ return det;
+}
+
+/** Actually index_orient3d = 10 + 4 * (max degree of input coordinates) */
+constexpr int index_orient3d = 14;
+
+/**
+ * Return the approximate orient3d of the four double3's, with
+ * the guarantee that if the value is -1 or 1 then the underlying
+ * mpq3 test would also have returned that value.
+ * When the return value is 0, we are not sure of the sign.
+ */
+static int filter_orient3d(const double3 &a, const double3 &b, const double3 &c, const double3 &d)
+{
+ double o3dfast = orient3d_fast(a, b, c, d);
+ if (o3dfast == 0.0) {
+ return 0;
+ }
+ double err_bound = supremum_orient3d(a, b, c, d) * index_orient3d * DBL_EPSILON;
+ if (fabs(o3dfast) > err_bound) {
+ return o3dfast > 0.0 ? 1 : -1;
+ }
+ return 0;
+}
+
+/**
+ * Return the approximate orient3d of the triangle plane points and v, with
+ * the guarantee that if the value is -1 or 1 then the underlying
+ * mpq3 test would also have returned that value.
+ * When the return value is 0, we are not sure of the sign.
+ */
+static int filter_tri_plane_vert_orient3d(const Face &tri, const Vert *v)
+{
+ return filter_orient3d(tri[0]->co, tri[1]->co, tri[2]->co, v->co);
+}
+
+/**
+ * Are vectors a and b parallel or nearly parallel?
+ * This routine should only return false if we are certain
+ * that they are not parallel, taking into account the
+ * possible numeric errors and input value approximation.
+ */
+static bool near_parallel_vecs(const double3 &a, const double3 &b)
+{
+ double3 cr = double3::cross_high_precision(a, b);
+ double cr_len_sq = cr.length_squared();
+ if (cr_len_sq == 0.0) {
+ return true;
+ }
+ double err_bound = supremum_dot_cross(a, b) * index_dot_cross * DBL_EPSILON;
+ if (cr_len_sq > err_bound) {
+ return false;
+ }
+ return true;
+}
+
+/**
+ * Return true if we are sure that dot(a,b) > 0, taking into
+ * account the error bounds due to numeric errors and input value
+ * approximation.
+ */
+static bool dot_must_be_positive(const double3 &a, const double3 &b)
+{
+ double d = double3::dot(a, b);
+ if (d <= 0.0) {
+ return false;
+ }
+ double err_bound = supremum_dot(a, b) * index_dot_plane_coords * DBL_EPSILON;
+ if (d > err_bound) {
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Return the approximate side of point p on a plane with normal plane_no and point plane_p.
+ * The answer will be 1 if p is definitely above the plane, -1 if it is definitely below.
+ * If the answer is 0, we are unsure about which side of the plane (or if it is on the plane).
+ * In exact arithmetic, the answer is just `sgn(dot(p - plane_p, plane_no))`.
+ *
+ * The plane_no input is constructed, so has a higher index.
+ */
+constexpr int index_plane_side = 3 + 2 * index_dot_plane_coords;
+
+static int filter_plane_side(const double3 &p,
+ const double3 &plane_p,
+ const double3 &plane_no,
+ const double3 &abs_p,
+ const double3 &abs_plane_p,
+ const double3 &abs_plane_no)
+{
+ double d = double3::dot(p - plane_p, plane_no);
+ if (d == 0.0) {
+ return 0;
+ }
+ double supremum = double3::dot(abs_p + abs_plane_p, abs_plane_no);
+ double err_bound = supremum * index_plane_side * DBL_EPSILON;
+ if (d > err_bound) {
+ return d > 0 ? 1 : -1;
+ }
+ return 0;
+}
+
+/**
+ * A fast, non-exhaustive test for non_trivial intersection.
+ * If this returns false then we are sure that tri1 and tri2
+ * do not intersect. If it returns true, they may or may not
+ * non-trivially intersect.
+ * We assume that bounding box overlap tests have already been
+ * done, so don't repeat those here. This routine is checking
+ * for the very common cases (when doing mesh self-intersect)
+ * where triangles share an edge or a vertex, but don't
+ * otherwise intersect.
+ */
+static bool may_non_trivially_intersect(Face *t1, Face *t2)
+{
+ Face &tri1 = *t1;
+ Face &tri2 = *t2;
+ BLI_assert(t1->plane_populated() && t2->plane_populated());
+ Face::FacePos share1_pos[3];
+ Face::FacePos share2_pos[3];
+ int n_shared = 0;
+ for (Face::FacePos p1 = 0; p1 < 3; ++p1) {
+ const Vert *v1 = tri1[p1];
+ for (Face::FacePos p2 = 0; p2 < 3; ++p2) {
+ const Vert *v2 = tri2[p2];
+ if (v1 == v2) {
+ share1_pos[n_shared] = p1;
+ share2_pos[n_shared] = p2;
+ ++n_shared;
+ }
+ }
+ }
+ if (n_shared == 2) {
+ /* t1 and t2 share an entire edge.
+ * If their normals are not parallel, they cannot non-trivially intersect. */
+ if (!near_parallel_vecs(tri1.plane->norm, tri2.plane->norm)) {
+ return false;
+ }
+ /* The normals are parallel or nearly parallel.
+ * If the normals are in the same direction and the edges have opposite
+ * directions in the two triangles, they cannot non-trivially intersect. */
+ bool erev1 = tri1.prev_pos(share1_pos[0]) == share1_pos[1];
+ bool erev2 = tri2.prev_pos(share2_pos[0]) == share2_pos[1];
+ if (erev1 != erev2) {
+ if (dot_must_be_positive(tri1.plane->norm, tri2.plane->norm)) {
+ return false;
+ }
+ }
+ }
+ else if (n_shared == 1) {
+ /* t1 and t2 share a vertex, but not an entire edge.
+ * If the two non-shared verts of t2 are both on the same
+ * side of tri1's plane, then they cannot non-trivially intersect.
+ * (There are some other cases that could be caught here but
+ * they are more expensive to check). */
+ Face::FacePos p = share2_pos[0];
+ const Vert *v2a = p == 0 ? tri2[1] : tri2[0];
+ const Vert *v2b = (p == 0 || p == 1) ? tri2[2] : tri2[1];
+ int o1 = filter_tri_plane_vert_orient3d(tri1, v2a);
+ int o2 = filter_tri_plane_vert_orient3d(tri1, v2b);
+ if (o1 == o2 && o1 != 0) {
+ return false;
+ }
+ p = share1_pos[0];
+ const Vert *v1a = p == 0 ? tri1[1] : tri1[0];
+ const Vert *v1b = (p == 0 || p == 1) ? tri1[2] : tri1[1];
+ o1 = filter_tri_plane_vert_orient3d(tri2, v1a);
+ o2 = filter_tri_plane_vert_orient3d(tri2, v1b);
+ if (o1 == o2 && o1 != 0) {
+ return false;
+ }
+ }
+ /* We weren't able to prove that any intersection is trivial. */
+ return true;
+}
+
+/*
+ * interesect_tri_tri and helper functions.
+ * This code uses the algorithm of Guigue and Devillers, as described
+ * in "Faster Triangle-Triangle Intersection Tests".
+ * Adapted from github code by Eric Haines:
+ * github.com/erich666/jgt-code/tree/master/Volume_08/Number_1/Guigue2003
+ */
+
+/**
+ * Return the point on ab where the plane with normal n containing point c intersects it.
+ * Assumes ab is not perpendicular to n.
+ * This works because the ratio of the projections of ab and ac onto n is the same as
+ * the ratio along the line ab of the intersection point to the whole of ab.
+ */
+static inline mpq3 tti_interp(const mpq3 &a, const mpq3 &b, const mpq3 &c, const mpq3 &n)
+{
+ mpq3 ab = a - b;
+ mpq_class den = mpq3::dot(ab, n);
+ BLI_assert(den != 0);
+ mpq_class alpha = mpq3::dot(a - c, n) / den;
+ return a - alpha * ab;
+}
+
+/**
+ * Return +1, 0, -1 as a + ad is above, on, or below the oriented plane containing a, b, c in CCW
+ * order. This is the same as -oriented(a, b, c, a + ad), but uses fewer arithmetic operations.
+ * TODO: change arguments to `const Vert *` and use floating filters.
+ */
+static inline int tti_above(const mpq3 &a, const mpq3 &b, const mpq3 &c, const mpq3 &ad)
+{
+ mpq3 n = mpq3::cross(b - a, c - a);
+ return sgn(mpq3::dot(ad, n));
+}
+
+/**
+ * Given that triangles (p1, q1, r1) and (p2, q2, r2) are in canonical order,
+ * use the classification chart in the Guigue and Devillers paper to find out
+ * how the intervals [i,j] and [k,l] overlap, where [i,j] is where p1r1 and p1q1
+ * intersect the plane-plane intersection line, L, and [k,l] is where p2q2 and p2r2
+ * intersect L. By the canonicalization, those segments intersect L exactly once.
+ * Canonicalization has made it so that for p1, q1, r1, either:
+ * (a)) p1 is off the second triangle's plane and both q1 and r1 are either
+ * on the plane or on the other side of it from p1; or
+ * (b) p1 is on the plane both q1 and r1 are on the same side
+ * of the plane and at least one of q1 and r1 are off the plane.
+ * Similarly for p2, q2, r2 with respect to the first triangle's plane.
+ */
+static ITT_value itt_canon2(const mpq3 &p1,
+ const mpq3 &q1,
+ const mpq3 &r1,
+ const mpq3 &p2,
+ const mpq3 &q2,
+ const mpq3 &r2,
+ const mpq3 &n1,
+ const mpq3 &n2)
+{
+ constexpr int dbg_level = 0;
+ if (dbg_level > 0) {
+ std::cout << "\ntri_tri_intersect_canon:\n";
+ std::cout << "p1=" << p1 << " q1=" << q1 << " r1=" << r1 << "\n";
+ std::cout << "p2=" << p2 << " q2=" << q2 << " r2=" << r2 << "\n";
+ std::cout << "n1=" << n1 << " n2=" << n2 << "\n";
+ std::cout << "approximate values:\n";
+ std::cout << "p1=(" << p1[0].get_d() << "," << p1[1].get_d() << "," << p1[2].get_d() << ")\n";
+ std::cout << "q1=(" << q1[0].get_d() << "," << q1[1].get_d() << "," << q1[2].get_d() << ")\n";
+ std::cout << "r1=(" << r1[0].get_d() << "," << r1[1].get_d() << "," << r1[2].get_d() << ")\n";
+ std::cout << "p2=(" << p2[0].get_d() << "," << p2[1].get_d() << "," << p2[2].get_d() << ")\n";
+ std::cout << "q2=(" << q2[0].get_d() << "," << q2[1].get_d() << "," << q2[2].get_d() << ")\n";
+ std::cout << "r2=(" << r2[0].get_d() << "," << r2[1].get_d() << "," << r2[2].get_d() << ")\n";
+ std::cout << "n1=(" << n1[0].get_d() << "," << n1[1].get_d() << "," << n1[2].get_d() << ")\n";
+ std::cout << "n2=(" << n2[0].get_d() << "," << n2[1].get_d() << "," << n2[2].get_d() << ")\n";
+ }
+ mpq3 p1p2 = p2 - p1;
+ mpq3 intersect_1;
+ mpq3 intersect_2;
+ bool no_overlap = false;
+ /* Top test in classification tree. */
+ if (tti_above(p1, q1, r2, p1p2) > 0) {
+ /* Middle right test in classification tree. */
+ if (tti_above(p1, r1, r2, p1p2) <= 0) {
+ /* Bottom right test in classification tree. */
+ if (tti_above(p1, r1, q2, p1p2) > 0) {
+ /* Overlap is [k [i l] j]. */
+ if (dbg_level > 0) {
+ std::cout << "overlap [k [i l] j]\n";
+ }
+ /* i is intersect with p1r1. l is intersect with p2r2. */
+ intersect_1 = tti_interp(p1, r1, p2, n2);
+ intersect_2 = tti_interp(p2, r2, p1, n1);
+ }
+ else {
+ /* Overlap is [i [k l] j]. */
+ if (dbg_level > 0) {
+ std::cout << "overlap [i [k l] j]\n";
+ }
+ /* k is intersect with p2q2. l is intersect is p2r2. */
+ intersect_1 = tti_interp(p2, q2, p1, n1);
+ intersect_2 = tti_interp(p2, r2, p1, n1);
+ }
+ }
+ else {
+ /* No overlap: [k l] [i j]. */
+ if (dbg_level > 0) {
+ std::cout << "no overlap: [k l] [i j]\n";
+ }
+ no_overlap = true;
+ }
+ }
+ else {
+ /* Middle left test in classification tree. */
+ if (tti_above(p1, q1, q2, p1p2) < 0) {
+ /* No overlap: [i j] [k l]. */
+ if (dbg_level > 0) {
+ std::cout << "no overlap: [i j] [k l]\n";
+ }
+ no_overlap = true;
+ }
+ else {
+ /* Bottom left test in classification tree. */
+ if (tti_above(p1, r1, q2, p1p2) >= 0) {
+ /* Overlap is [k [i j] l]. */
+ if (dbg_level > 0) {
+ std::cout << "overlap [k [i j] l]\n";
+ }
+ /* i is intersect with p1r1. j is intersect with p1q1. */
+ intersect_1 = tti_interp(p1, r1, p2, n2);
+ intersect_2 = tti_interp(p1, q1, p2, n2);
+ }
+ else {
+ /* Overlap is [i [k j] l]. */
+ if (dbg_level > 0) {
+ std::cout << "overlap [i [k j] l]\n";
+ }
+ /* k is intersect with p2q2. j is intersect with p1q1. */
+ intersect_1 = tti_interp(p2, q2, p1, n1);
+ intersect_2 = tti_interp(p1, q1, p2, n2);
+ }
+ }
+ }
+ if (no_overlap) {
+ return ITT_value(INONE);
+ }
+ if (intersect_1 == intersect_2) {
+ if (dbg_level > 0) {
+ std::cout << "single intersect: " << intersect_1 << "\n";
+ }
+ return ITT_value(IPOINT, intersect_1);
+ }
+ if (dbg_level > 0) {
+ std::cout << "intersect segment: " << intersect_1 << ", " << intersect_2 << "\n";
+ }
+ return ITT_value(ISEGMENT, intersect_1, intersect_2);
+}
+
+/* Helper function for intersect_tri_tri. Args have been canonicalized for triangle 1. */
+
+static ITT_value itt_canon1(const mpq3 &p1,
+ const mpq3 &q1,
+ const mpq3 &r1,
+ const mpq3 &p2,
+ const mpq3 &q2,
+ const mpq3 &r2,
+ const mpq3 &n1,
+ const mpq3 &n2,
+ int sp2,
+ int sq2,
+ int sr2)
+{
+ constexpr int dbg_level = 0;
+ if (sp2 > 0) {
+ if (sq2 > 0) {
+ return itt_canon2(p1, r1, q1, r2, p2, q2, n1, n2);
+ }
+ if (sr2 > 0) {
+ return itt_canon2(p1, r1, q1, q2, r2, p2, n1, n2);
+ }
+ return itt_canon2(p1, q1, r1, p2, q2, r2, n1, n2);
+ }
+ if (sp2 < 0) {
+ if (sq2 < 0) {
+ return itt_canon2(p1, q1, r1, r2, p2, q2, n1, n2);
+ }
+ if (sr2 < 0) {
+ return itt_canon2(p1, q1, r1, q2, r2, p2, n1, n2);
+ }
+ return itt_canon2(p1, r1, q1, p2, q2, r2, n1, n2);
+ }
+ if (sq2 < 0) {
+ if (sr2 >= 0) {
+ return itt_canon2(p1, r1, q1, q2, r2, p2, n1, n2);
+ }
+ return itt_canon2(p1, q1, r1, p2, q2, r2, n1, n2);
+ }
+ if (sq2 > 0) {
+ if (sr2 > 0) {
+ return itt_canon2(p1, r1, q1, p2, q2, r2, n1, n2);
+ }
+ return itt_canon2(p1, q1, r1, q2, r2, p2, n1, n2);
+ }
+ if (sr2 > 0) {
+ return itt_canon2(p1, q1, r1, r2, p2, q2, n1, n2);
+ }
+ if (sr2 < 0) {
+ return itt_canon2(p1, r1, q1, r2, p2, q2, n1, n2);
+ }
+ if (dbg_level > 0) {
+ std::cout << "triangles are co-planar\n";
+ }
+ return ITT_value(ICOPLANAR);
+}
+
+static ITT_value intersect_tri_tri(const IMesh &tm, int t1, int t2)
+{
+ constexpr int dbg_level = 0;
+# ifdef PERFDEBUG
+ incperfcount(1); /* Intersect_tri_tri calls. */
+# endif
+ const Face &tri1 = *tm.face(t1);
+ const Face &tri2 = *tm.face(t2);
+ BLI_assert(tri1.plane_populated() && tri2.plane_populated());
+ const Vert *vp1 = tri1[0];
+ const Vert *vq1 = tri1[1];
+ const Vert *vr1 = tri1[2];
+ const Vert *vp2 = tri2[0];
+ const Vert *vq2 = tri2[1];
+ const Vert *vr2 = tri2[2];
+ if (dbg_level > 0) {
+ std::cout << "\nINTERSECT_TRI_TRI t1=" << t1 << ", t2=" << t2 << "\n";
+ std::cout << " p1 = " << vp1 << "\n";
+ std::cout << " q1 = " << vq1 << "\n";
+ std::cout << " r1 = " << vr1 << "\n";
+ std::cout << " p2 = " << vp2 << "\n";
+ std::cout << " q2 = " << vq2 << "\n";
+ std::cout << " r2 = " << vr2 << "\n";
+ }
+
+ /* Get signs of t1's vertices' distances to plane of t2 and vice versa. */
+
+ /* Try first getting signs with double arithmetic, with error bounds.
+ * If the signs calculated in this section are not 0, they are the same
+ * as what they would be using exact arithmetic. */
+ const double3 &d_p1 = vp1->co;
+ const double3 &d_q1 = vq1->co;
+ const double3 &d_r1 = vr1->co;
+ const double3 &d_p2 = vp2->co;
+ const double3 &d_q2 = vq2->co;
+ const double3 &d_r2 = vr2->co;
+ const double3 &d_n2 = tri2.plane->norm;
+
+ const double3 &abs_d_p1 = double3::abs(d_p1);
+ const double3 &abs_d_q1 = double3::abs(d_q1);
+ const double3 &abs_d_r1 = double3::abs(d_r1);
+ const double3 &abs_d_r2 = double3::abs(d_r2);
+ const double3 &abs_d_n2 = double3::abs(d_n2);
+
+ int sp1 = filter_plane_side(d_p1, d_r2, d_n2, abs_d_p1, abs_d_r2, abs_d_n2);
+ int sq1 = filter_plane_side(d_q1, d_r2, d_n2, abs_d_q1, abs_d_r2, abs_d_n2);
+ int sr1 = filter_plane_side(d_r1, d_r2, d_n2, abs_d_r1, abs_d_r2, abs_d_n2);
+ if ((sp1 > 0 && sq1 > 0 && sr1 > 0) || (sp1 < 0 && sq1 < 0 && sr1 < 0)) {
+# ifdef PERFDEBUG
+ incperfcount(2); /* Tri tri intersects decided by filter plane tests. */
+# endif
+ if (dbg_level > 0) {
+ std::cout << "no intersection, all t1's verts above or below t2\n";
+ }
+ return ITT_value(INONE);
+ }
+
+ const double3 &d_n1 = tri1.plane->norm;
+ const double3 &abs_d_p2 = double3::abs(d_p2);
+ const double3 &abs_d_q2 = double3::abs(d_q2);
+ const double3 &abs_d_n1 = double3::abs(d_n1);
+
+ int sp2 = filter_plane_side(d_p2, d_r1, d_n1, abs_d_p2, abs_d_r1, abs_d_n1);
+ int sq2 = filter_plane_side(d_q2, d_r1, d_n1, abs_d_q2, abs_d_r1, abs_d_n1);
+ int sr2 = filter_plane_side(d_r2, d_r1, d_n1, abs_d_r2, abs_d_r1, abs_d_n1);
+ if ((sp2 > 0 && sq2 > 0 && sr2 > 0) || (sp2 < 0 && sq2 < 0 && sr2 < 0)) {
+# ifdef PERFDEBUG
+ incperfcount(2); /* Tri tri intersects decided by filter plane tests. */
+# endif
+ if (dbg_level > 0) {
+ std::cout << "no intersection, all t2's verts above or below t1\n";
+ }
+ return ITT_value(INONE);
+ }
+
+ const mpq3 &p1 = vp1->co_exact;
+ const mpq3 &q1 = vq1->co_exact;
+ const mpq3 &r1 = vr1->co_exact;
+ const mpq3 &p2 = vp2->co_exact;
+ const mpq3 &q2 = vq2->co_exact;
+ const mpq3 &r2 = vr2->co_exact;
+
+ const mpq3 &n2 = tri2.plane->norm_exact;
+ if (sp1 == 0) {
+ sp1 = sgn(mpq3::dot(p1 - r2, n2));
+ }
+ if (sq1 == 0) {
+ sq1 = sgn(mpq3::dot(q1 - r2, n2));
+ }
+ if (sr1 == 0) {
+ sr1 = sgn(mpq3::dot(r1 - r2, n2));
+ }
+
+ if (dbg_level > 1) {
+ std::cout << " sp1=" << sp1 << " sq1=" << sq1 << " sr1=" << sr1 << "\n";
+ }
+
+ if ((sp1 * sq1 > 0) && (sp1 * sr1 > 0)) {
+ if (dbg_level > 0) {
+ std::cout << "no intersection, all t1's verts above or below t2 (exact)\n";
+ }
+# ifdef PERFDEBUG
+ incperfcount(3); /* Tri tri intersects decided by exact plane tests. */
+# endif
+ return ITT_value(INONE);
+ }
+
+ /* Repeat for signs of t2's vertices with respect to plane of t1. */
+ const mpq3 &n1 = tri1.plane->norm_exact;
+ if (sp2 == 0) {
+ sp2 = sgn(mpq3::dot(p2 - r1, n1));
+ }
+ if (sq2 == 0) {
+ sq2 = sgn(mpq3::dot(q2 - r1, n1));
+ }
+ if (sr2 == 0) {
+ sr2 = sgn(mpq3::dot(r2 - r1, n1));
+ }
+
+ if (dbg_level > 1) {
+ std::cout << " sp2=" << sp2 << " sq2=" << sq2 << " sr2=" << sr2 << "\n";
+ }
+
+ if ((sp2 * sq2 > 0) && (sp2 * sr2 > 0)) {
+ if (dbg_level > 0) {
+ std::cout << "no intersection, all t2's verts above or below t1 (exact)\n";
+ }
+# ifdef PERFDEBUG
+ incperfcount(3); /* Tri tri intersects decided by exact plane tests. */
+# endif
+ return ITT_value(INONE);
+ }
+
+ /* Do rest of the work with vertices in a canonical order, where p1 is on
+ * positive side of plane and q1, r1 are not, or p1 is on the plane and
+ * q1 and r1 are off the plane on the same side. */
+ ITT_value ans;
+ if (sp1 > 0) {
+ if (sq1 > 0) {
+ ans = itt_canon1(r1, p1, q1, p2, r2, q2, n1, n2, sp2, sr2, sq2);
+ }
+ else if (sr1 > 0) {
+ ans = itt_canon1(q1, r1, p1, p2, r2, q2, n1, n2, sp2, sr2, sq2);
+ }
+ else {
+ ans = itt_canon1(p1, q1, r1, p2, q2, r2, n1, n2, sp2, sq2, sr2);
+ }
+ }
+ else if (sp1 < 0) {
+ if (sq1 < 0) {
+ ans = itt_canon1(r1, p1, q1, p2, q2, r2, n1, n2, sp2, sq2, sr2);
+ }
+ else if (sr1 < 0) {
+ ans = itt_canon1(q1, r1, p1, p2, q2, r2, n1, n2, sp2, sq2, sr2);
+ }
+ else {
+ ans = itt_canon1(p1, q1, r1, p2, r2, q2, n1, n2, sp2, sr2, sq2);
+ }
+ }
+ else {
+ if (sq1 < 0) {
+ if (sr1 >= 0) {
+ ans = itt_canon1(q1, r1, p1, p2, r2, q2, n1, n2, sp2, sr2, sq2);
+ }
+ else {
+ ans = itt_canon1(p1, q1, r1, p2, q2, r2, n1, n2, sp2, sq2, sr2);
+ }
+ }
+ else if (sq1 > 0) {
+ if (sr1 > 0) {
+ ans = itt_canon1(p1, q1, r1, p2, r2, q2, n1, n2, sp2, sr2, sq2);
+ }
+ else {
+ ans = itt_canon1(q1, r1, p1, p2, q2, r2, n1, n2, sp2, sq2, sr2);
+ }
+ }
+ else {
+ if (sr1 > 0) {
+ ans = itt_canon1(r1, p1, q1, p2, q2, r2, n1, n2, sp2, sq2, sr2);
+ }
+ else if (sr1 < 0) {
+ ans = itt_canon1(r1, p1, q1, p2, r2, q2, n1, n2, sp2, sr2, sq2);
+ }
+ else {
+ if (dbg_level > 0) {
+ std::cout << "triangles are co-planar\n";
+ }
+ ans = ITT_value(ICOPLANAR);
+ }
+ }
+ }
+ if (ans.kind == ICOPLANAR) {
+ ans.t_source = t2;
+ }
+
+# ifdef PERFDEBUG
+ if (ans.kind != INONE) {
+ incperfcount(4);
+ }
+# endif
+ return ans;
+}
+
+struct CDT_data {
+ const Plane *t_plane;
+ Vector<mpq2> vert;
+ Vector<std::pair<int, int>> edge;
+ Vector<Vector<int>> face;
+ /** Parallels face, gives id from input #IMesh of input face. */
+ Vector<int> input_face;
+ /** Parallels face, says if input face orientation is opposite. */
+ Vector<bool> is_reversed;
+ /** Result of running CDT on input with (vert, edge, face). */
+ CDT_result<mpq_class> cdt_out;
+ int proj_axis;
+};
+
+/**
+ * We could de-duplicate verts here, but CDT routine will do that anyway.
+ */
+static int prepare_need_vert(CDT_data &cd, const mpq3 &p3d)
+{
+ mpq2 p2d = project_3d_to_2d(p3d, cd.proj_axis);
+ int v = cd.vert.append_and_get_index(p2d);
+ return v;
+}
+
+/**
+ * To un-project a 2d vert that was projected along cd.proj_axis, we copy the coordinates
+ * from the two axes not involved in the projection, and use the plane equation of the
+ * originating 3d plane, cd.t_plane, to derive the coordinate of the projected axis.
+ * The plane equation says a point p is on the plane if dot(p, plane.n()) + plane.d() == 0.
+ * Assume that the projection axis is such that plane.n()[proj_axis] != 0.
+ */
+static mpq3 unproject_cdt_vert(const CDT_data &cd, const mpq2 &p2d)
+{
+ mpq3 p3d;
+ BLI_assert(cd.t_plane->exact_populated());
+ BLI_assert(cd.t_plane->norm_exact[cd.proj_axis] != 0);
+ const mpq3 &n = cd.t_plane->norm_exact;
+ const mpq_class &d = cd.t_plane->d_exact;
+ switch (cd.proj_axis) {
+ case (0): {
+ mpq_class num = n[1] * p2d[0] + n[2] * p2d[1] + d;
+ num = -num;
+ p3d[0] = num / n[0];
+ p3d[1] = p2d[0];
+ p3d[2] = p2d[1];
+ break;
+ }
+ case (1): {
+ p3d[0] = p2d[0];
+ mpq_class num = n[0] * p2d[0] + n[2] * p2d[1] + d;
+ num = -num;
+ p3d[1] = num / n[1];
+ p3d[2] = p2d[1];
+ break;
+ }
+ case (2): {
+ p3d[0] = p2d[0];
+ p3d[1] = p2d[1];
+ mpq_class num = n[0] * p2d[0] + n[1] * p2d[1] + d;
+ num = -num;
+ p3d[2] = num / n[2];
+ break;
+ }
+ default:
+ BLI_assert(false);
+ }
+ return p3d;
+}
+
+static void prepare_need_edge(CDT_data &cd, const mpq3 &p1, const mpq3 &p2)
+{
+ int v1 = prepare_need_vert(cd, p1);
+ int v2 = prepare_need_vert(cd, p2);
+ cd.edge.append(std::pair<int, int>(v1, v2));
+}
+
+static void prepare_need_tri(CDT_data &cd, const IMesh &tm, int t)
+{
+ const Face &tri = *tm.face(t);
+ int v0 = prepare_need_vert(cd, tri[0]->co_exact);
+ int v1 = prepare_need_vert(cd, tri[1]->co_exact);
+ int v2 = prepare_need_vert(cd, tri[2]->co_exact);
+ bool rev;
+ /* How to get CCW orientation of projected triangle? Note that when look down y axis
+ * as opposed to x or z, the orientation of the other two axes is not right-and-up. */
+ BLI_assert(cd.t_plane->exact_populated());
+ if (tri.plane->norm_exact[cd.proj_axis] >= 0) {
+ rev = cd.proj_axis == 1;
+ }
+ else {
+ rev = cd.proj_axis != 1;
+ }
+ int cd_t = cd.face.append_and_get_index(Vector<int>());
+ cd.face[cd_t].append(v0);
+ if (rev) {
+ cd.face[cd_t].append(v2);
+ cd.face[cd_t].append(v1);
+ }
+ else {
+ cd.face[cd_t].append(v1);
+ cd.face[cd_t].append(v2);
+ }
+ cd.input_face.append(t);
+ cd.is_reversed.append(rev);
+}
+
+static CDT_data prepare_cdt_input(const IMesh &tm, int t, const Vector<ITT_value> itts)
+{
+ CDT_data ans;
+ BLI_assert(tm.face(t)->plane_populated());
+ ans.t_plane = tm.face(t)->plane;
+ BLI_assert(ans.t_plane->exact_populated());
+ ans.proj_axis = mpq3::dominant_axis(ans.t_plane->norm_exact);
+ prepare_need_tri(ans, tm, t);
+ for (const ITT_value &itt : itts) {
+ switch (itt.kind) {
+ case INONE:
+ break;
+ case IPOINT: {
+ prepare_need_vert(ans, itt.p1);
+ break;
+ }
+ case ISEGMENT: {
+ prepare_need_edge(ans, itt.p1, itt.p2);
+ break;
+ }
+ case ICOPLANAR: {
+ prepare_need_tri(ans, tm, itt.t_source);
+ break;
+ }
+ }
+ }
+ return ans;
+}
+
+static CDT_data prepare_cdt_input_for_cluster(const IMesh &tm,
+ const CoplanarClusterInfo &clinfo,
+ int c,
+ const Vector<ITT_value> itts)
+{
+ CDT_data ans;
+ BLI_assert(c < clinfo.tot_cluster());
+ const CoplanarCluster &cl = clinfo.cluster(c);
+ BLI_assert(cl.tot_tri() > 0);
+ int t0 = cl.tri(0);
+ BLI_assert(tm.face(t0)->plane_populated());
+ ans.t_plane = tm.face(t0)->plane;
+ BLI_assert(ans.t_plane->exact_populated());
+ ans.proj_axis = mpq3::dominant_axis(ans.t_plane->norm_exact);
+ for (const int t : cl) {
+ prepare_need_tri(ans, tm, t);
+ }
+ for (const ITT_value &itt : itts) {
+ switch (itt.kind) {
+ case IPOINT: {
+ prepare_need_vert(ans, itt.p1);
+ break;
+ }
+ case ISEGMENT: {
+ prepare_need_edge(ans, itt.p1, itt.p2);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ return ans;
+}
+
+/**
+ * Fills in cd.cdt_out with result of doing the cdt calculation on (vert, edge, face).
+ */
+static void do_cdt(CDT_data &cd)
+{
+ constexpr int dbg_level = 0;
+ CDT_input<mpq_class> cdt_in;
+ cdt_in.vert = Span<mpq2>(cd.vert);
+ cdt_in.edge = Span<std::pair<int, int>>(cd.edge);
+ cdt_in.face = Span<Vector<int>>(cd.face);
+ if (dbg_level > 0) {
+ std::cout << "CDT input\nVerts:\n";
+ for (int i : cdt_in.vert.index_range()) {
+ std::cout << "v" << i << ": " << cdt_in.vert[i] << "=(" << cdt_in.vert[i][0].get_d() << ","
+ << cdt_in.vert[i][1].get_d() << ")\n";
+ }
+ std::cout << "Edges:\n";
+ for (int i : cdt_in.edge.index_range()) {
+ std::cout << "e" << i << ": (" << cdt_in.edge[i].first << ", " << cdt_in.edge[i].second
+ << ")\n";
+ }
+ std::cout << "Tris\n";
+ for (int f : cdt_in.face.index_range()) {
+ std::cout << "f" << f << ": ";
+ for (int j : cdt_in.face[f].index_range()) {
+ std::cout << cdt_in.face[f][j] << " ";
+ }
+ std::cout << "\n";
+ }
+ }
+ cdt_in.epsilon = 0; /* TODO: needs attention for non-exact T. */
+ cd.cdt_out = blender::meshintersect::delaunay_2d_calc(cdt_in, CDT_INSIDE);
+ if (dbg_level > 0) {
+ std::cout << "\nCDT result\nVerts:\n";
+ for (int i : cd.cdt_out.vert.index_range()) {
+ std::cout << "v" << i << ": " << cd.cdt_out.vert[i] << "=(" << cd.cdt_out.vert[i][0].get_d()
+ << "," << cd.cdt_out.vert[i][1].get_d() << "\n";
+ }
+ std::cout << "Tris\n";
+ for (int f : cd.cdt_out.face.index_range()) {
+ std::cout << "f" << f << ": ";
+ for (int j : cd.cdt_out.face[f].index_range()) {
+ std::cout << cd.cdt_out.face[f][j] << " ";
+ }
+ std::cout << "orig: ";
+ for (int j : cd.cdt_out.face_orig[f].index_range()) {
+ std::cout << cd.cdt_out.face_orig[f][j] << " ";
+ }
+ std::cout << "\n";
+ }
+ std::cout << "Edges\n";
+ for (int e : cd.cdt_out.edge.index_range()) {
+ std::cout << "e" << e << ": (" << cd.cdt_out.edge[e].first << ", "
+ << cd.cdt_out.edge[e].second << ") ";
+ std::cout << "orig: ";
+ for (int j : cd.cdt_out.edge_orig[e].index_range()) {
+ std::cout << cd.cdt_out.edge_orig[e][j] << " ";
+ }
+ std::cout << "\n";
+ }
+ }
+}
+
+static int get_cdt_edge_orig(
+ int i0, int i1, const CDT_data &cd, const IMesh &in_tm, bool *r_is_intersect)
+{
+ int foff = cd.cdt_out.face_edge_offset;
+ *r_is_intersect = false;
+ for (int e : cd.cdt_out.edge.index_range()) {
+ std::pair<int, int> edge = cd.cdt_out.edge[e];
+ if ((edge.first == i0 && edge.second == i1) || (edge.first == i1 && edge.second == i0)) {
+ /* Pick an arbitrary orig, but not one equal to NO_INDEX, if we can help it. */
+ /* TODO: if edge has origs from more than on part of the nary input,
+ * then want to set *r_is_intersect to true. */
+ for (int orig_index : cd.cdt_out.edge_orig[e]) {
+ /* orig_index encodes the triangle and pos within the triangle of the input edge. */
+ if (orig_index >= foff) {
+ int in_face_index = (orig_index / foff) - 1;
+ int pos = orig_index % foff;
+ /* We need to retrieve the edge orig field from the Face used to populate the
+ * in_face_index'th face of the CDT, at the pos'th position of the face. */
+ int in_tm_face_index = cd.input_face[in_face_index];
+ BLI_assert(in_tm_face_index < in_tm.face_size());
+ const Face *facep = in_tm.face(in_tm_face_index);
+ BLI_assert(pos < facep->size());
+ bool is_rev = cd.is_reversed[in_face_index];
+ int eorig = is_rev ? facep->edge_orig[2 - pos] : facep->edge_orig[pos];
+ if (eorig != NO_INDEX) {
+ return eorig;
+ }
+ }
+ else {
+ /* This edge came from an edge input to the CDT problem,
+ * so it is an intersect edge. */
+ *r_is_intersect = true;
+ /* TODO: maybe there is an orig index:
+ * This happens if an input edge was formed by an input face having
+ * an edge that is co-planar with the cluster, while the face as a whole is not. */
+ return NO_INDEX;
+ }
+ }
+ return NO_INDEX;
+ }
+ }
+ return NO_INDEX;
+}
+
+/**
+ * Using the result of CDT in cd.cdt_out, extract an #IMesh representing the subdivision
+ * of input triangle t, which should be an element of cd.input_face.
+ */
+static IMesh extract_subdivided_tri(const CDT_data &cd,
+ const IMesh &in_tm,
+ int t,
+ IMeshArena *arena)
+{
+ const CDT_result<mpq_class> &cdt_out = cd.cdt_out;
+ int t_in_cdt = -1;
+ for (int i = 0; i < cd.input_face.size(); ++i) {
+ if (cd.input_face[i] == t) {
+ t_in_cdt = i;
+ }
+ }
+ if (t_in_cdt == -1) {
+ std::cout << "Could not find " << t << " in cdt input tris\n";
+ BLI_assert(false);
+ return IMesh();
+ }
+ int t_orig = in_tm.face(t)->orig;
+ constexpr int inline_buf_size = 20;
+ Vector<Face *, inline_buf_size> faces;
+ for (int f : cdt_out.face.index_range()) {
+ if (cdt_out.face_orig[f].contains(t_in_cdt)) {
+ BLI_assert(cdt_out.face[f].size() == 3);
+ int i0 = cdt_out.face[f][0];
+ int i1 = cdt_out.face[f][1];
+ int i2 = cdt_out.face[f][2];
+ mpq3 v0co = unproject_cdt_vert(cd, cdt_out.vert[i0]);
+ mpq3 v1co = unproject_cdt_vert(cd, cdt_out.vert[i1]);
+ mpq3 v2co = unproject_cdt_vert(cd, cdt_out.vert[i2]);
+ /* No need to provide an original index: if coord matches
+ * an original one, then it will already be in the arena
+ * with the correct orig field. */
+ const Vert *v0 = arena->add_or_find_vert(v0co, NO_INDEX);
+ const Vert *v1 = arena->add_or_find_vert(v1co, NO_INDEX);
+ const Vert *v2 = arena->add_or_find_vert(v2co, NO_INDEX);
+ Face *facep;
+ bool is_isect0;
+ bool is_isect1;
+ bool is_isect2;
+ if (cd.is_reversed[t_in_cdt]) {
+ int oe0 = get_cdt_edge_orig(i0, i2, cd, in_tm, &is_isect0);
+ int oe1 = get_cdt_edge_orig(i2, i1, cd, in_tm, &is_isect1);
+ int oe2 = get_cdt_edge_orig(i1, i0, cd, in_tm, &is_isect2);
+ facep = arena->add_face(
+ {v0, v2, v1}, t_orig, {oe0, oe1, oe2}, {is_isect0, is_isect1, is_isect2});
+ }
+ else {
+ int oe0 = get_cdt_edge_orig(i0, i1, cd, in_tm, &is_isect0);
+ int oe1 = get_cdt_edge_orig(i1, i2, cd, in_tm, &is_isect1);
+ int oe2 = get_cdt_edge_orig(i2, i0, cd, in_tm, &is_isect2);
+ facep = arena->add_face(
+ {v0, v1, v2}, t_orig, {oe0, oe1, oe2}, {is_isect0, is_isect1, is_isect2});
+ }
+ facep->populate_plane(false);
+ faces.append(facep);
+ }
+ }
+ return IMesh(faces);
+}
+
+static IMesh extract_single_tri(const IMesh &tm, int t)
+{
+ Face *f = tm.face(t);
+ return IMesh({f});
+}
+
+static bool bvhtreeverlap_cmp(const BVHTreeOverlap &a, const BVHTreeOverlap &b)
+{
+ if (a.indexA < b.indexA) {
+ return true;
+ }
+ if ((a.indexA == b.indexA) & (a.indexB < b.indexB)) {
+ return true;
+ }
+ return false;
+}
+class TriOverlaps {
+ BVHTree *tree_{nullptr};
+ BVHTree *tree_b_{nullptr};
+ BVHTreeOverlap *overlap_{nullptr};
+ uint overlap_tot_{0};
+
+ struct CBData {
+ const IMesh &tm;
+ std::function<int(int)> shape_fn;
+ int nshapes;
+ bool use_self;
+ };
+
+ public:
+ TriOverlaps(const IMesh &tm,
+ const Array<BoundingBox> &tri_bb,
+ int nshapes,
+ std::function<int(int)> shape_fn,
+ bool use_self)
+ {
+ constexpr int dbg_level = 0;
+ if (dbg_level > 0) {
+ std::cout << "TriOverlaps construction\n";
+ }
+ /* Tree type is 8 => octtree; axis = 6 => using XYZ axes only. */
+ tree_ = BLI_bvhtree_new(tm.face_size(), FLT_EPSILON, 8, 6);
+ /* In the common case of a binary boolean and no self intersection in
+ * each shape, we will use two trees and simple bounding box overlap. */
+ bool two_trees_no_self = nshapes == 2 && !use_self;
+ if (two_trees_no_self) {
+ tree_b_ = BLI_bvhtree_new(tm.face_size(), FLT_EPSILON, 8, 6);
+ }
+ float bbpts[6];
+ for (int t : tm.face_index_range()) {
+ const BoundingBox &bb = tri_bb[t];
+ copy_v3_v3(bbpts, bb.min);
+ copy_v3_v3(bbpts + 3, bb.max);
+ int shape = shape_fn(tm.face(t)->orig);
+ if (two_trees_no_self) {
+ if (shape == 0) {
+ BLI_bvhtree_insert(tree_, t, bbpts, 2);
+ }
+ else if (shape == 1) {
+ BLI_bvhtree_insert(tree_b_, t, bbpts, 2);
+ }
+ }
+ else {
+ if (shape != -1) {
+ BLI_bvhtree_insert(tree_, t, bbpts, 2);
+ }
+ }
+ }
+ BLI_bvhtree_balance(tree_);
+ if (two_trees_no_self) {
+ BLI_bvhtree_balance(tree_b_);
+ /* Don't expect a lot of trivial intersects in this case. */
+ overlap_ = BLI_bvhtree_overlap(tree_, tree_b_, &overlap_tot_, NULL, NULL);
+ }
+ else {
+ CBData cbdata{tm, shape_fn, nshapes, use_self};
+ if (nshapes == 1 && use_self) {
+ /* Expect a lot of trivial intersects from quads that are triangulated
+ * and faces that share vertices.
+ * Filter them out with a callback. */
+ overlap_ = BLI_bvhtree_overlap(
+ tree_, tree_, &overlap_tot_, only_nontrivial_intersects, &cbdata);
+ }
+ else {
+ overlap_ = BLI_bvhtree_overlap(
+ tree_, tree_, &overlap_tot_, only_different_shapes, &cbdata);
+ }
+ }
+ /* The rest of the code is simpler and easier to parallelize if, in the two-trees case,
+ * we repeat the overlaps with indexA and indexB reversed. It is important that
+ * in the repeated part, sorting will then bring things with indexB together. */
+ if (two_trees_no_self) {
+ overlap_ = static_cast<BVHTreeOverlap *>(
+ MEM_reallocN(overlap_, 2 * overlap_tot_ * sizeof(overlap_[0])));
+ for (uint i = 0; i < overlap_tot_; ++i) {
+ overlap_[overlap_tot_ + i].indexA = overlap_[i].indexB;
+ overlap_[overlap_tot_ + i].indexB = overlap_[i].indexA;
+ }
+ overlap_tot_ += overlap_tot_;
+ }
+ /* Sort the overlaps to bring all the intersects with a given indexA together. */
+ std::sort(overlap_, overlap_ + overlap_tot_, bvhtreeverlap_cmp);
+ if (dbg_level > 0) {
+ std::cout << overlap_tot_ << " overlaps found:\n";
+ for (BVHTreeOverlap ov : overlap()) {
+ std::cout << "A: " << ov.indexA << ", B: " << ov.indexB << "\n";
+ }
+ }
+ }
+
+ ~TriOverlaps()
+ {
+ if (tree_) {
+ BLI_bvhtree_free(tree_);
+ }
+ if (tree_b_) {
+ BLI_bvhtree_free(tree_b_);
+ }
+ if (overlap_) {
+ MEM_freeN(overlap_);
+ }
+ }
+
+ Span<BVHTreeOverlap> overlap() const
+ {
+ return Span<BVHTreeOverlap>(overlap_, overlap_tot_);
+ }
+
+ private:
+ static bool only_nontrivial_intersects(void *userdata,
+ int index_a,
+ int index_b,
+ int UNUSED(thread))
+ {
+ CBData *cbdata = static_cast<CBData *>(userdata);
+ return may_non_trivially_intersect(cbdata->tm.face(index_a), cbdata->tm.face(index_b));
+ }
+
+ static bool only_different_shapes(void *userdata, int index_a, int index_b, int UNUSED(thread))
+ {
+ CBData *cbdata = static_cast<CBData *>(userdata);
+ return cbdata->tm.face(index_a)->orig != cbdata->tm.face(index_b)->orig;
+ }
+};
+
+/**
+ * Data needed for parallelization of #calc_overlap_itts.
+ */
+struct OverlapIttsData {
+ Vector<std::pair<int, int>> intersect_pairs;
+ Map<std::pair<int, int>, ITT_value> &itt_map;
+ const IMesh &tm;
+ IMeshArena *arena;
+
+ OverlapIttsData(Map<std::pair<int, int>, ITT_value> &itt_map, const IMesh &tm, IMeshArena *arena)
+ : itt_map(itt_map), tm(tm), arena(arena)
+ {
+ }
+};
+
+/**
+ * Return a std::pair containing a and b in canonical order:
+ * With a <= b.
+ */
+static std::pair<int, int> canon_int_pair(int a, int b)
+{
+ if (a > b) {
+ std::swap(a, b);
+ }
+ return std::pair<int, int>(a, b);
+}
+
+static void calc_overlap_itts_range_func(void *__restrict userdata,
+ const int iter,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ constexpr int dbg_level = 0;
+ OverlapIttsData *data = static_cast<OverlapIttsData *>(userdata);
+ std::pair<int, int> tri_pair = data->intersect_pairs[iter];
+ int a = tri_pair.first;
+ int b = tri_pair.second;
+ if (dbg_level > 0) {
+ std::cout << "calc_overlap_itts_range_func a=" << a << ", b=" << b << "\n";
+ }
+ ITT_value itt = intersect_tri_tri(data->tm, a, b);
+ if (dbg_level > 0) {
+ std::cout << "result of intersecting " << a << " and " << b << " = " << itt << "\n";
+ }
+ BLI_assert(data->itt_map.contains(tri_pair));
+ data->itt_map.add_overwrite(tri_pair, itt);
+}
+
+/**
+ * Fill in itt_map with the vector of ITT_values that result from intersecting the triangles in ov.
+ * Use a canonical order for triangles: (a,b) where a < b.
+ * Don't bother doing this if both a and b are part of the same co-planar cluster, as the
+ * intersection for those will be handled by CDT, later.
+ */
+static void calc_overlap_itts(Map<std::pair<int, int>, ITT_value> &itt_map,
+ const IMesh &tm,
+ const CoplanarClusterInfo &clinfo,
+ const TriOverlaps &ov,
+ IMeshArena *arena)
+{
+ OverlapIttsData data(itt_map, tm, arena);
+ /* Put dummy values in `itt_map` initially,
+ * so map entries will exist when doing the range function.
+ * This means we won't have to protect the `itt_map.add_overwrite` function with a lock. */
+ for (const BVHTreeOverlap &olap : ov.overlap()) {
+ std::pair<int, int> key = canon_int_pair(olap.indexA, olap.indexB);
+ if (!itt_map.contains(key)) {
+ int ca = clinfo.tri_cluster(key.first);
+ int cb = clinfo.tri_cluster(key.second);
+ if (ca == NO_INDEX || ca != cb) {
+ itt_map.add_new(key, ITT_value());
+ data.intersect_pairs.append(key);
+ }
+ }
+ }
+ int tot_intersect_pairs = data.intersect_pairs.size();
+ TaskParallelSettings settings;
+ BLI_parallel_range_settings_defaults(&settings);
+ settings.min_iter_per_thread = 1000;
+ settings.use_threading = intersect_use_threading;
+ BLI_task_parallel_range(0, tot_intersect_pairs, &data, calc_overlap_itts_range_func, &settings);
+}
+
+/**
+ * Data needed for parallelization of calc_subdivided_tris.
+ */
+struct OverlapTriRange {
+ int tri_index;
+ int overlap_start;
+ int len;
+};
+struct SubdivideTrisData {
+ Array<IMesh> &r_tri_subdivided;
+ const IMesh &tm;
+ const Map<std::pair<int, int>, ITT_value> &itt_map;
+ Span<BVHTreeOverlap> overlap;
+ IMeshArena *arena;
+
+ /* This vector gives, for each triangle in tm that has an intersection
+ * we want to calculate: what the index of that triangle in tm is,
+ * where it starts in the ov structure as indexA, and how many
+ * overlap pairs have that same indexA (they will be continuous). */
+ Vector<OverlapTriRange> overlap_tri_range;
+
+ SubdivideTrisData(Array<IMesh> &r_tri_subdivided,
+ const IMesh &tm,
+ const Map<std::pair<int, int>, ITT_value> &itt_map,
+ Span<BVHTreeOverlap> overlap,
+ IMeshArena *arena)
+ : r_tri_subdivided(r_tri_subdivided),
+ tm(tm),
+ itt_map(itt_map),
+ overlap(overlap),
+ arena(arena),
+ overlap_tri_range{}
+ {
+ }
+};
+
+static void calc_subdivided_tri_range_func(void *__restrict userdata,
+ const int iter,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ constexpr int dbg_level = 0;
+ SubdivideTrisData *data = static_cast<SubdivideTrisData *>(userdata);
+ OverlapTriRange &otr = data->overlap_tri_range[iter];
+ int t = otr.tri_index;
+ if (dbg_level > 0) {
+ std::cout << "calc_subdivided_tri_range_func\nt=" << t << " start=" << otr.overlap_start
+ << " len=" << otr.len << "\n";
+ }
+ constexpr int inline_capacity = 100;
+ Vector<ITT_value, inline_capacity> itts(otr.len);
+ for (int j = otr.overlap_start; j < otr.overlap_start + otr.len; ++j) {
+ int t_other = data->overlap[j].indexB;
+ std::pair<int, int> key = canon_int_pair(t, t_other);
+ ITT_value itt;
+ if (data->itt_map.contains(key)) {
+ itt = data->itt_map.lookup(key);
+ }
+ if (itt.kind != INONE) {
+ itts.append(itt);
+ }
+ if (dbg_level > 0) {
+ std::cout << " tri t" << t_other << "; result = " << itt << "\n";
+ }
+ }
+ if (itts.size() > 0) {
+ CDT_data cd_data = prepare_cdt_input(data->tm, t, itts);
+ do_cdt(cd_data);
+ data->r_tri_subdivided[t] = extract_subdivided_tri(cd_data, data->tm, t, data->arena);
+ if (dbg_level > 0) {
+ std::cout << "subdivide output\n" << data->r_tri_subdivided[t];
+ }
+ }
+}
+
+/**
+ * For each triangle in tm, fill in the corresponding slot in
+ * r_tri_subdivided with the result of intersecting it with
+ * all the other triangles in the mesh, if it intersects any others.
+ * But don't do this for triangles that are part of a cluster.
+ * Also, do nothing here if the answer is just the triangle itself.
+ */
+static void calc_subdivided_tris(Array<IMesh> &r_tri_subdivided,
+ const IMesh &tm,
+ const Map<std::pair<int, int>, ITT_value> &itt_map,
+ const CoplanarClusterInfo &clinfo,
+ const TriOverlaps &ov,
+ IMeshArena *arena)
+{
+ const int dbg_level = 0;
+ if (dbg_level > 0) {
+ std::cout << "\nCALC_SUBDIVIDED_TRIS\n\n";
+ }
+ Span<BVHTreeOverlap> overlap = ov.overlap();
+ SubdivideTrisData data(r_tri_subdivided, tm, itt_map, overlap, arena);
+ int overlap_tot = overlap.size();
+ data.overlap_tri_range = Vector<OverlapTriRange>();
+ data.overlap_tri_range.reserve(overlap_tot);
+ int overlap_index = 0;
+ while (overlap_index < overlap_tot) {
+ int t = overlap[overlap_index].indexA;
+ int i = overlap_index;
+ while (i + 1 < overlap_tot && overlap[i + 1].indexA == t) {
+ ++i;
+ }
+ /* Now overlap[overlap_index] to overlap[i] have indexA == t.
+ * We only record ranges for triangles that are not in clusters,
+ * because the ones in clusters are handled separately. */
+ if (clinfo.tri_cluster(t) == NO_INDEX) {
+ int len = i - overlap_index + 1;
+ if (!(len == 1 && overlap[overlap_index].indexB == t)) {
+ OverlapTriRange range = {t, overlap_index, len};
+ data.overlap_tri_range.append(range);
+# ifdef PERFDEBUG
+ bumpperfcount(0, len); /* Non-cluster overlaps. */
+# endif
+ }
+ }
+ overlap_index = i + 1;
+ }
+ int overlap_tri_range_tot = data.overlap_tri_range.size();
+ TaskParallelSettings settings;
+ BLI_parallel_range_settings_defaults(&settings);
+ settings.min_iter_per_thread = 50;
+ settings.use_threading = intersect_use_threading;
+ BLI_task_parallel_range(
+ 0, overlap_tri_range_tot, &data, calc_subdivided_tri_range_func, &settings);
+}
+
+/* Get first index in ov where indexA == t. Assuming sorted on indexA. */
+static int find_first_overlap_index(const TriOverlaps &ov, int t)
+{
+ Span<BVHTreeOverlap> span = ov.overlap();
+ if (span.size() == 0) {
+ return -1;
+ }
+ BVHTreeOverlap bo{t, -1};
+ const BVHTreeOverlap *p = std::lower_bound(
+ span.begin(), span.end(), bo, [](const BVHTreeOverlap &o1, const BVHTreeOverlap &o2) {
+ return o1.indexA < o2.indexA;
+ });
+ if (p != span.end()) {
+ return p - span.begin();
+ }
+ return -1;
+}
+
+static CDT_data calc_cluster_subdivided(const CoplanarClusterInfo &clinfo,
+ int c,
+ const IMesh &tm,
+ const TriOverlaps &ov,
+ const Map<std::pair<int, int>, ITT_value> &itt_map,
+ IMeshArena *UNUSED(arena))
+{
+ constexpr int dbg_level = 0;
+ BLI_assert(c < clinfo.tot_cluster());
+ const CoplanarCluster &cl = clinfo.cluster(c);
+ /* Make a CDT input with triangles from C and intersects from other triangles in tm. */
+ if (dbg_level > 0) {
+ std::cout << "CALC_CLUSTER_SUBDIVIDED for cluster " << c << " = " << cl << "\n";
+ }
+ /* Get vector itts of all intersections of a triangle of cl with any triangle of tm not
+ * in cl and not co-planar with it (for that latter, if there were an intersection,
+ * it should already be in cluster cl). */
+ Vector<ITT_value> itts;
+ Span<BVHTreeOverlap> ovspan = ov.overlap();
+ for (int t : cl) {
+ if (dbg_level > 0) {
+ std::cout << "find intersects with triangle " << t << " of cluster\n";
+ }
+ int first_i = find_first_overlap_index(ov, t);
+ if (first_i == -1) {
+ continue;
+ }
+ for (int i = first_i; i < ovspan.size() && ovspan[i].indexA == t; ++i) {
+ int t_other = ovspan[i].indexB;
+ if (clinfo.tri_cluster(t_other) != c) {
+ if (dbg_level > 0) {
+ std::cout << "use intersect(" << t << "," << t_other << "\n";
+ }
+ std::pair<int, int> key = canon_int_pair(t, t_other);
+ if (itt_map.contains(key)) {
+ ITT_value itt = itt_map.lookup(key);
+ if (itt.kind != INONE && itt.kind != ICOPLANAR) {
+ itts.append(itt);
+ if (dbg_level > 0) {
+ std::cout << " itt = " << itt << "\n";
+ }
+ }
+ }
+ }
+ }
+ }
+ /* Use CDT to subdivide the cluster triangles and the points and segs in itts. */
+ CDT_data cd_data = prepare_cdt_input_for_cluster(tm, clinfo, c, itts);
+ do_cdt(cd_data);
+ return cd_data;
+}
+
+static IMesh union_tri_subdivides(const blender::Array<IMesh> &tri_subdivided)
+{
+ int tot_tri = 0;
+ for (const IMesh &m : tri_subdivided) {
+ tot_tri += m.face_size();
+ }
+ Array<Face *> faces(tot_tri);
+ int face_index = 0;
+ for (const IMesh &m : tri_subdivided) {
+ for (Face *f : m.faces()) {
+ faces[face_index++] = f;
+ }
+ }
+ return IMesh(faces);
+}
+
+static CoplanarClusterInfo find_clusters(const IMesh &tm, const Array<BoundingBox> &tri_bb)
+{
+ constexpr int dbg_level = 0;
+ if (dbg_level > 0) {
+ std::cout << "FIND_CLUSTERS\n";
+ }
+ CoplanarClusterInfo ans(tm.face_size());
+ /* There can be more than one #CoplanarCluster per plane. Accumulate them in
+ * a Vector. We will have to merge some elements of the Vector as we discover
+ * triangles that form intersection bridges between two or more clusters. */
+ Map<Plane, Vector<CoplanarCluster>> plane_cls;
+ plane_cls.reserve(tm.face_size());
+ for (int t : tm.face_index_range()) {
+ /* Use a canonical version of the plane for map index.
+ * We can't just store the canonical version in the face
+ * since canonicalizing loses the orientation of the normal. */
+ Plane tplane = *tm.face(t)->plane;
+ BLI_assert(tplane.exact_populated());
+ tplane.make_canonical();
+ if (dbg_level > 0) {
+ std::cout << "plane for tri " << t << " = " << &tplane << "\n";
+ }
+ /* Assume all planes are in canonical from (see canon_plane()). */
+ if (plane_cls.contains(tplane)) {
+ Vector<CoplanarCluster> &curcls = plane_cls.lookup(tplane);
+ if (dbg_level > 0) {
+ std::cout << "already has " << curcls.size() << " clusters\n";
+ }
+ int proj_axis = mpq3::dominant_axis(tplane.norm_exact);
+ /* Partition `curcls` into those that intersect t non-trivially, and those that don't. */
+ Vector<CoplanarCluster *> int_cls;
+ Vector<CoplanarCluster *> no_int_cls;
+ for (CoplanarCluster &cl : curcls) {
+ if (bbs_might_intersect(tri_bb[t], cl.bounding_box()) &&
+ non_trivially_coplanar_intersects(tm, t, cl, proj_axis)) {
+ if (dbg_level > 1) {
+ std::cout << "append to int_cls\n";
+ }
+ int_cls.append(&cl);
+ }
+ else {
+ if (dbg_level > 1) {
+ std::cout << "append to no_int_cls\n";
+ }
+ no_int_cls.append(&cl);
+ }
+ }
+ if (int_cls.size() == 0) {
+ /* t doesn't intersect any existing cluster in its plane, so make one just for it. */
+ if (dbg_level > 1) {
+ std::cout << "no intersecting clusters for t, make a new one\n";
+ }
+ curcls.append(CoplanarCluster(t, tri_bb[t]));
+ }
+ else if (int_cls.size() == 1) {
+ /* t intersects exactly one existing cluster, so can add t to that cluster. */
+ if (dbg_level > 1) {
+ std::cout << "exactly one existing cluster, " << int_cls[0] << ", adding to it\n";
+ }
+ int_cls[0]->add_tri(t, tri_bb[t]);
+ }
+ else {
+ /* t intersections 2 or more existing clusters: need to merge them and replace all the
+ * originals with the merged one in `curcls`. */
+ if (dbg_level > 1) {
+ std::cout << "merging\n";
+ }
+ CoplanarCluster mergecl;
+ mergecl.add_tri(t, tri_bb[t]);
+ for (CoplanarCluster *cl : int_cls) {
+ for (int t : *cl) {
+ mergecl.add_tri(t, tri_bb[t]);
+ }
+ }
+ Vector<CoplanarCluster> newvec;
+ newvec.append(mergecl);
+ for (CoplanarCluster *cl_no_int : no_int_cls) {
+ newvec.append(*cl_no_int);
+ }
+ plane_cls.add_overwrite(tplane, newvec);
+ }
+ }
+ else {
+ if (dbg_level > 0) {
+ std::cout << "first cluster for its plane\n";
+ }
+ plane_cls.add_new(tplane, Vector<CoplanarCluster>{CoplanarCluster(t, tri_bb[t])});
+ }
+ }
+ /* Does this give deterministic order for cluster ids? I think so, since
+ * hash for planes is on their values, not their addresses. */
+ for (auto item : plane_cls.items()) {
+ for (const CoplanarCluster &cl : item.value) {
+ if (cl.tot_tri() > 1) {
+ ans.add_cluster(cl);
+ }
+ }
+ }
+
+ return ans;
+}
+
+static bool face_is_degenerate(const Face *f)
+{
+ const Face &face = *f;
+ const Vert *v0 = face[0];
+ const Vert *v1 = face[1];
+ const Vert *v2 = face[2];
+ if (v0 == v1 || v0 == v2 || v1 == v2) {
+ return true;
+ }
+ double3 da = v2->co - v0->co;
+ double3 db = v2->co - v1->co;
+ double3 dab = double3::cross_high_precision(da, db);
+ double dab_length_squared = dab.length_squared();
+ double err_bound = supremum_dot_cross(dab, dab) * index_dot_cross * DBL_EPSILON;
+ if (dab_length_squared > err_bound) {
+ return false;
+ }
+ mpq3 a = v2->co_exact - v0->co_exact;
+ mpq3 b = v2->co_exact - v1->co_exact;
+ mpq3 ab = mpq3::cross(a, b);
+ if (ab.x == 0 && ab.y == 0 && ab.z == 0) {
+ return true;
+ }
+
+ return false;
+}
+
+/* Data and functions to test triangle degeneracy in parallel. */
+struct DegenData {
+ const IMesh &tm;
+};
+
+struct DegenChunkData {
+ bool has_degenerate_tri = false;
+};
+
+static void degenerate_range_func(void *__restrict userdata,
+ const int iter,
+ const TaskParallelTLS *__restrict tls)
+{
+ DegenData *data = static_cast<DegenData *>(userdata);
+ DegenChunkData *chunk_data = static_cast<DegenChunkData *>(tls->userdata_chunk);
+ const Face *f = data->tm.face(iter);
+ bool is_degenerate = face_is_degenerate(f);
+ chunk_data->has_degenerate_tri |= is_degenerate;
+}
+
+static void degenerate_reduce(const void *__restrict UNUSED(userdata),
+ void *__restrict chunk_join,
+ void *__restrict chunk)
+{
+ DegenChunkData *degen_chunk_join = static_cast<DegenChunkData *>(chunk_join);
+ DegenChunkData *degen_chunk = static_cast<DegenChunkData *>(chunk);
+ degen_chunk_join->has_degenerate_tri |= degen_chunk->has_degenerate_tri;
+}
+
+/* Does triangle #IMesh tm have any triangles with zero area? */
+static bool has_degenerate_tris(const IMesh &tm)
+{
+ DegenData degen_data = {tm};
+ DegenChunkData degen_chunk_data;
+ TaskParallelSettings settings;
+ BLI_parallel_range_settings_defaults(&settings);
+ settings.userdata_chunk = &degen_chunk_data;
+ settings.userdata_chunk_size = sizeof(degen_chunk_data);
+ settings.func_reduce = degenerate_reduce;
+ settings.min_iter_per_thread = 1000;
+ settings.use_threading = intersect_use_threading;
+ BLI_task_parallel_range(0, tm.face_size(), &degen_data, degenerate_range_func, &settings);
+ return degen_chunk_data.has_degenerate_tri;
+}
+
+static IMesh remove_degenerate_tris(const IMesh &tm_in)
+{
+ IMesh ans;
+ Vector<Face *> new_faces;
+ new_faces.reserve(tm_in.face_size());
+ for (Face *f : tm_in.faces()) {
+ if (!face_is_degenerate(f)) {
+ new_faces.append(f);
+ }
+ }
+ ans.set_faces(new_faces);
+ return ans;
+}
+
+/* This is the main routine for calculating the self_intersection of a triangle mesh. */
+IMesh trimesh_self_intersect(const IMesh &tm_in, IMeshArena *arena)
+{
+ return trimesh_nary_intersect(
+ tm_in, 1, [](int UNUSED(t)) { return 0; }, true, arena);
+}
+
+IMesh trimesh_nary_intersect(const IMesh &tm_in,
+ int nshapes,
+ std::function<int(int)> shape_fn,
+ bool use_self,
+ IMeshArena *arena)
+{
+ constexpr int dbg_level = 0;
+ if (dbg_level > 0) {
+ std::cout << "\nTRIMESH_NARY_INTERSECT nshapes=" << nshapes << " use_self=" << use_self
+ << "\n";
+ for (const Face *f : tm_in.faces()) {
+ BLI_assert(f->is_tri());
+ UNUSED_VARS_NDEBUG(f);
+ }
+ if (dbg_level > 1) {
+ std::cout << "input mesh:\n" << tm_in;
+ for (int t : tm_in.face_index_range()) {
+ std::cout << "shape(" << t << ") = " << shape_fn(tm_in.face(t)->orig) << "\n";
+ }
+ }
+ }
+# ifdef PERFDEBUG
+ perfdata_init();
+ double start_time = PIL_check_seconds_timer();
+ std::cout << "trimesh_nary_intersect start\n";
+# endif
+ /* Usually can use tm_in but if it has degenerate or illegal triangles,
+ * then need to work on a copy of it without those triangles. */
+ const IMesh *tm_clean = &tm_in;
+ IMesh tm_cleaned;
+ if (has_degenerate_tris(tm_in)) {
+ if (dbg_level > 0) {
+ std::cout << "cleaning degenerate triangles\n";
+ }
+ tm_cleaned = remove_degenerate_tris(tm_in);
+ tm_clean = &tm_cleaned;
+ if (dbg_level > 1) {
+ std::cout << "cleaned input mesh:\n" << tm_cleaned;
+ }
+ }
+ /* Temporary, while developing: populate all plane normals exactly. */
+ for (Face *f : tm_clean->faces()) {
+ f->populate_plane(true);
+ }
+# ifdef PERFDEBUG
+ double clean_time = PIL_check_seconds_timer();
+ std::cout << "cleaned, time = " << clean_time - start_time << "\n";
+# endif
+ Array<BoundingBox> tri_bb = calc_face_bounding_boxes(*tm_clean);
+# ifdef PERFDEBUG
+ double bb_calc_time = PIL_check_seconds_timer();
+ std::cout << "bbs calculated, time = " << bb_calc_time - clean_time << "\n";
+# endif
+ TriOverlaps tri_ov(*tm_clean, tri_bb, nshapes, shape_fn, use_self);
+# ifdef PERFDEBUG
+ double overlap_time = PIL_check_seconds_timer();
+ std::cout << "intersect overlaps calculated, time = " << overlap_time - bb_calc_time << "\n";
+
+# endif
+ CoplanarClusterInfo clinfo = find_clusters(*tm_clean, tri_bb);
+ if (dbg_level > 1) {
+ std::cout << clinfo;
+ }
+# ifdef PERFDEBUG
+ double find_cluster_time = PIL_check_seconds_timer();
+ std::cout << "clusters found, time = " << find_cluster_time - overlap_time << "\n";
+ doperfmax(0, tm_in.face_size());
+ doperfmax(1, clinfo.tot_cluster());
+ doperfmax(2, tri_ov.overlap().size());
+# endif
+ /* itt_map((a,b)) will hold the intersection value resulting from intersecting
+ * triangles with indices a and b, where a < b. */
+ Map<std::pair<int, int>, ITT_value> itt_map;
+ itt_map.reserve(tri_ov.overlap().size());
+ calc_overlap_itts(itt_map, *tm_clean, clinfo, tri_ov, arena);
+# ifdef PERFDEBUG
+ double itt_time = PIL_check_seconds_timer();
+ std::cout << "itts found, time = " << itt_time - find_cluster_time << "\n";
+# endif
+ Array<IMesh> tri_subdivided(tm_clean->face_size());
+ calc_subdivided_tris(tri_subdivided, *tm_clean, itt_map, clinfo, tri_ov, arena);
+# ifdef PERFDEBUG
+ double subdivided_tris_time = PIL_check_seconds_timer();
+ std::cout << "subdivided tris found, time = " << subdivided_tris_time - itt_time << "\n";
+# endif
+ Array<CDT_data> cluster_subdivided(clinfo.tot_cluster());
+ for (int c : clinfo.index_range()) {
+ cluster_subdivided[c] = calc_cluster_subdivided(clinfo, c, *tm_clean, tri_ov, itt_map, arena);
+ }
+# ifdef PERFDEBUG
+ double cluster_subdivide_time = PIL_check_seconds_timer();
+ std::cout << "subdivided clusters found, time = "
+ << cluster_subdivide_time - subdivided_tris_time << "\n";
+# endif
+ for (int t : tm_clean->face_index_range()) {
+ int c = clinfo.tri_cluster(t);
+ if (c != NO_INDEX) {
+ BLI_assert(tri_subdivided[t].face_size() == 0);
+ tri_subdivided[t] = extract_subdivided_tri(cluster_subdivided[c], *tm_clean, t, arena);
+ }
+ else if (tri_subdivided[t].face_size() == 0) {
+ tri_subdivided[t] = extract_single_tri(*tm_clean, t);
+ }
+ }
+# ifdef PERFDEBUG
+ double extract_time = PIL_check_seconds_timer();
+ std::cout << "triangles extracted, time = " << extract_time - cluster_subdivide_time << "\n";
+# endif
+ IMesh combined = union_tri_subdivides(tri_subdivided);
+ if (dbg_level > 1) {
+ std::cout << "TRIMESH_NARY_INTERSECT answer:\n";
+ std::cout << combined;
+ }
+# ifdef PERFDEBUG
+ double end_time = PIL_check_seconds_timer();
+ std::cout << "triangles combined, time = " << end_time - extract_time << "\n";
+ std::cout << "trimesh_nary_intersect done, total time = " << end_time - start_time << "\n";
+ dump_perfdata();
+# endif
+ return combined;
+}
+
+static std::ostream &operator<<(std::ostream &os, const CoplanarCluster &cl)
+{
+ os << "cl(";
+ bool first = true;
+ for (const int t : cl) {
+ if (first) {
+ first = false;
+ }
+ else {
+ os << ",";
+ }
+ os << t;
+ }
+ os << ")";
+ return os;
+}
+
+static std::ostream &operator<<(std::ostream &os, const CoplanarClusterInfo &clinfo)
+{
+ os << "Coplanar Cluster Info:\n";
+ for (int c : clinfo.index_range()) {
+ os << c << ": " << clinfo.cluster(c) << "\n";
+ }
+ return os;
+}
+
+static std::ostream &operator<<(std::ostream &os, const ITT_value &itt)
+{
+ switch (itt.kind) {
+ case INONE:
+ os << "none";
+ break;
+ case IPOINT:
+ os << "point " << itt.p1;
+ break;
+ case ISEGMENT:
+ os << "segment " << itt.p1 << " " << itt.p2;
+ break;
+ case ICOPLANAR:
+ os << "co-planar t" << itt.t_source;
+ break;
+ }
+ return os;
+}
+
+/**
+ * Writing the obj_mesh has the side effect of populating verts.
+ */
+void write_obj_mesh(IMesh &m, const std::string &objname)
+{
+ /* Would like to use #BKE_tempdir_base() here, but that brings in dependence on kernel library.
+ * This is just for developer debugging anyway,
+ * and should never be called in production Blender. */
+# ifdef _WIN_32
+ const char *objdir = BLI_getenv("HOME");
+# else
+ const char *objdir = "/tmp/";
+# endif
+ if (m.face_size() == 0) {
+ return;
+ }
+
+ std::string fname = std::string(objdir) + objname + std::string(".obj");
+ std::ofstream f;
+ f.open(fname);
+ if (!f) {
+ std::cout << "Could not open file " << fname << "\n";
+ return;
+ }
+
+ if (!m.has_verts()) {
+ m.populate_vert();
+ }
+ for (const Vert *v : m.vertices()) {
+ const double3 dv = v->co;
+ f << "v " << dv[0] << " " << dv[1] << " " << dv[2] << "\n";
+ }
+ int i = 0;
+ for (const Face *face : m.faces()) {
+ /* OBJ files use 1-indexing for vertices. */
+ f << "f ";
+ for (const Vert *v : *face) {
+ int i = m.lookup_vert(v);
+ BLI_assert(i != NO_INDEX);
+ /* OBJ files use 1-indexing for vertices. */
+ f << i + 1 << " ";
+ }
+ f << "\n";
+ ++i;
+ }
+ f.close();
+}
+
+# ifdef PERFDEBUG
+struct PerfCounts {
+ Vector<int> count;
+ Vector<const char *> count_name;
+ Vector<int> max;
+ Vector<const char *> max_name;
+};
+
+static PerfCounts *perfdata = nullptr;
+
+static void perfdata_init(void)
+{
+ perfdata = new PerfCounts;
+
+ /* count 0. */
+ perfdata->count.append(0);
+ perfdata->count_name.append("Non-cluster overlaps");
+
+ /* count 1. */
+ perfdata->count.append(0);
+ perfdata->count_name.append("intersect_tri_tri calls");
+
+ /* count 2. */
+ perfdata->count.append(0);
+ perfdata->count_name.append("tri tri intersects decided by filter plane tests");
+
+ /* count 3. */
+ perfdata->count.append(0);
+ perfdata->count_name.append("tri tri intersects decided by exact plane tests");
+
+ /* count 4. */
+ perfdata->count.append(0);
+ perfdata->count_name.append("final non-NONE intersects");
+
+ /* max 0. */
+ perfdata->max.append(0);
+ perfdata->max_name.append("total faces");
+
+ /* max 1. */
+ perfdata->max.append(0);
+ perfdata->max_name.append("total clusters");
+
+ /* max 2. */
+ perfdata->max.append(0);
+ perfdata->max_name.append("total overlaps");
+}
+
+static void incperfcount(int countnum)
+{
+ perfdata->count[countnum]++;
+}
+
+static void bumpperfcount(int countnum, int amt)
+{
+ perfdata->count[countnum] += amt;
+}
+
+static void doperfmax(int maxnum, int val)
+{
+ perfdata->max[maxnum] = max_ii(perfdata->max[maxnum], val);
+}
+
+static void dump_perfdata(void)
+{
+ std::cout << "\nPERFDATA\n";
+ for (int i : perfdata->count.index_range()) {
+ std::cout << perfdata->count_name[i] << " = " << perfdata->count[i] << "\n";
+ }
+ for (int i : perfdata->max.index_range()) {
+ std::cout << perfdata->max_name[i] << " = " << perfdata->max[i] << "\n";
+ }
+ delete perfdata;
+}
+# endif
+
+}; // namespace blender::meshintersect
+
+#endif // WITH_GMP
diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c
index 67d41ffb779..cde4394a8c3 100644
--- a/source/blender/blenlib/intern/path_util.c
+++ b/source/blender/blenlib/intern/path_util.c
@@ -532,7 +532,7 @@ void BLI_path_rel(char *file, const char *relfile)
char *ptemp;
/* fix missing volume name in relative base,
* can happen with old recent-files.txt files */
- get_default_root(temp);
+ BLI_windows_get_default_root_dir(temp);
ptemp = &temp[2];
if (relfile[0] != '\\' && relfile[0] != '/') {
ptemp++;
@@ -1026,7 +1026,7 @@ bool BLI_path_abs(char *path, const char *basepath)
*/
if (!wasrelative && !BLI_path_is_abs(path)) {
char *p = path;
- get_default_root(tmp);
+ BLI_windows_get_default_root_dir(tmp);
// get rid of the slashes at the beginning of the path
while (ELEM(*p, '\\', '/')) {
p++;
@@ -1385,7 +1385,7 @@ void BLI_make_file_string(const char *relabase, char *string, const char *dir, c
string[3] = '\0';
}
else { /* we're out of luck here, guessing the first valid drive, usually c:\ */
- get_default_root(string);
+ BLI_windows_get_default_root_dir(string);
}
/* ignore leading slashes */
diff --git a/source/blender/blenlib/intern/winstuff.c b/source/blender/blenlib/intern/winstuff.c
index df7e7107d11..333b6783087 100644
--- a/source/blender/blenlib/intern/winstuff.c
+++ b/source/blender/blenlib/intern/winstuff.c
@@ -36,14 +36,12 @@
# include "BLI_utildefines.h"
# include "BLI_winstuff.h"
-# include "../blenkernel/BKE_global.h" /* G.background, bad level include (no function calls) */
-
# include "utf_winfunc.h"
# include "utfconv.h"
/* FILE_MAXDIR + FILE_MAXFILE */
-int BLI_getInstallationDir(char *str)
+int BLI_windows_get_executable_dir(char *str)
{
char dir[FILE_MAXDIR];
int a;
@@ -60,19 +58,19 @@ int BLI_getInstallationDir(char *str)
return 1;
}
-static void RegisterBlendExtension_Fail(HKEY root)
+static void register_blend_extension_failed(HKEY root, const bool background)
{
printf("failed\n");
if (root) {
RegCloseKey(root);
}
- if (!G.background) {
+ if (!background) {
MessageBox(0, "Could not register file extension.", "Blender error", MB_OK | MB_ICONERROR);
}
TerminateProcess(GetCurrentProcess(), 1);
}
-void RegisterBlendExtension(void)
+void BLI_windows_register_blend_extension(const bool background)
{
LONG lresult;
HKEY hkey = 0;
@@ -108,7 +106,7 @@ void RegisterBlendExtension(void)
usr_mode = true;
lresult = RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Classes", 0, KEY_ALL_ACCESS, &root);
if (lresult != ERROR_SUCCESS) {
- RegisterBlendExtension_Fail(0);
+ register_blend_extension_failed(0, background);
}
}
@@ -120,7 +118,7 @@ void RegisterBlendExtension(void)
RegCloseKey(hkey);
}
if (lresult != ERROR_SUCCESS) {
- RegisterBlendExtension_Fail(root);
+ register_blend_extension_failed(root, background);
}
lresult = RegCreateKeyEx(root,
@@ -138,7 +136,7 @@ void RegisterBlendExtension(void)
RegCloseKey(hkey);
}
if (lresult != ERROR_SUCCESS) {
- RegisterBlendExtension_Fail(root);
+ register_blend_extension_failed(root, background);
}
lresult = RegCreateKeyEx(root,
@@ -156,7 +154,7 @@ void RegisterBlendExtension(void)
RegCloseKey(hkey);
}
if (lresult != ERROR_SUCCESS) {
- RegisterBlendExtension_Fail(root);
+ register_blend_extension_failed(root, background);
}
lresult = RegCreateKeyEx(
@@ -167,10 +165,10 @@ void RegisterBlendExtension(void)
RegCloseKey(hkey);
}
if (lresult != ERROR_SUCCESS) {
- RegisterBlendExtension_Fail(root);
+ register_blend_extension_failed(root, background);
}
- BLI_getInstallationDir(InstallDir);
+ BLI_windows_get_executable_dir(InstallDir);
GetSystemDirectory(SysDir, FILE_MAXDIR);
ThumbHandlerDLL = "BlendThumb.dll";
snprintf(
@@ -179,7 +177,7 @@ void RegisterBlendExtension(void)
RegCloseKey(root);
printf("success (%s)\n", usr_mode ? "user" : "system");
- if (!G.background) {
+ if (!background) {
sprintf(MBox,
"File extension registered for %s.",
usr_mode ? "the current user. To register for all users, run as an administrator" :
@@ -189,7 +187,7 @@ void RegisterBlendExtension(void)
TerminateProcess(GetCurrentProcess(), 0);
}
-void get_default_root(char *root)
+void BLI_windows_get_default_root_dir(char *root)
{
char str[MAX_PATH + 1];
@@ -236,7 +234,7 @@ void get_default_root(char *root)
}
}
if (0 == rc) {
- printf("ERROR in 'get_default_root': can't find a valid drive!\n");
+ printf("ERROR in 'BLI_windows_get_default_root_dir': can't find a valid drive!\n");
root[0] = 'C';
root[1] = ':';
root[2] = '\\';
@@ -246,30 +244,6 @@ void get_default_root(char *root)
}
}
-/* UNUSED */
-# if 0
-int check_file_chars(char *filename)
-{
- char *p = filename;
- while (*p) {
- switch (*p) {
- case ':':
- case '?':
- case '*':
- case '|':
- case '\\':
- case '/':
- case '\"':
- return 0;
- break;
- }
-
- p++;
- }
- return 1;
-}
-# endif
-
#else
/* intentionally empty for UNIX */
diff --git a/source/blender/blenlib/tests/BLI_array_test.cc b/source/blender/blenlib/tests/BLI_array_test.cc
index 38ab695d238..7d967eca87e 100644
--- a/source/blender/blenlib/tests/BLI_array_test.cc
+++ b/source/blender/blenlib/tests/BLI_array_test.cc
@@ -1,6 +1,7 @@
/* Apache License, Version 2.0 */
#include "BLI_array.hh"
+#include "BLI_exception_safety_test_utils.hh"
#include "BLI_strict_flags.h"
#include "BLI_vector.hh"
#include "testing/testing.h"
@@ -188,4 +189,65 @@ TEST(array, ReverseIterator)
EXPECT_EQ_ARRAY(array.data(), Span({13, 14, 15, 16}).data(), 4);
}
+TEST(array, SpanConstructorExceptions)
+{
+ std::array<ExceptionThrower, 4> values;
+ values[2].throw_during_copy = true;
+ Span<ExceptionThrower> span{values};
+ EXPECT_ANY_THROW({ Array<ExceptionThrower> array(span); });
+}
+
+TEST(array, SizeValueConstructorExceptions)
+{
+ ExceptionThrower value;
+ value.throw_during_copy = true;
+ EXPECT_ANY_THROW({ Array<ExceptionThrower> array(5, value); });
+}
+
+TEST(array, MoveConstructorExceptions)
+{
+ Array<ExceptionThrower, 4> array(3);
+ array[1].throw_during_move = true;
+ EXPECT_ANY_THROW({ Array<ExceptionThrower> array_copy(std::move(array)); });
+}
+
+TEST(array, CopyAssignmentExceptions)
+{
+ Array<ExceptionThrower> array(5);
+ array[3].throw_during_copy = true;
+ Array<ExceptionThrower> array_copy(10);
+ EXPECT_ANY_THROW({ array_copy = array; });
+}
+
+TEST(array, MoveAssignmentExceptions)
+{
+ Array<ExceptionThrower, 4> array(4);
+ array[2].throw_during_move = true;
+ Array<ExceptionThrower> array_moved(10);
+ EXPECT_ANY_THROW({ array_moved = std::move(array); });
+}
+
+TEST(array, Last)
+{
+ Array<int> array = {5, 7, 8, 9};
+ EXPECT_EQ(array.last(), 9);
+ array.last() = 1;
+ EXPECT_EQ(array[3], 1);
+ EXPECT_EQ(const_cast<const Array<int> &>(array).last(), 1);
+}
+
+TEST(array, Reinitialize)
+{
+ Array<std::string> array = {"hello", "world"};
+ EXPECT_EQ(array.size(), 2);
+ EXPECT_EQ(array[1], "world");
+ array.reinitialize(3);
+ EXPECT_EQ(array.size(), 3);
+ EXPECT_EQ(array[0], "");
+ EXPECT_EQ(array[1], "");
+ EXPECT_EQ(array[2], "");
+ array.reinitialize(0);
+ EXPECT_EQ(array.size(), 0);
+}
+
} // namespace blender::tests
diff --git a/source/blender/blenlib/tests/BLI_delaunay_2d_test.cc b/source/blender/blenlib/tests/BLI_delaunay_2d_test.cc
index 752f833461d..2287389f7aa 100644
--- a/source/blender/blenlib/tests/BLI_delaunay_2d_test.cc
+++ b/source/blender/blenlib/tests/BLI_delaunay_2d_test.cc
@@ -4,48 +4,30 @@
#include "MEM_guardedalloc.h"
+extern "C" {
#include "BLI_math.h"
#include "BLI_rand.h"
#include "PIL_time.h"
-
-#include "BLI_delaunay_2d.h"
+}
#include <fstream>
#include <iostream>
#include <sstream>
+#include <type_traits>
-#define DO_REGULAR_TESTS 1
+#define DO_CPP_TESTS 1
+#define DO_C_TESTS 1
#define DO_RANDOM_TESTS 0
-#define DO_FILE_TESTS 0
-static void fill_input_verts(CDT_input *r_input, float (*vcos)[2], int nverts)
-{
- r_input->verts_len = nverts;
- r_input->edges_len = 0;
- r_input->faces_len = 0;
- r_input->vert_coords = vcos;
- r_input->edges = NULL;
- r_input->faces = NULL;
- r_input->faces_start_table = NULL;
- r_input->faces_len_table = NULL;
- r_input->epsilon = 1e-5f;
- r_input->skip_input_modify = false;
-}
+#include "BLI_array.hh"
+#include "BLI_double2.hh"
+#include "BLI_math_mpq.hh"
+#include "BLI_mpq2.hh"
+#include "BLI_vector.hh"
-static void add_input_edges(CDT_input *r_input, int (*edges)[2], int nedges)
-{
- r_input->edges_len = nedges;
- r_input->edges = edges;
-}
+#include "BLI_delaunay_2d.h"
-static void add_input_faces(
- CDT_input *r_input, int *faces, int *faces_start_table, int *faces_len_table, int nfaces)
-{
- r_input->faces_len = nfaces;
- r_input->faces = faces;
- r_input->faces_start_table = faces_start_table;
- r_input->faces_len_table = faces_len_table;
-}
+namespace blender::meshintersect {
/* The spec should have the form:
* #verts #edges #faces
@@ -53,177 +35,164 @@ static void add_input_faces(
* <int> <int> [#edges lines]
* <int> <int> ... <int> [#faces lines]
*/
-static void fill_input_from_string(CDT_input *r_input, const char *spec)
+template<typename T> CDT_input<T> fill_input_from_string(const char *spec)
{
- std::string line;
- std::vector<std::vector<int>> faces;
- int i, j;
- int nverts, nedges, nfaces;
- float(*p)[2];
- int(*e)[2];
- int *farr;
- int *flen;
- int *fstart;
-
std::istringstream ss(spec);
+ std::string line;
getline(ss, line);
std::istringstream hdrss(line);
+ int nverts, nedges, nfaces;
hdrss >> nverts >> nedges >> nfaces;
if (nverts == 0) {
- return;
- }
- p = (float(*)[2])MEM_malloc_arrayN(nverts, 2 * sizeof(float), __func__);
- if (nedges > 0) {
- e = (int(*)[2])MEM_malloc_arrayN(nedges, 2 * sizeof(int), __func__);
- }
- if (nfaces > 0) {
- flen = (int *)MEM_malloc_arrayN(nfaces, sizeof(int), __func__);
- fstart = (int *)MEM_malloc_arrayN(nfaces, sizeof(int), __func__);
+ return CDT_input<T>();
}
- i = 0;
+ Array<vec2<T>> verts(nverts);
+ Array<std::pair<int, int>> edges(nedges);
+ Array<Vector<int>> faces(nfaces);
+ int i = 0;
while (i < nverts && getline(ss, line)) {
std::istringstream iss(line);
- iss >> p[i][0] >> p[i][1];
+ double dp0, dp1;
+ iss >> dp0 >> dp1;
+ T p0(dp0);
+ T p1(dp1);
+ verts[i] = vec2<T>(p0, p1);
i++;
}
i = 0;
while (i < nedges && getline(ss, line)) {
std::istringstream ess(line);
- ess >> e[i][0] >> e[i][1];
+ int e0, e1;
+ ess >> e0 >> e1;
+ edges[i] = std::pair<int, int>(e0, e1);
i++;
}
i = 0;
while (i < nfaces && getline(ss, line)) {
std::istringstream fss(line);
int v;
- faces.push_back(std::vector<int>());
while (fss >> v) {
- faces[i].push_back(v);
+ faces[i].append(v);
}
i++;
}
- fill_input_verts(r_input, p, nverts);
- if (nedges > 0) {
- add_input_edges(r_input, e, nedges);
+ CDT_input<T> ans;
+ ans.vert = verts;
+ ans.edge = edges;
+ ans.face = faces;
+#ifdef WITH_GMP
+ if (std::is_same<mpq_class, T>::value) {
+ ans.epsilon = T(0);
}
- if (nfaces > 0) {
- for (i = 0; i < nfaces; i++) {
- flen[i] = (int)faces[i].size();
- if (i == 0) {
- fstart[i] = 0;
- }
- else {
- fstart[i] = fstart[i - 1] + flen[i - 1];
- }
- }
- farr = (int *)MEM_malloc_arrayN(fstart[nfaces - 1] + flen[nfaces - 1], sizeof(int), __func__);
- for (i = 0; i < nfaces; i++) {
- for (j = 0; j < (int)faces[i].size(); j++) {
- farr[fstart[i] + j] = faces[i][j];
+ else {
+ ans.epsilon = T(0.00001);
+ }
+#else
+ ans.epsilon = T(0.00001);
+#endif
+ return ans;
+}
+
+/* Find an original index in a table mapping new to original.
+ * Return -1 if not found.
+ */
+static int get_orig_index(const Array<Vector<int>> &out_to_orig, int orig_index)
+{
+ int n = static_cast<int>(out_to_orig.size());
+ for (int i = 0; i < n; ++i) {
+ for (int orig : out_to_orig[i]) {
+ if (orig == orig_index) {
+ return i;
}
}
- add_input_faces(r_input, farr, fstart, flen, nfaces);
}
+ return -1;
}
-#if DO_FILE_TESTS
-static void fill_input_from_file(CDT_input *in, const char *filename)
+template<typename T> static double math_to_double(const T UNUSED(v))
{
- std::FILE *fp = std::fopen(filename, "rb");
- if (fp) {
- std::string contents;
- std::fseek(fp, 0, SEEK_END);
- contents.resize(std::ftell(fp));
- std::rewind(fp);
- std::fread(&contents[0], 1, contents.size(), fp);
- std::fclose(fp);
- fill_input_from_string(in, contents.c_str());
- }
- else {
- printf("couldn't open file %s\n", filename);
- }
+ BLI_assert(false); /* Need implementation for other type. */
+ return 0.0;
}
-#endif
-static void free_spec_arrays(CDT_input *in)
+template<> double math_to_double<double>(const double v)
{
- if (in->vert_coords) {
- MEM_freeN(in->vert_coords);
- }
- if (in->edges) {
- MEM_freeN(in->edges);
- }
- if (in->faces_len_table) {
- MEM_freeN(in->faces_len_table);
- MEM_freeN(in->faces_start_table);
- MEM_freeN(in->faces);
- }
+ return v;
}
-/* which output vert index goes with given input vertex? -1 if not found */
-static int get_output_vert_index(const CDT_result *r, int in_index)
+#ifdef WITH_GMP
+template<> double math_to_double<mpq_class>(const mpq_class v)
{
- int i, j;
+ return v.get_d();
+}
+#endif
- for (i = 0; i < r->verts_len; i++) {
- for (j = 0; j < r->verts_orig_len_table[i]; j++) {
- if (r->verts_orig[r->verts_orig_start_table[i] + j] == in_index) {
- return i;
- }
- }
- }
- return -1;
+template<typename T> static T math_abs(const T v);
+
+#ifdef WITH_GMP
+template<> mpq_class math_abs(const mpq_class v)
+{
+ return abs(v);
}
+#endif
-/* which output edge index is for given output vert indices? */
-static int get_edge(const CDT_result *r, int out_index_1, int out_index_2)
+template<> double math_abs(const double v)
{
- int i;
+ return fabs(v);
+}
- for (i = 0; i < r->edges_len; i++) {
- if ((r->edges[i][0] == out_index_1 && r->edges[i][1] == out_index_2) ||
- (r->edges[i][0] == out_index_2 && r->edges[i][1] == out_index_1)) {
+/* Find an output index corresponding to a given coordinate (appproximately).
+ * Return -1 if not found.
+ */
+template<typename T> int get_vertex_by_coord(const CDT_result<T> &out, double x, double y)
+{
+ int nv = static_cast<int>(out.vert.size());
+ for (int i = 0; i < nv; ++i) {
+ double vx = math_to_double(out.vert[i][0]);
+ double vy = math_to_double(out.vert[i][1]);
+ if (fabs(vx - x) <= 1e-5 && fabs(vy - y) <= 1e-5) {
return i;
}
}
return -1;
}
-/* return true if given output edge has given input edge id in its originals list */
-static bool out_edge_has_input_id(const CDT_result *r, int out_edge_index, int in_edge_index)
+/* Find an edge between two given output vertex indices. -1 if not found, */
+template<typename T>
+int get_output_edge_index(const CDT_result<T> &out, int out_index_1, int out_index_2)
{
- if (r->edges_orig == NULL) {
- return false;
- }
- if (out_edge_index < 0 || out_edge_index >= r->edges_len) {
- return false;
- }
- for (int i = 0; i < r->edges_orig_len_table[out_edge_index]; i++) {
- if (r->edges_orig[r->edges_orig_start_table[out_edge_index] + i] == in_edge_index) {
- return true;
+ int ne = static_cast<int>(out.edge.size());
+ for (int i = 0; i < ne; ++i) {
+ if ((out.edge[i].first == out_index_1 && out.edge[i].second == out_index_2) ||
+ (out.edge[i].first == out_index_2 && out.edge[i].second == out_index_1)) {
+ return i;
}
}
- return false;
+ return -1;
}
-/* which face is for given output vertex ngon? */
-static int get_face(const CDT_result *r, const int *out_indices, int nverts)
+template<typename T>
+bool output_edge_has_input_id(const CDT_result<T> &out, int out_edge_index, int in_edge_index)
{
- int f, cycle_start, k, fstart;
- bool ok;
+ return out_edge_index < static_cast<int>(out.edge_orig.size()) &&
+ out.edge_orig[out_edge_index].contains(in_edge_index);
+}
- if (r->faces_len == 0) {
- return -1;
- }
- for (f = 0; f < r->faces_len; f++) {
- if (r->faces_len_table[f] != nverts) {
+/* Which out face is for a give output vertex ngon? -1 if not found.
+ * Allow for cyclic shifts vertices of one poly vs the other.
+ */
+template<typename T> int get_output_face_index(const CDT_result<T> &out, const Array<int> &poly)
+{
+ int nf = static_cast<int>(out.face.size());
+ int npolyv = static_cast<int>(poly.size());
+ for (int f = 0; f < nf; ++f) {
+ if (out.face[f].size() != poly.size()) {
continue;
}
- fstart = r->faces_start_table[f];
- for (cycle_start = 0; cycle_start < nverts; cycle_start++) {
- ok = true;
- for (k = 0; ok && k < nverts; k++) {
- if (r->faces[fstart + ((cycle_start + k) % nverts)] != out_indices[k]) {
+ for (int cycle_start = 0; cycle_start < npolyv; ++cycle_start) {
+ bool ok = true;
+ for (int k = 0; ok && k < npolyv; ++k) {
+ if (out.face[f][(cycle_start + k) % npolyv] != poly[k]) {
ok = false;
}
}
@@ -235,240 +204,286 @@ static int get_face(const CDT_result *r, const int *out_indices, int nverts)
return -1;
}
-static int get_face_tri(const CDT_result *r, int out_index_1, int out_index_2, int out_index_3)
+template<typename T>
+int get_output_tri_index(const CDT_result<T> &out,
+ int out_index_1,
+ int out_index_2,
+ int out_index_3)
{
- int tri[3];
+ Array<int> tri{out_index_1, out_index_2, out_index_3};
+ return get_output_face_index(out, tri);
+}
- tri[0] = out_index_1;
- tri[1] = out_index_2;
- tri[2] = out_index_3;
- return get_face(r, tri, 3);
+template<typename T>
+bool output_face_has_input_id(const CDT_result<T> &out, int out_face_index, int in_face_index)
+{
+ return out_face_index < static_cast<int>(out.face_orig.size()) &&
+ out.face_orig[out_face_index].contains(in_face_index);
}
-/* return true if given otuput face has given input face id in its originals list */
-static bool out_face_has_input_id(const CDT_result *r, int out_face_index, int in_face_index)
+/* For debugging. */
+template<typename T> std::ostream &operator<<(std::ostream &os, const CDT_result<T> &r)
{
- if (r->faces_orig == NULL) {
- return false;
+ os << "\nRESULT\n";
+ os << r.vert.size() << " verts, " << r.edge.size() << " edges, " << r.face.size() << " faces\n";
+ os << "\nVERTS\n";
+ for (int i : r.vert.index_range()) {
+ os << "v" << i << " = " << r.vert[i] << "\n";
+ os << " orig: ";
+ for (int j : r.vert_orig[i].index_range()) {
+ os << r.vert_orig[i][j] << " ";
+ }
+ os << "\n";
}
- if (out_face_index < 0 || out_face_index >= r->faces_len) {
- return false;
+ os << "\nEDGES\n";
+ for (int i : r.edge.index_range()) {
+ os << "e" << i << " = (" << r.edge[i].first << ", " << r.edge[i].second << ")\n";
+ os << " orig: ";
+ for (int j : r.edge_orig[i].size()) {
+ os << r.edge_orig[i][j] << " ";
+ }
+ os << "\n";
}
- for (int i = 0; i < r->faces_orig_len_table[out_face_index]; i++) {
- if (r->faces_orig[r->faces_orig_start_table[out_face_index] + i] == in_face_index) {
- return true;
+ os << "\nFACES\n";
+ for (int i : r.face.index_range()) {
+ os << "f" << i << " = ";
+ for (int j : r.face[i].index_range()) {
+ os << r.face[i][j] << " ";
}
+ os << "\n";
+ os << " orig: ";
+ for (int j : r.face_orig[i].index_range()) {
+ os << r.face_orig[i][j] << " ";
+ }
+ os << "\n";
}
- return false;
+ return os;
}
-#if DO_FILE_TESTS
-/* for debugging */
-static void dump_result(CDT_result *r)
-{
- int i, j;
+static bool draw_append = false; /* Will be set to true after first call. */
- fprintf(stderr, "\nRESULT\n");
- fprintf(stderr,
- "verts_len=%d edges_len=%d faces_len=%d\n",
- r->verts_len,
- r->edges_len,
- r->faces_len);
- fprintf(stderr, "\nvert coords:\n");
- for (i = 0; i < r->verts_len; i++) {
- fprintf(stderr, "%d: (%f,%f)\n", i, r->vert_coords[i][0], r->vert_coords[i][1]);
+template<typename T>
+void graph_draw(const std::string &label,
+ const Array<vec2<T>> &verts,
+ const Array<std::pair<int, int>> &edges,
+ const Array<Vector<int>> &UNUSED(faces))
+{
+ /* Would like to use BKE_tempdir_base() here, but that brings in dependence on kernel library.
+ * This is just for developer debugging anyway, and should never be called in production Blender.
+ */
+#ifdef WIN32
+ constexpr const char *drawfile = "./cdt_test_draw.html";
+#else
+ constexpr const char *drawfile = "/tmp/cdt_test_draw.html";
+#endif
+ constexpr int max_draw_width = 1400;
+ constexpr int max_draw_height = 1000;
+ constexpr int thin_line = 1;
+ constexpr int vert_radius = 3;
+ constexpr bool draw_vert_labels = true;
+ constexpr bool draw_edge_labels = false;
+
+ if (verts.size() == 0) {
+ return;
}
- fprintf(stderr, "vert orig:\n");
- for (i = 0; i < r->verts_len; i++) {
- fprintf(stderr, "%d:", i);
- for (j = 0; j < r->verts_orig_len_table[i]; j++) {
- fprintf(stderr, " %d", r->verts_orig[r->verts_orig_start_table[i] + j]);
+ vec2<double> vmin(1e10, 1e10);
+ vec2<double> vmax(-1e10, -1e10);
+ for (const vec2<T> &v : verts) {
+ for (int i = 0; i < 2; ++i) {
+ double dvi = math_to_double(v[i]);
+ if (dvi < vmin[i]) {
+ vmin[i] = dvi;
+ }
+ if (dvi > vmax[i]) {
+ vmax[i] = dvi;
+ }
}
- fprintf(stderr, "\n");
}
- fprintf(stderr, "\nedges:\n");
- for (i = 0; i < r->edges_len; i++) {
- fprintf(stderr, "%d: (%d,%d)\n", i, r->edges[i][0], r->edges[i][1]);
+ double draw_margin = ((vmax.x - vmin.x) + (vmax.y - vmin.y)) * 0.05;
+ double minx = vmin.x - draw_margin;
+ double maxx = vmax.x + draw_margin;
+ double miny = vmin.y - draw_margin;
+ double maxy = vmax.y + draw_margin;
+
+ double width = maxx - minx;
+ double height = maxy - miny;
+ double aspect = height / width;
+ int view_width = max_draw_width;
+ int view_height = static_cast<int>(view_width * aspect);
+ if (view_height > max_draw_height) {
+ view_height = max_draw_height;
+ view_width = static_cast<int>(view_height / aspect);
}
- if (r->edges_orig) {
- fprintf(stderr, "edge orig:\n");
- for (i = 0; i < r->edges_len; i++) {
- fprintf(stderr, "%d:", i);
- for (j = 0; j < r->edges_orig_len_table[i]; j++) {
- fprintf(stderr, " %d", r->edges_orig[r->edges_orig_start_table[i] + j]);
- }
- fprintf(stderr, "\n");
- }
+ double scale = view_width / width;
+
+#define SX(x) ((math_to_double(x) - minx) * scale)
+#define SY(y) ((maxy - math_to_double(y)) * scale)
+
+ std::ofstream f;
+ if (draw_append) {
+ f.open(drawfile, std::ios_base::app);
}
- fprintf(stderr, "\nfaces:\n");
- for (i = 0; i < r->faces_len; i++) {
- fprintf(stderr, "%d: ", i);
- for (j = 0; j < r->faces_len_table[i]; j++) {
- fprintf(stderr, " %d", r->faces[r->faces_start_table[i] + j]);
+ else {
+ f.open(drawfile);
+ }
+ if (!f) {
+ std::cout << "Could not open file " << drawfile << "\n";
+ return;
+ }
+
+ f << "<div>" << label << "</div>\n<div>\n"
+ << "<svg version=\"1.1\" "
+ "xmlns=\"http://www.w3.org/2000/svg\" "
+ "xmlns:xlink=\"http://www.w3.org/1999/xlink\" "
+ "xml:space=\"preserve\"\n"
+ << "width=\"" << view_width << "\" height=\"" << view_height << "\">n";
+
+ for (const std::pair<int, int> &e : edges) {
+ const vec2<T> &uco = verts[e.first];
+ const vec2<T> &vco = verts[e.second];
+ int strokew = thin_line;
+ f << "<line fill=\"none\" stroke=\"black\" stroke-width=\"" << strokew << "\" x1=\""
+ << SX(uco[0]) << "\" y1=\"" << SY(uco[1]) << "\" x2=\"" << SX(vco[0]) << "\" y2=\""
+ << SY(vco[1]) << "\">\n";
+ f << " <title>[" << e.first << "][" << e.second << "]</title>\n";
+ f << "</line>\n";
+ if (draw_edge_labels) {
+ f << "<text x=\"" << SX(0.5 * (uco[0] + vco[0])) << "\" y=\"" << SY(0.5 * (uco[1] + vco[1]))
+ << "\" font-size=\"small\">";
+ f << "[" << e.first << "][" << e.second << "]</text>\n";
}
- fprintf(stderr, "\n");
}
- if (r->faces_orig) {
- fprintf(stderr, "face orig:\n");
- for (i = 0; i < r->faces_len; i++) {
- fprintf(stderr, "%d:", i);
- for (j = 0; j < r->faces_orig_len_table[i]; j++) {
- fprintf(stderr, " %d", r->faces_orig[r->faces_orig_start_table[i] + j]);
- }
- fprintf(stderr, "\n");
+
+ int i = 0;
+ for (const vec2<T> &vco : verts) {
+ f << "<circle fill=\"black\" cx=\"" << SX(vco[0]) << "\" cy=\"" << SY(vco[1]) << "\" r=\""
+ << vert_radius << "\">\n";
+ f << " <title>[" << i << "]" << vco << "</title>\n";
+ f << "</circle>\n";
+ if (draw_vert_labels) {
+ f << "<text x=\"" << SX(vco[0]) + vert_radius << "\" y=\"" << SY(vco[1]) - vert_radius
+ << "\" font-size=\"small\">[" << i << "]</text>\n";
}
+ ++i;
}
+
+ draw_append = true;
+#undef SX
+#undef SY
+}
+
+/* Should tests draw their output to an html file? */
+constexpr bool DO_DRAW = false;
+
+template<typename T> void expect_coord_near(const vec2<T> &testco, const vec2<T> &refco);
+
+#ifdef WITH_GMP
+template<>
+void expect_coord_near<mpq_class>(const vec2<mpq_class> &testco, const vec2<mpq_class> &refco)
+{
+ EXPECT_EQ(testco[0], refco[0]);
+ EXPECT_EQ(testco[0], refco[0]);
}
#endif
-#if DO_REGULAR_TESTS
-TEST(delaunay, Empty)
+template<> void expect_coord_near<double>(const vec2<double> &testco, const vec2<double> &refco)
{
- CDT_input in;
- CDT_result *out;
+ EXPECT_NEAR(testco[0], refco[0], 1e-5);
+ EXPECT_NEAR(testco[1], refco[1], 1e-5);
+}
+
+#if DO_CPP_TESTS
- fill_input_verts(&in, NULL, 0);
- out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL);
- EXPECT_NE((CDT_result *)NULL, out);
- EXPECT_EQ(out->verts_len, 0);
- EXPECT_EQ(out->edges_len, 0);
- EXPECT_EQ(out->faces_len, 0);
- BLI_delaunay_2d_cdt_free(out);
+template<typename T> void empty_test()
+{
+ CDT_input<T> in;
+
+ CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
+ EXPECT_EQ(0, out.vert.size());
+ EXPECT_EQ(0, out.edge.size());
+ EXPECT_EQ(0, out.face.size());
+ EXPECT_EQ(0, out.vert_orig.size());
+ EXPECT_EQ(0, out.edge_orig.size());
+ EXPECT_EQ(0, out.face_orig.size());
}
-TEST(delaunay, OnePt)
+template<typename T> void onept_test()
{
- CDT_input in;
- CDT_result *out;
const char *spec = R"(1 0 0
0.0 0.0
)";
- fill_input_from_string(&in, spec);
- out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL);
- EXPECT_EQ(out->verts_len, 1);
- EXPECT_EQ(out->edges_len, 0);
- EXPECT_EQ(out->faces_len, 0);
- if (out->verts_len >= 1) {
- EXPECT_EQ(out->vert_coords[0][0], 0.0f);
- EXPECT_EQ(out->vert_coords[0][1], 0.0f);
+ CDT_input<T> in = fill_input_from_string<T>(spec);
+ CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
+ EXPECT_EQ(out.vert.size(), 1);
+ EXPECT_EQ(out.edge.size(), 0);
+ EXPECT_EQ(out.face.size(), 0);
+ if (out.vert.size() >= 1) {
+ expect_coord_near<T>(out.vert[0], vec2<T>(0, 0));
}
- free_spec_arrays(&in);
- BLI_delaunay_2d_cdt_free(out);
}
-TEST(delaunay, TwoPt)
+template<typename T> void twopt_test()
{
- CDT_input in;
- CDT_result *out;
- int v0_out, v1_out, e0_out;
const char *spec = R"(2 0 0
0.0 -0.75
0.0 0.75
)";
- fill_input_from_string(&in, spec);
- out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL);
- EXPECT_EQ(out->verts_len, 2);
- EXPECT_EQ(out->edges_len, 1);
- EXPECT_EQ(out->faces_len, 0);
- v0_out = get_output_vert_index(out, 0);
- v1_out = get_output_vert_index(out, 1);
+ CDT_input<T> in = fill_input_from_string<T>(spec);
+ CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
+ EXPECT_EQ(out.vert.size(), 2);
+ EXPECT_EQ(out.edge.size(), 1);
+ EXPECT_EQ(out.face.size(), 0);
+ int v0_out = get_orig_index(out.vert_orig, 0);
+ int v1_out = get_orig_index(out.vert_orig, 1);
EXPECT_NE(v0_out, -1);
EXPECT_NE(v1_out, -1);
EXPECT_NE(v0_out, v1_out);
- if (out->verts_len >= 2) {
- EXPECT_NEAR(out->vert_coords[v0_out][0], 0.0, in.epsilon);
- EXPECT_NEAR(out->vert_coords[v0_out][1], -0.75, in.epsilon);
- EXPECT_NEAR(out->vert_coords[v1_out][0], 0.0, in.epsilon);
- EXPECT_NEAR(out->vert_coords[v1_out][1], 0.75, in.epsilon);
+ if (out.vert.size() >= 1) {
+ expect_coord_near<T>(out.vert[v0_out], vec2<T>(0.0, -0.75));
+ expect_coord_near<T>(out.vert[v1_out], vec2<T>(0.0, 0.75));
}
- e0_out = get_edge(out, v0_out, v1_out);
+ int e0_out = get_output_edge_index(out, v0_out, v1_out);
EXPECT_EQ(e0_out, 0);
- free_spec_arrays(&in);
- BLI_delaunay_2d_cdt_free(out);
+ if (DO_DRAW) {
+ graph_draw<T>("TwoPt", out.vert, out.edge, out.face);
+ }
}
-TEST(delaunay, ThreePt)
+template<typename T> void threept_test()
{
- CDT_input in;
- CDT_result *out;
- int v0_out, v1_out, v2_out;
- int e0_out, e1_out, e2_out;
- int f0_out;
const char *spec = R"(3 0 0
-0.1 -0.75
0.1 0.75
0.5 0.5
)";
- fill_input_from_string(&in, spec);
- out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL);
- EXPECT_EQ(out->verts_len, 3);
- EXPECT_EQ(out->edges_len, 3);
- EXPECT_EQ(out->faces_len, 1);
- v0_out = get_output_vert_index(out, 0);
- v1_out = get_output_vert_index(out, 1);
- v2_out = get_output_vert_index(out, 2);
+ CDT_input<T> in = fill_input_from_string<T>(spec);
+ CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
+ EXPECT_EQ(out.vert.size(), 3);
+ EXPECT_EQ(out.edge.size(), 3);
+ EXPECT_EQ(out.face.size(), 1);
+ int v0_out = get_orig_index(out.vert_orig, 0);
+ int v1_out = get_orig_index(out.vert_orig, 1);
+ int v2_out = get_orig_index(out.vert_orig, 2);
EXPECT_TRUE(v0_out != -1 && v1_out != -1 && v2_out != -1);
EXPECT_TRUE(v0_out != v1_out && v0_out != v2_out && v1_out != v2_out);
- e0_out = get_edge(out, v0_out, v1_out);
- e1_out = get_edge(out, v1_out, v2_out);
- e2_out = get_edge(out, v2_out, v0_out);
+ int e0_out = get_output_edge_index(out, v0_out, v1_out);
+ int e1_out = get_output_edge_index(out, v1_out, v2_out);
+ int e2_out = get_output_edge_index(out, v2_out, v0_out);
EXPECT_TRUE(e0_out != -1 && e1_out != -1 && e2_out != -1);
EXPECT_TRUE(e0_out != e1_out && e0_out != e2_out && e1_out != e2_out);
- f0_out = get_face_tri(out, v0_out, v2_out, v1_out);
+ int f0_out = get_output_tri_index(out, v0_out, v2_out, v1_out);
EXPECT_EQ(f0_out, 0);
- free_spec_arrays(&in);
- BLI_delaunay_2d_cdt_free(out);
-}
-
-TEST(delaunay, ThreePtsMerge)
-{
- CDT_input in;
- CDT_result *out;
- int v0_out, v1_out, v2_out;
- const char *spec = R"(3 0 0
- -0.05 -0.05
- 0.05 -0.05
- 0.0 0.03660254
- )";
-
- /* First with epsilon such that points are within that distance of each other */
- fill_input_from_string(&in, spec);
- in.epsilon = 0.21f;
- out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL);
- EXPECT_EQ(out->verts_len, 1);
- EXPECT_EQ(out->edges_len, 0);
- EXPECT_EQ(out->faces_len, 0);
- v0_out = get_output_vert_index(out, 0);
- v1_out = get_output_vert_index(out, 1);
- v2_out = get_output_vert_index(out, 2);
- EXPECT_EQ(v0_out, 0);
- EXPECT_EQ(v1_out, 0);
- EXPECT_EQ(v2_out, 0);
- BLI_delaunay_2d_cdt_free(out);
- /* Now with epsilon such that points are farther away than that.
- * Note that the points won't merge with each other if distance is
- * less than .01, but that they may merge with points on the Delaunay
- * triangulation lines, so make epsilon even smaller to avoid that for
- * this test.
- */
- in.epsilon = 0.05f;
- out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL);
- EXPECT_EQ(out->verts_len, 3);
- EXPECT_EQ(out->edges_len, 3);
- EXPECT_EQ(out->faces_len, 1);
- free_spec_arrays(&in);
- BLI_delaunay_2d_cdt_free(out);
+ if (DO_DRAW) {
+ graph_draw<T>("ThreePt", out.vert, out.edge, out.face);
+ }
}
-TEST(delaunay, MixedPts)
+template<typename T> void mixedpts_test()
{
- CDT_input in;
- CDT_result *out;
- int v0_out, v1_out, v2_out, v3_out;
- int e0_out, e1_out, e2_out;
+ /* Edges form a chain of length 3. */
const char *spec = R"(4 3 0
0.0 0.0
-0.5 -0.5
@@ -479,135 +494,129 @@ TEST(delaunay, MixedPts)
2 3
)";
- fill_input_from_string(&in, spec);
- out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL);
- EXPECT_EQ(out->verts_len, 4);
- EXPECT_EQ(out->edges_len, 6);
- v0_out = get_output_vert_index(out, 0);
- v1_out = get_output_vert_index(out, 1);
- v2_out = get_output_vert_index(out, 2);
- v3_out = get_output_vert_index(out, 3);
+ CDT_input<T> in = fill_input_from_string<T>(spec);
+ CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
+ EXPECT_EQ(out.vert.size(), 4);
+ EXPECT_EQ(out.edge.size(), 6);
+ int v0_out = get_orig_index(out.vert_orig, 0);
+ int v1_out = get_orig_index(out.vert_orig, 1);
+ int v2_out = get_orig_index(out.vert_orig, 2);
+ int v3_out = get_orig_index(out.vert_orig, 3);
EXPECT_TRUE(v0_out != -1 && v1_out != -1 && v2_out != -1 && v3_out != -1);
- e0_out = get_edge(out, v0_out, v1_out);
- e1_out = get_edge(out, v1_out, v2_out);
- e2_out = get_edge(out, v2_out, v3_out);
+ int e0_out = get_output_edge_index(out, v0_out, v1_out);
+ int e1_out = get_output_edge_index(out, v1_out, v2_out);
+ int e2_out = get_output_edge_index(out, v2_out, v3_out);
EXPECT_TRUE(e0_out != -1 && e1_out != -1 && e2_out != -1);
- EXPECT_TRUE(out_edge_has_input_id(out, e0_out, 0));
- EXPECT_TRUE(out_edge_has_input_id(out, e1_out, 1));
- EXPECT_TRUE(out_edge_has_input_id(out, e2_out, 2));
- free_spec_arrays(&in);
- BLI_delaunay_2d_cdt_free(out);
+ EXPECT_TRUE(output_edge_has_input_id(out, e0_out, 0));
+ EXPECT_TRUE(output_edge_has_input_id(out, e1_out, 1));
+ EXPECT_TRUE(output_edge_has_input_id(out, e2_out, 2));
+ if (DO_DRAW) {
+ graph_draw<T>("MixedPts", out.vert, out.edge, out.face);
+ }
}
-TEST(delaunay, Quad0)
+template<typename T> void quad0_test()
{
- CDT_input in;
- CDT_result *out;
- int e_diag_out;
const char *spec = R"(4 0 0
0.0 1.0
1.0 0.0
2.0 0.1
2.25 0.5
)";
- fill_input_from_string(&in, spec);
- out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL);
- EXPECT_EQ(out->verts_len, 4);
- EXPECT_EQ(out->edges_len, 5);
- e_diag_out = get_edge(out, 1, 3);
+
+ CDT_input<T> in = fill_input_from_string<T>(spec);
+ CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
+ EXPECT_EQ(out.vert.size(), 4);
+ EXPECT_EQ(out.edge.size(), 5);
+ int e_diag_out = get_output_edge_index(out, 1, 3);
EXPECT_NE(e_diag_out, -1);
- free_spec_arrays(&in);
- BLI_delaunay_2d_cdt_free(out);
+ if (DO_DRAW) {
+ graph_draw<T>("Quad0", out.vert, out.edge, out.face);
+ }
}
-TEST(delaunay, Quad1)
+template<typename T> void quad1_test()
{
- CDT_input in;
- CDT_result *out;
- int e_diag_out;
const char *spec = R"(4 0 0
0.0 0.0
0.9 -1.0
2.0 0.0
0.9 3.0
)";
- fill_input_from_string(&in, spec);
- out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL);
- EXPECT_EQ(out->verts_len, 4);
- EXPECT_EQ(out->edges_len, 5);
- e_diag_out = get_edge(out, 0, 2);
+
+ CDT_input<T> in = fill_input_from_string<T>(spec);
+ CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
+ EXPECT_EQ(out.vert.size(), 4);
+ EXPECT_EQ(out.edge.size(), 5);
+ int e_diag_out = get_output_edge_index(out, 0, 2);
EXPECT_NE(e_diag_out, -1);
- free_spec_arrays(&in);
- BLI_delaunay_2d_cdt_free(out);
+ if (DO_DRAW) {
+ graph_draw<T>("Quad1", out.vert, out.edge, out.face);
+ }
}
-TEST(delaunay, Quad2)
+template<typename T> void quad2_test()
{
- CDT_input in;
- CDT_result *out;
- int e_diag_out;
const char *spec = R"(4 0 0
0.5 0.0
0.15 0.2
0.3 0.4
.45 0.35
)";
- fill_input_from_string(&in, spec);
- out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL);
- EXPECT_EQ(out->verts_len, 4);
- EXPECT_EQ(out->edges_len, 5);
- e_diag_out = get_edge(out, 1, 3);
+
+ CDT_input<T> in = fill_input_from_string<T>(spec);
+ CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
+ EXPECT_EQ(out.vert.size(), 4);
+ EXPECT_EQ(out.edge.size(), 5);
+ int e_diag_out = get_output_edge_index(out, 1, 3);
EXPECT_NE(e_diag_out, -1);
- free_spec_arrays(&in);
- BLI_delaunay_2d_cdt_free(out);
+ if (DO_DRAW) {
+ graph_draw<T>("Quad2", out.vert, out.edge, out.face);
+ }
}
-TEST(delaunay, Quad3)
+template<typename T> void quad3_test()
{
- CDT_input in;
- CDT_result *out;
- int e_diag_out;
const char *spec = R"(4 0 0
0.5 0.0
0.0 0.0
0.3 0.4
.45 0.35
)";
- fill_input_from_string(&in, spec);
- out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL);
- EXPECT_EQ(out->verts_len, 4);
- EXPECT_EQ(out->edges_len, 5);
- e_diag_out = get_edge(out, 0, 2);
+
+ CDT_input<T> in = fill_input_from_string<T>(spec);
+ CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
+ EXPECT_EQ(out.vert.size(), 4);
+ EXPECT_EQ(out.edge.size(), 5);
+ int e_diag_out = get_output_edge_index(out, 0, 2);
EXPECT_NE(e_diag_out, -1);
- free_spec_arrays(&in);
- BLI_delaunay_2d_cdt_free(out);
+ if (DO_DRAW) {
+ graph_draw<T>("Quad3", out.vert, out.edge, out.face);
+ }
}
-TEST(delaunay, Quad4)
+template<typename T> void quad4_test()
{
- CDT_input in;
- CDT_result *out;
- int e_diag_out;
const char *spec = R"(4 0 0
1.0 1.0
0.0 0.0
1.0 -3.0
0.0 1.0
)";
- fill_input_from_string(&in, spec);
- out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL);
- EXPECT_EQ(out->verts_len, 4);
- EXPECT_EQ(out->edges_len, 5);
- e_diag_out = get_edge(out, 0, 1);
+
+ CDT_input<T> in = fill_input_from_string<T>(spec);
+ CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
+ EXPECT_EQ(out.vert.size(), 4);
+ EXPECT_EQ(out.edge.size(), 5);
+ int e_diag_out = get_output_edge_index(out, 0, 1);
EXPECT_NE(e_diag_out, -1);
- free_spec_arrays(&in);
- BLI_delaunay_2d_cdt_free(out);
+ if (DO_DRAW) {
+ graph_draw<T>("Quad4", out.vert, out.edge, out.face);
+ }
}
-TEST(delaunay, LineInSquare)
+template<typename T> void lineinsquare_test()
{
- CDT_input in;
- CDT_result *out;
const char *spec = R"(6 1 1
-0.5 -0.5
0.5 -0.5
@@ -618,20 +627,24 @@ TEST(delaunay, LineInSquare)
4 5
0 1 3 2
)";
- fill_input_from_string(&in, spec);
- out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS);
- EXPECT_EQ(out->verts_len, 6);
- EXPECT_EQ(out->faces_len, 1);
- free_spec_arrays(&in);
- BLI_delaunay_2d_cdt_free(out);
+
+ CDT_input<T> in = fill_input_from_string<T>(spec);
+ CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
+ EXPECT_EQ(out.vert.size(), 6);
+ EXPECT_EQ(out.face.size(), 6);
+ if (DO_DRAW) {
+ graph_draw<T>("LineInSquare - full", out.vert, out.edge, out.face);
+ }
+ CDT_result<T> out2 = delaunay_2d_calc(in, CDT_CONSTRAINTS);
+ EXPECT_EQ(out2.vert.size(), 6);
+ EXPECT_EQ(out2.face.size(), 1);
+ if (DO_DRAW) {
+ graph_draw<T>("LineInSquare - constraints", out2.vert, out2.edge, out2.face);
+ }
}
-TEST(delaunay, CrossSegs)
+template<typename T> void crosssegs_test()
{
- CDT_input in;
- CDT_result *out;
- int v0_out, v1_out, v2_out, v3_out, v_intersect;
- int i;
const char *spec = R"(4 2 0
-0.5 0.0
0.5 0.0
@@ -641,36 +654,37 @@ TEST(delaunay, CrossSegs)
2 3
)";
- fill_input_from_string(&in, spec);
- out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL);
- EXPECT_EQ(out->verts_len, 5);
- EXPECT_EQ(out->edges_len, 8);
- EXPECT_EQ(out->faces_len, 4);
- v0_out = get_output_vert_index(out, 0);
- v1_out = get_output_vert_index(out, 1);
- v2_out = get_output_vert_index(out, 2);
- v3_out = get_output_vert_index(out, 3);
+ CDT_input<T> in = fill_input_from_string<T>(spec);
+ CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
+ EXPECT_EQ(out.vert.size(), 5);
+ EXPECT_EQ(out.edge.size(), 8);
+ EXPECT_EQ(out.face.size(), 4);
+ int v0_out = get_orig_index(out.vert_orig, 0);
+ int v1_out = get_orig_index(out.vert_orig, 1);
+ int v2_out = get_orig_index(out.vert_orig, 2);
+ int v3_out = get_orig_index(out.vert_orig, 3);
EXPECT_TRUE(v0_out != -1 && v1_out != -1 && v2_out != -1 && v3_out != -1);
- v_intersect = -1;
- for (i = 0; i < out->verts_len; i++) {
- if (i != v0_out && i != v1_out && i != v2_out && i != v3_out) {
- EXPECT_EQ(v_intersect, -1);
- v_intersect = i;
+ if (out.vert.size() == 5) {
+ int v_intersect = -1;
+ for (int i = 0; i < 5; i++) {
+ if (i != v0_out && i != v1_out && i != v2_out && i != v3_out) {
+ EXPECT_EQ(v_intersect, -1);
+ v_intersect = i;
+ }
+ }
+ EXPECT_NE(v_intersect, -1);
+ if (v_intersect != -1) {
+ expect_coord_near<T>(out.vert[v_intersect], vec2<T>(0, 0));
}
}
- EXPECT_NE(v_intersect, -1);
- if (v_intersect != -1) {
- EXPECT_NEAR(out->vert_coords[v_intersect][0], 0.0f, in.epsilon);
- EXPECT_NEAR(out->vert_coords[v_intersect][1], 0.0f, in.epsilon);
+ if (DO_DRAW) {
+ graph_draw<T>("CrossSegs", out.vert, out.edge, out.face);
}
- free_spec_arrays(&in);
- BLI_delaunay_2d_cdt_free(out);
}
-TEST(delaunay, DiamondCross)
+template<typename T> void diamondcross_test()
{
- CDT_input in;
- CDT_result *out;
+ /* Diamond with constraint edge from top to bottom. Some dup verts. */
const char *spec = R"(7 5 0
0.0 0.0
1.0 3.0
@@ -686,24 +700,18 @@ TEST(delaunay, DiamondCross)
5 6
)";
- fill_input_from_string(&in, spec);
- out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL);
- EXPECT_EQ(out->verts_len, 4);
- EXPECT_EQ(out->edges_len, 5);
- EXPECT_EQ(out->faces_len, 2);
- free_spec_arrays(&in);
- BLI_delaunay_2d_cdt_free(out);
+ CDT_input<T> in = fill_input_from_string<T>(spec);
+ CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
+ EXPECT_EQ(out.vert.size(), 4);
+ EXPECT_EQ(out.edge.size(), 5);
+ EXPECT_EQ(out.face.size(), 2);
+ if (DO_DRAW) {
+ graph_draw<T>("DiamondCross", out.vert, out.edge, out.face);
+ }
}
-TEST(delaunay, TwoDiamondsCrossed)
+template<typename T> void twodiamondscross_test()
{
- CDT_input in;
- CDT_result *out;
- /* Input has some repetition of vertices, on purpose */
- int e[][2] = {{0, 1}, {1, 2}, {2, 3}, {3, 4}, {5, 6}, {6, 7}, {7, 8}, {8, 9}, {10, 11}};
- int v_out[12];
- int e_out[9], e_cross_1, e_cross_2, e_cross_3;
- int i;
const char *spec = R"(12 9 0
0.0 0.0
1.0 2.0
@@ -728,40 +736,43 @@ TEST(delaunay, TwoDiamondsCrossed)
10 11
)";
- fill_input_from_string(&in, spec);
- out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL);
- EXPECT_EQ(out->verts_len, 8);
- EXPECT_EQ(out->edges_len, 15);
- EXPECT_EQ(out->faces_len, 8);
- for (i = 0; i < 12; i++) {
- v_out[i] = get_output_vert_index(out, i);
- EXPECT_NE(v_out[i], -1);
+ CDT_input<T> in = fill_input_from_string<T>(spec);
+ CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
+ EXPECT_EQ(out.vert.size(), 8);
+ EXPECT_EQ(out.edge.size(), 15);
+ EXPECT_EQ(out.face.size(), 8);
+ if (out.vert.size() == 8 && out.edge.size() == 15 && out.face.size() == 8) {
+ int v_out[12];
+ for (int i = 0; i < 12; ++i) {
+ v_out[i] = get_orig_index(out.vert_orig, i);
+ EXPECT_NE(v_out[i], -1);
+ }
+ EXPECT_EQ(v_out[0], v_out[4]);
+ EXPECT_EQ(v_out[0], v_out[10]);
+ EXPECT_EQ(v_out[5], v_out[9]);
+ EXPECT_EQ(v_out[7], v_out[11]);
+ int e_out[9];
+ for (int i = 0; i < 8; ++i) {
+ e_out[i] = get_output_edge_index(out, v_out[in.edge[i].first], v_out[in.edge[i].second]);
+ EXPECT_NE(e_out[i], -1);
+ }
+ /* there won't be a single edge for the input cross edge, but rather 3 */
+ EXPECT_EQ(get_output_edge_index(out, v_out[10], v_out[11]), -1);
+ int e_cross_1 = get_output_edge_index(out, v_out[0], v_out[2]);
+ int e_cross_2 = get_output_edge_index(out, v_out[2], v_out[5]);
+ int e_cross_3 = get_output_edge_index(out, v_out[5], v_out[7]);
+ EXPECT_TRUE(e_cross_1 != -1 && e_cross_2 != -1 && e_cross_3 != -1);
+ EXPECT_TRUE(output_edge_has_input_id(out, e_cross_1, 8));
+ EXPECT_TRUE(output_edge_has_input_id(out, e_cross_2, 8));
+ EXPECT_TRUE(output_edge_has_input_id(out, e_cross_3, 8));
}
- EXPECT_EQ(v_out[0], v_out[4]);
- EXPECT_EQ(v_out[0], v_out[10]);
- EXPECT_EQ(v_out[5], v_out[9]);
- EXPECT_EQ(v_out[7], v_out[11]);
- for (i = 0; i < 8; i++) {
- e_out[i] = get_edge(out, v_out[e[i][0]], v_out[e[i][1]]);
- EXPECT_NE(e_out[i], -1);
+ if (DO_DRAW) {
+ graph_draw<T>("TwoDiamondsCross", out.vert, out.edge, out.face);
}
- /* there won't be a single edge for the input cross edge, but rather 3 */
- EXPECT_EQ(get_edge(out, v_out[10], v_out[11]), -1);
- e_cross_1 = get_edge(out, v_out[0], v_out[2]);
- e_cross_2 = get_edge(out, v_out[2], v_out[5]);
- e_cross_3 = get_edge(out, v_out[5], v_out[7]);
- EXPECT_TRUE(e_cross_1 != -1 && e_cross_2 != -1 && e_cross_3 != -1);
- EXPECT_TRUE(out_edge_has_input_id(out, e_cross_1, 8));
- EXPECT_TRUE(out_edge_has_input_id(out, e_cross_2, 8));
- EXPECT_TRUE(out_edge_has_input_id(out, e_cross_3, 8));
- free_spec_arrays(&in);
- BLI_delaunay_2d_cdt_free(out);
-}
-
-TEST(delaunay, ManyCross)
-{
- CDT_input in;
- CDT_result *out;
+}
+
+template<typename T> void manycross_test()
+{
/* Input has some repetition of vertices, on purpose */
const char *spec = R"(27 21 0
0.0 0.0
@@ -814,21 +825,18 @@ TEST(delaunay, ManyCross)
25 26
)";
- fill_input_from_string(&in, spec);
- out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL);
- EXPECT_EQ(out->verts_len, 19);
- EXPECT_EQ(out->edges_len, 46);
- EXPECT_EQ(out->faces_len, 28);
- free_spec_arrays(&in);
- BLI_delaunay_2d_cdt_free(out);
+ CDT_input<T> in = fill_input_from_string<T>(spec);
+ CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
+ EXPECT_EQ(out.vert.size(), 19);
+ EXPECT_EQ(out.edge.size(), 46);
+ EXPECT_EQ(out.face.size(), 28);
+ if (DO_DRAW) {
+ graph_draw<T>("ManyCross", out.vert, out.edge, out.face);
+ }
}
-TEST(delaunay, TwoFace)
+template<typename T> void twoface_test()
{
- CDT_input in;
- CDT_result *out;
- int v_out[6], f0_out, f1_out, e0_out, e1_out, e2_out;
- int i;
const char *spec = R"(6 0 2
0.0 0.0
1.0 0.0
@@ -840,40 +848,116 @@ TEST(delaunay, TwoFace)
3 4 5
)";
- fill_input_from_string(&in, spec);
- out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL);
- EXPECT_EQ(out->verts_len, 6);
- EXPECT_EQ(out->edges_len, 9);
- EXPECT_EQ(out->faces_len, 4);
- for (i = 0; i < 6; i++) {
- v_out[i] = get_output_vert_index(out, i);
- EXPECT_NE(v_out[i], -1);
+ CDT_input<T> in = fill_input_from_string<T>(spec);
+ CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
+ EXPECT_EQ(out.vert.size(), 6);
+ EXPECT_EQ(out.edge.size(), 9);
+ EXPECT_EQ(out.face.size(), 4);
+ if (out.vert.size() == 6 && out.edge.size() == 9 && out.face.size() == 4) {
+ int v_out[6];
+ for (int i = 0; i < 6; i++) {
+ v_out[i] = get_orig_index(out.vert_orig, i);
+ EXPECT_NE(v_out[i], -1);
+ }
+ int f0_out = get_output_tri_index(out, v_out[0], v_out[1], v_out[2]);
+ int f1_out = get_output_tri_index(out, v_out[3], v_out[4], v_out[5]);
+ EXPECT_NE(f0_out, -1);
+ EXPECT_NE(f1_out, -1);
+ int e0_out = get_output_edge_index(out, v_out[0], v_out[1]);
+ int e1_out = get_output_edge_index(out, v_out[1], v_out[2]);
+ int e2_out = get_output_edge_index(out, v_out[2], v_out[0]);
+ EXPECT_NE(e0_out, -1);
+ EXPECT_NE(e1_out, -1);
+ EXPECT_NE(e2_out, -1);
+ EXPECT_TRUE(output_edge_has_input_id(out, e0_out, out.face_edge_offset + 0));
+ EXPECT_TRUE(output_edge_has_input_id(out, e1_out, out.face_edge_offset + 1));
+ EXPECT_TRUE(output_edge_has_input_id(out, e2_out, out.face_edge_offset + 2));
+ EXPECT_TRUE(output_face_has_input_id(out, f0_out, 0));
+ EXPECT_TRUE(output_face_has_input_id(out, f1_out, 1));
+ }
+ if (DO_DRAW) {
+ graph_draw<T>("TwoFace", out.vert, out.edge, out.face);
}
- f0_out = get_face(out, &v_out[0], 3);
- f1_out = get_face(out, &v_out[3], 3);
- EXPECT_NE(f0_out, -1);
- EXPECT_NE(f1_out, -1);
- e0_out = get_edge(out, v_out[0], v_out[1]);
- e1_out = get_edge(out, v_out[1], v_out[2]);
- e2_out = get_edge(out, v_out[2], v_out[0]);
- EXPECT_NE(e0_out, -1);
- EXPECT_NE(e1_out, -1);
- EXPECT_NE(e2_out, -1);
- EXPECT_TRUE(out_edge_has_input_id(out, e0_out, out->face_edge_offset + 0));
- EXPECT_TRUE(out_edge_has_input_id(out, e1_out, out->face_edge_offset + 1));
- EXPECT_TRUE(out_edge_has_input_id(out, e2_out, out->face_edge_offset + 2));
- EXPECT_TRUE(out_face_has_input_id(out, f0_out, 0));
- EXPECT_TRUE(out_face_has_input_id(out, f1_out, 1));
- free_spec_arrays(&in);
- BLI_delaunay_2d_cdt_free(out);
-}
-
-TEST(delaunay, OverlapFaces)
-{
- CDT_input in;
- CDT_result *out;
- int v_out[12], v_int1, v_int2, f0_out, f1_out, f2_out;
- int i;
+}
+
+template<typename T> void twoface2_test()
+{
+ const char *spec = R"(6 0 2
+ 0.0 0.0
+ 4.0 4.0
+ -4.0 2.0
+ 3.0 0.0
+ 3.0 6.0
+ -1.0 2.0
+ 0 1 2
+ 3 4 5
+ )";
+
+ CDT_input<T> in = fill_input_from_string<T>(spec);
+ CDT_result<T> out = delaunay_2d_calc(in, CDT_INSIDE);
+ EXPECT_EQ(out.vert.size(), 10);
+ EXPECT_EQ(out.edge.size(), 18);
+ EXPECT_EQ(out.face.size(), 9);
+ if (out.vert.size() == 10 && out.edge.size() == 18 && out.face.size() == 9) {
+ /* Input verts have no dups, so expect output ones match input ones. */
+ for (int i = 0; i < 6; i++) {
+ EXPECT_EQ(get_orig_index(out.vert_orig, i), i);
+ }
+ int v6 = get_vertex_by_coord(out, 3.0, 3.0);
+ EXPECT_NE(v6, -1);
+ int v7 = get_vertex_by_coord(out, 3.0, 3.75);
+ EXPECT_NE(v7, -1);
+ int v8 = get_vertex_by_coord(out, 0.0, 3.0);
+ EXPECT_NE(v8, -1);
+ int v9 = get_vertex_by_coord(out, 1.0, 1.0);
+ EXPECT_NE(v9, -1);
+ /* f0 to f3 should be triangles part of input face 0, not part of input face 1. */
+ int f0 = get_output_tri_index(out, 0, 9, 5);
+ EXPECT_NE(f0, -1);
+ EXPECT_TRUE(output_face_has_input_id(out, f0, 0));
+ EXPECT_FALSE(output_face_has_input_id(out, f0, 1));
+ int f1 = get_output_tri_index(out, 0, 5, 2);
+ EXPECT_NE(f1, -1);
+ EXPECT_TRUE(output_face_has_input_id(out, f1, 0));
+ EXPECT_FALSE(output_face_has_input_id(out, f1, 1));
+ int f2 = get_output_tri_index(out, 2, 5, 8);
+ EXPECT_NE(f2, -1);
+ EXPECT_TRUE(output_face_has_input_id(out, f2, 0));
+ EXPECT_FALSE(output_face_has_input_id(out, f2, 1));
+ int f3 = get_output_tri_index(out, 6, 1, 7);
+ EXPECT_NE(f3, -1);
+ EXPECT_TRUE(output_face_has_input_id(out, f3, 0));
+ EXPECT_FALSE(output_face_has_input_id(out, f3, 1));
+ /* f4 and f5 should be triangles part of input face 1, not part of input face 0. */
+ int f4 = get_output_tri_index(out, 8, 7, 4);
+ EXPECT_NE(f4, -1);
+ EXPECT_FALSE(output_face_has_input_id(out, f4, 0));
+ EXPECT_TRUE(output_face_has_input_id(out, f4, 1));
+ int f5 = get_output_tri_index(out, 3, 6, 9);
+ EXPECT_NE(f5, -1);
+ EXPECT_FALSE(output_face_has_input_id(out, f5, 0));
+ EXPECT_TRUE(output_face_has_input_id(out, f5, 1));
+ /* f6 to f8 should be triangles part of both input faces. */
+ int f6 = get_output_tri_index(out, 5, 9, 6);
+ EXPECT_NE(f6, -1);
+ EXPECT_TRUE(output_face_has_input_id(out, f6, 0));
+ EXPECT_TRUE(output_face_has_input_id(out, f6, 1));
+ int f7 = get_output_tri_index(out, 5, 6, 7);
+ EXPECT_NE(f7, -1);
+ EXPECT_TRUE(output_face_has_input_id(out, f7, 0));
+ EXPECT_TRUE(output_face_has_input_id(out, f7, 1));
+ int f8 = get_output_tri_index(out, 5, 7, 8);
+ EXPECT_NE(f8, -1);
+ EXPECT_TRUE(output_face_has_input_id(out, f8, 0));
+ EXPECT_TRUE(output_face_has_input_id(out, f8, 1));
+ }
+ if (DO_DRAW) {
+ graph_draw<T>("TwoFace2", out.vert, out.edge, out.face);
+ }
+}
+
+template<typename T> void overlapfaces_test()
+{
const char *spec = R"(12 0 3
0.0 0.0
1.0 0.0
@@ -892,64 +976,69 @@ TEST(delaunay, OverlapFaces)
8 9 10 11
)";
- fill_input_from_string(&in, spec);
- out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL);
- EXPECT_EQ(out->verts_len, 14);
- EXPECT_EQ(out->edges_len, 33);
- EXPECT_EQ(out->faces_len, 20);
- for (i = 0; i < 12; i++) {
- v_out[i] = get_output_vert_index(out, i);
- EXPECT_NE(v_out[i], -1);
- }
- v_int1 = 12;
- v_int2 = 13;
- if (out->verts_len > 13) {
- if (fabsf(out->vert_coords[v_int1][0] - 1.0f) > in.epsilon) {
+ CDT_input<T> in = fill_input_from_string<T>(spec);
+ CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
+ EXPECT_EQ(out.vert.size(), 14);
+ EXPECT_EQ(out.edge.size(), 33);
+ EXPECT_EQ(out.face.size(), 20);
+ if (out.vert.size() == 14 && out.edge.size() == 33 && out.face.size() == 20) {
+ int v_out[12];
+ for (int i = 0; i < 12; i++) {
+ v_out[i] = get_orig_index(out.vert_orig, i);
+ EXPECT_NE(v_out[i], -1);
+ }
+ int v_int1 = 12;
+ int v_int2 = 13;
+ T x = out.vert[v_int1][0] - T(1);
+ if (math_abs(x) > in.epsilon) {
v_int1 = 13;
v_int2 = 12;
}
- EXPECT_NEAR(out->vert_coords[v_int1][0], 1.0, in.epsilon);
- EXPECT_NEAR(out->vert_coords[v_int1][1], 0.5, in.epsilon);
- EXPECT_NEAR(out->vert_coords[v_int2][0], 0.5, in.epsilon);
- EXPECT_NEAR(out->vert_coords[v_int2][1], 1.0, in.epsilon);
- EXPECT_EQ(out->verts_orig_len_table[v_int1], 0);
- EXPECT_EQ(out->verts_orig_len_table[v_int2], 0);
+ expect_coord_near<T>(out.vert[v_int1], vec2<T>(1, 0.5));
+ expect_coord_near<T>(out.vert[v_int2], vec2<T>(0.5, 1));
+ EXPECT_EQ(out.vert_orig[v_int1].size(), 0);
+ EXPECT_EQ(out.vert_orig[v_int2].size(), 0);
+ int f0_out = get_output_tri_index(out, v_out[1], v_int1, v_out[4]);
+ EXPECT_NE(f0_out, -1);
+ EXPECT_TRUE(output_face_has_input_id(out, f0_out, 0));
+ int f1_out = get_output_tri_index(out, v_out[4], v_int1, v_out[2]);
+ EXPECT_NE(f1_out, -1);
+ EXPECT_TRUE(output_face_has_input_id(out, f1_out, 0));
+ EXPECT_TRUE(output_face_has_input_id(out, f1_out, 1));
+ int f2_out = get_output_tri_index(out, v_out[8], v_out[9], v_out[10]);
+ if (f2_out == -1) {
+ f2_out = get_output_tri_index(out, v_out[8], v_out[9], v_out[11]);
+ }
+ EXPECT_NE(f2_out, -1);
+ EXPECT_TRUE(output_face_has_input_id(out, f2_out, 0));
+ EXPECT_TRUE(output_face_has_input_id(out, f2_out, 2));
}
- f0_out = get_face_tri(out, v_out[1], v_int1, v_out[4]);
- EXPECT_NE(f0_out, -1);
- EXPECT_TRUE(out_face_has_input_id(out, f0_out, 0));
- f1_out = get_face_tri(out, v_out[4], v_int1, v_out[2]);
- EXPECT_NE(f1_out, -1);
- EXPECT_TRUE(out_face_has_input_id(out, f1_out, 0));
- EXPECT_TRUE(out_face_has_input_id(out, f1_out, 1));
- f2_out = get_face_tri(out, v_out[8], v_out[9], v_out[10]);
- if (f2_out == -1) {
- f2_out = get_face_tri(out, v_out[8], v_out[9], v_out[11]);
+ if (DO_DRAW) {
+ graph_draw<T>("OverlapFaces - full", out.vert, out.edge, out.face);
}
- EXPECT_NE(f2_out, -1);
- EXPECT_TRUE(out_face_has_input_id(out, f2_out, 0));
- EXPECT_TRUE(out_face_has_input_id(out, f2_out, 2));
- BLI_delaunay_2d_cdt_free(out);
- /* Different output types */
- out = BLI_delaunay_2d_cdt_calc(&in, CDT_INSIDE);
- EXPECT_EQ(out->faces_len, 18);
- BLI_delaunay_2d_cdt_free(out);
+ /* Different output types. */
+ CDT_result<T> out2 = delaunay_2d_calc(in, CDT_INSIDE);
+ EXPECT_EQ(out2.face.size(), 18);
+ if (DO_DRAW) {
+ graph_draw<T>("OverlapFaces - inside", out2.vert, out2.edge, out2.face);
+ }
- out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS);
- EXPECT_EQ(out->faces_len, 4);
- BLI_delaunay_2d_cdt_free(out);
+ CDT_result<T> out3 = delaunay_2d_calc(in, CDT_CONSTRAINTS);
+ EXPECT_EQ(out3.face.size(), 4);
+ if (DO_DRAW) {
+ graph_draw<T>("OverlapFaces - constraints", out3.vert, out3.edge, out3.face);
+ }
- out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS_VALID_BMESH);
- EXPECT_EQ(out->faces_len, 5);
- free_spec_arrays(&in);
- BLI_delaunay_2d_cdt_free(out);
+ CDT_result<T> out4 = delaunay_2d_calc(in, CDT_CONSTRAINTS_VALID_BMESH);
+ EXPECT_EQ(out4.face.size(), 5);
+ if (DO_DRAW) {
+ graph_draw<T>("OverlapFaces - valid bmesh", out4.vert, out4.edge, out4.face);
+ }
}
-TEST(delaunay, TwoSquaresOverlap)
+template<typename T> void twosquaresoverlap_test()
{
- CDT_input in;
- CDT_result *out;
const char *spec = R"(8 0 2
1.0 -1.0
-1.0 -1.0
@@ -963,22 +1052,18 @@ TEST(delaunay, TwoSquaresOverlap)
3 2 1 0
)";
- fill_input_from_string(&in, spec);
- out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS_VALID_BMESH);
- EXPECT_EQ(out->verts_len, 10);
- EXPECT_EQ(out->edges_len, 12);
- EXPECT_EQ(out->faces_len, 3);
- free_spec_arrays(&in);
- BLI_delaunay_2d_cdt_free(out);
+ CDT_input<T> in = fill_input_from_string<T>(spec);
+ CDT_result<T> out = delaunay_2d_calc(in, CDT_CONSTRAINTS_VALID_BMESH);
+ EXPECT_EQ(out.vert.size(), 10);
+ EXPECT_EQ(out.edge.size(), 12);
+ EXPECT_EQ(out.face.size(), 3);
+ if (DO_DRAW) {
+ graph_draw<T>("TwoSquaresOverlap", out.vert, out.edge, out.face);
+ }
}
-TEST(delaunay, TwoFaceEdgeOverlap)
+template<typename T> void twofaceedgeoverlap_test()
{
- CDT_input in;
- CDT_result *out;
- int i, v_out[6], v_int;
- int e01, e1i, ei2, e20, e24, e4i, ei0;
- int f02i, f24i, f10i;
const char *spec = R"(6 0 2
5.657 0.0
-1.414 -5.831
@@ -990,56 +1075,57 @@ TEST(delaunay, TwoFaceEdgeOverlap)
5 4 3
)";
- fill_input_from_string(&in, spec);
- out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS);
- EXPECT_EQ(out->verts_len, 5);
- EXPECT_EQ(out->edges_len, 7);
- EXPECT_EQ(out->faces_len, 3);
- if (out->verts_len == 5 && out->edges_len == 7 && out->faces_len == 3) {
- v_int = 4;
- for (i = 0; i < 6; i++) {
- v_out[i] = get_output_vert_index(out, i);
+ CDT_input<T> in = fill_input_from_string<T>(spec);
+ CDT_result<T> out = delaunay_2d_calc(in, CDT_CONSTRAINTS);
+ EXPECT_EQ(out.vert.size(), 5);
+ EXPECT_EQ(out.edge.size(), 7);
+ EXPECT_EQ(out.face.size(), 3);
+ if (out.vert.size() == 5 && out.edge.size() == 7 && out.face.size() == 3) {
+ int v_int = 4;
+ int v_out[6];
+ for (int i = 0; i < 6; i++) {
+ v_out[i] = get_orig_index(out.vert_orig, i);
EXPECT_NE(v_out[i], -1);
EXPECT_NE(v_out[i], v_int);
}
EXPECT_EQ(v_out[0], v_out[3]);
EXPECT_EQ(v_out[2], v_out[5]);
- e01 = get_edge(out, v_out[0], v_out[1]);
- EXPECT_TRUE(out_edge_has_input_id(out, e01, 1));
- e1i = get_edge(out, v_out[1], v_int);
- EXPECT_TRUE(out_edge_has_input_id(out, e1i, 0));
- ei2 = get_edge(out, v_int, v_out[2]);
- EXPECT_TRUE(out_edge_has_input_id(out, ei2, 0));
- e20 = get_edge(out, v_out[2], v_out[0]);
- EXPECT_TRUE(out_edge_has_input_id(out, e20, 2));
- EXPECT_TRUE(out_edge_has_input_id(out, e20, 5));
- e24 = get_edge(out, v_out[2], v_out[4]);
- EXPECT_TRUE(out_edge_has_input_id(out, e24, 3));
- e4i = get_edge(out, v_out[4], v_int);
- EXPECT_TRUE(out_edge_has_input_id(out, e4i, 4));
- ei0 = get_edge(out, v_int, v_out[0]);
- EXPECT_TRUE(out_edge_has_input_id(out, ei0, 4));
- f02i = get_face_tri(out, v_out[0], v_out[2], v_int);
+ int e01 = get_output_edge_index(out, v_out[0], v_out[1]);
+ int foff = out.face_edge_offset;
+ EXPECT_TRUE(output_edge_has_input_id(out, e01, foff + 1));
+ int e1i = get_output_edge_index(out, v_out[1], v_int);
+ EXPECT_TRUE(output_edge_has_input_id(out, e1i, foff + 0));
+ int ei2 = get_output_edge_index(out, v_int, v_out[2]);
+ EXPECT_TRUE(output_edge_has_input_id(out, ei2, foff + 0));
+ int e20 = get_output_edge_index(out, v_out[2], v_out[0]);
+ EXPECT_TRUE(output_edge_has_input_id(out, e20, foff + 2));
+ EXPECT_TRUE(output_edge_has_input_id(out, e20, 2 * foff + 2));
+ int e24 = get_output_edge_index(out, v_out[2], v_out[4]);
+ EXPECT_TRUE(output_edge_has_input_id(out, e24, 2 * foff + 0));
+ int e4i = get_output_edge_index(out, v_out[4], v_int);
+ EXPECT_TRUE(output_edge_has_input_id(out, e4i, 2 * foff + 1));
+ int ei0 = get_output_edge_index(out, v_int, v_out[0]);
+ EXPECT_TRUE(output_edge_has_input_id(out, ei0, 2 * foff + 1));
+ int f02i = get_output_tri_index(out, v_out[0], v_out[2], v_int);
EXPECT_NE(f02i, -1);
- EXPECT_TRUE(out_face_has_input_id(out, f02i, 0));
- EXPECT_TRUE(out_face_has_input_id(out, f02i, 1));
- f24i = get_face_tri(out, v_out[2], v_out[4], v_int);
+ EXPECT_TRUE(output_face_has_input_id(out, f02i, 0));
+ EXPECT_TRUE(output_face_has_input_id(out, f02i, 1));
+ int f24i = get_output_tri_index(out, v_out[2], v_out[4], v_int);
EXPECT_NE(f24i, -1);
- EXPECT_TRUE(out_face_has_input_id(out, f24i, 1));
- EXPECT_FALSE(out_face_has_input_id(out, f24i, 0));
- f10i = get_face_tri(out, v_out[1], v_out[0], v_int);
+ EXPECT_TRUE(output_face_has_input_id(out, f24i, 1));
+ EXPECT_FALSE(output_face_has_input_id(out, f24i, 0));
+ int f10i = get_output_tri_index(out, v_out[1], v_out[0], v_int);
EXPECT_NE(f10i, -1);
- EXPECT_TRUE(out_face_has_input_id(out, f10i, 0));
- EXPECT_FALSE(out_face_has_input_id(out, f10i, 1));
+ EXPECT_TRUE(output_face_has_input_id(out, f10i, 0));
+ EXPECT_FALSE(output_face_has_input_id(out, f10i, 1));
+ }
+ if (DO_DRAW) {
+ graph_draw<T>("TwoFaceEdgeOverlap", out.vert, out.edge, out.face);
}
- free_spec_arrays(&in);
- BLI_delaunay_2d_cdt_free(out);
}
-TEST(delaunay, TriInTri)
+template<typename T> void triintri_test()
{
- CDT_input in;
- CDT_result *out;
const char *spec = R"(6 0 2
-5.65685 0.0
1.41421 -5.83095
@@ -1051,19 +1137,18 @@ TEST(delaunay, TriInTri)
3 4 5
)";
- fill_input_from_string(&in, spec);
- out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS_VALID_BMESH);
- EXPECT_EQ(out->verts_len, 6);
- EXPECT_EQ(out->edges_len, 8);
- EXPECT_EQ(out->faces_len, 3);
- free_spec_arrays(&in);
- BLI_delaunay_2d_cdt_free(out);
+ CDT_input<T> in = fill_input_from_string<T>(spec);
+ CDT_result<T> out = delaunay_2d_calc(in, CDT_CONSTRAINTS_VALID_BMESH);
+ EXPECT_EQ(out.vert.size(), 6);
+ EXPECT_EQ(out.edge.size(), 8);
+ EXPECT_EQ(out.face.size(), 3);
+ if (DO_DRAW) {
+ graph_draw<T>("TriInTri", out.vert, out.edge, out.face);
+ }
}
-TEST(delaunay, DiamondInSquare)
+template<typename T> void diamondinsquare_test()
{
- CDT_input in;
- CDT_result *out;
const char *spec = R"(8 0 2
0.0 0.0
1.0 0.0
@@ -1076,19 +1161,19 @@ TEST(delaunay, DiamondInSquare)
0 1 2 3
4 5 6 7
)";
- fill_input_from_string(&in, spec);
- out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS_VALID_BMESH);
- EXPECT_EQ(out->verts_len, 8);
- EXPECT_EQ(out->edges_len, 10);
- EXPECT_EQ(out->faces_len, 3);
- free_spec_arrays(&in);
- BLI_delaunay_2d_cdt_free(out);
+
+ CDT_input<T> in = fill_input_from_string<T>(spec);
+ CDT_result<T> out = delaunay_2d_calc(in, CDT_CONSTRAINTS_VALID_BMESH);
+ EXPECT_EQ(out.vert.size(), 8);
+ EXPECT_EQ(out.edge.size(), 10);
+ EXPECT_EQ(out.face.size(), 3);
+ if (DO_DRAW) {
+ graph_draw<T>("DiamondInSquare", out.vert, out.edge, out.face);
+ }
}
-TEST(delaunay, DiamondInSquareWire)
+template<typename T> void diamondinsquarewire_test()
{
- CDT_input in;
- CDT_result *out;
const char *spec = R"(8 8 0
0.0 0.0
1.0 0.0
@@ -1107,67 +1192,19 @@ TEST(delaunay, DiamondInSquareWire)
6 7
7 4
)";
- fill_input_from_string(&in, spec);
- out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS);
- EXPECT_EQ(out->verts_len, 8);
- EXPECT_EQ(out->edges_len, 8);
- EXPECT_EQ(out->faces_len, 2);
- free_spec_arrays(&in);
- BLI_delaunay_2d_cdt_free(out);
-}
-
-TEST(delaunay, TinyEdge)
-{
- CDT_input in;
- CDT_result *out;
- /* An intersect with triangle would be at (0.8, 0.2). */
- const char *spec = R"(4 1 1
- 0.0 0.0
- 1.0 0.0
- 0.5 0.5
- 0.84 0.21
- 0 3
- 0 1 2
- )";
- fill_input_from_string(&in, spec);
- in.epsilon = 0.1;
- out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS);
- EXPECT_EQ(out->verts_len, 4);
- EXPECT_EQ(out->edges_len, 5);
- EXPECT_EQ(out->faces_len, 2);
- free_spec_arrays(&in);
- BLI_delaunay_2d_cdt_free(out);
-}
-TEST(delaunay, TinyEdge2)
-{
- CDT_input in;
- CDT_result *out;
- /* An intersect with triangle would be at (0.8, 0.2). */
- const char *spec = R"(6 1 1
- 0.0 0.0
- 0.2 -0.2
- 1.0 0.0
- 0.5 0.5
- 0.2 0.4
- 0.84 0.21
- 0 5
- 0 1 2 3 4
- )";
- fill_input_from_string(&in, spec);
- in.epsilon = 0.1;
- out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS);
- EXPECT_EQ(out->verts_len, 6);
- EXPECT_EQ(out->edges_len, 7);
- EXPECT_EQ(out->faces_len, 2);
- free_spec_arrays(&in);
- BLI_delaunay_2d_cdt_free(out);
+ CDT_input<T> in = fill_input_from_string<T>(spec);
+ CDT_result<T> out = delaunay_2d_calc(in, CDT_CONSTRAINTS);
+ EXPECT_EQ(out.vert.size(), 8);
+ EXPECT_EQ(out.edge.size(), 8);
+ EXPECT_EQ(out.face.size(), 2);
+ if (DO_DRAW) {
+ graph_draw<T>("DiamondInSquareWire", out.vert, out.edge, out.face);
+ }
}
-TEST(delaunay, repeatededge)
+template<typename T> void repeatedge_test()
{
- CDT_input in;
- CDT_result *out;
const char *spec = R"(5 3 0
0.0 0.0
0.0 1.0
@@ -1178,256 +1215,316 @@ TEST(delaunay, repeatededge)
2 3
2 3
)";
- fill_input_from_string(&in, spec);
- out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS);
- EXPECT_EQ(out->edges_len, 2);
- free_spec_arrays(&in);
- BLI_delaunay_2d_cdt_free(out);
+
+ CDT_input<T> in = fill_input_from_string<T>(spec);
+ CDT_result<T> out = delaunay_2d_calc(in, CDT_CONSTRAINTS);
+ EXPECT_EQ(out.edge.size(), 2);
+ if (DO_DRAW) {
+ graph_draw<T>("RepeatEdge", out.vert, out.edge, out.face);
+ }
}
-TEST(delaunay, NearSeg)
+template<typename T> void repeattri_test()
{
- CDT_input in;
- CDT_result *out;
- int v[4], e0, e1, e2, i;
- const char *spec = R"(4 2 0
+ const char *spec = R"(3 0 2
0.0 0.0
1.0 0.0
- 0.25 0.09
- 0.25 1.0
- 0 1
- 2 3
+ 0.5 1.0
+ 0 1 2
+ 0 1 2
)";
- fill_input_from_string(&in, spec);
- in.epsilon = 0.1;
- out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS);
- EXPECT_EQ(out->verts_len, 4);
- EXPECT_EQ(out->edges_len, 3);
- EXPECT_EQ(out->faces_len, 0);
- if (out->edges_len == 3) {
- for (i = 0; i < 4; i++) {
- v[i] = get_output_vert_index(out, i);
- EXPECT_NE(v[i], -1);
- }
- e0 = get_edge(out, v[0], v[2]);
- e1 = get_edge(out, v[2], v[1]);
- e2 = get_edge(out, v[2], v[3]);
- EXPECT_TRUE(out_edge_has_input_id(out, e0, 0));
- EXPECT_TRUE(out_edge_has_input_id(out, e1, 0));
- EXPECT_TRUE(out_edge_has_input_id(out, e2, 1));
+ CDT_input<T> in = fill_input_from_string<T>(spec);
+ CDT_result<T> out = delaunay_2d_calc(in, CDT_CONSTRAINTS);
+ EXPECT_EQ(out.edge.size(), 3);
+ EXPECT_EQ(out.face.size(), 1);
+ EXPECT_TRUE(output_face_has_input_id(out, 0, 0));
+ EXPECT_TRUE(output_face_has_input_id(out, 0, 1));
+ if (DO_DRAW) {
+ graph_draw<T>("RepeatTri", out.vert, out.edge, out.face);
}
- free_spec_arrays(&in);
- BLI_delaunay_2d_cdt_free(out);
}
-TEST(delaunay, OverlapSegs)
+TEST(delaunay_d, Empty)
{
- CDT_input in;
- CDT_result *out;
- int v[4], e0, e1, e2, i;
- const char *spec = R"(4 2 0
- 0.0 0.0
- 1.0 0.0
- 0.4 0.09
- 1.4 0.09
- 0 1
- 2 3
- )";
+ empty_test<double>();
+}
- fill_input_from_string(&in, spec);
- in.epsilon = 0.1;
- out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS);
- EXPECT_EQ(out->verts_len, 4);
- EXPECT_EQ(out->edges_len, 3);
- EXPECT_EQ(out->faces_len, 0);
- if (out->edges_len == 3) {
- for (i = 0; i < 4; i++) {
- v[i] = get_output_vert_index(out, i);
- EXPECT_NE(v[i], -1);
- }
- e0 = get_edge(out, v[0], v[2]);
- e1 = get_edge(out, v[2], v[1]);
- e2 = get_edge(out, v[1], v[3]);
- EXPECT_TRUE(out_edge_has_input_id(out, e0, 0));
- EXPECT_TRUE(out_edge_has_input_id(out, e1, 0));
- EXPECT_TRUE(out_edge_has_input_id(out, e1, 1));
- EXPECT_TRUE(out_edge_has_input_id(out, e2, 1));
- }
- free_spec_arrays(&in);
- BLI_delaunay_2d_cdt_free(out);
+TEST(delaunay_d, OnePt)
+{
+ onept_test<double>();
}
-TEST(delaunay, NearSegWithDup)
+TEST(delaunay_d, TwoPt)
{
- CDT_input in;
- CDT_result *out;
- int v[5], e0, e1, e2, e3, i;
- const char *spec = R"(5 3 0
- 0.0 0.0
- 1.0 0.0
- 0.25 0.09
- 0.25 1.0
- 0.75 0.09
- 0 1
- 2 3
- 2 4
- )";
+ twopt_test<double>();
+}
- fill_input_from_string(&in, spec);
- in.epsilon = 0.1;
- out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS);
- EXPECT_EQ(out->verts_len, 5);
- EXPECT_EQ(out->edges_len, 4);
- EXPECT_EQ(out->faces_len, 0);
- if (out->edges_len == 5) {
- for (i = 0; i < 5; i++) {
- v[i] = get_output_vert_index(out, i);
- EXPECT_NE(v[i], -1);
- }
- e0 = get_edge(out, v[0], v[2]);
- e1 = get_edge(out, v[2], v[4]);
- e2 = get_edge(out, v[4], v[1]);
- e3 = get_edge(out, v[3], v[2]);
- EXPECT_TRUE(out_edge_has_input_id(out, e0, 0));
- EXPECT_TRUE(out_edge_has_input_id(out, e1, 0));
- EXPECT_TRUE(out_edge_has_input_id(out, e1, 2));
- EXPECT_TRUE(out_edge_has_input_id(out, e2, 0));
- EXPECT_TRUE(out_edge_has_input_id(out, e3, 1));
- }
- free_spec_arrays(&in);
- BLI_delaunay_2d_cdt_free(out);
+TEST(delaunay_d, ThreePt)
+{
+ threept_test<double>();
}
-TEST(delaunay, TwoNearSeg)
+TEST(delaunay_d, MixedPts)
{
- CDT_input in;
- CDT_result *out;
- int v[5], e0, e1, e2, e3, e4, i;
- const char *spec = R"(5 3 0
- 0.0 0.0
- 1.0 0.0
- 0.25 0.09
- 0.25 1.0
- 0.75 0.09
- 0 1
- 3 2
- 3 4
- )";
+ mixedpts_test<double>();
+}
- fill_input_from_string(&in, spec);
- in.epsilon = 0.1;
- out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS);
- EXPECT_EQ(out->verts_len, 5);
- EXPECT_EQ(out->edges_len, 5);
- EXPECT_EQ(out->faces_len, 1);
- if (out->edges_len == 5) {
- for (i = 0; i < 5; i++) {
- v[i] = get_output_vert_index(out, i);
- EXPECT_NE(v[i], -1);
- }
- e0 = get_edge(out, v[0], v[2]);
- e1 = get_edge(out, v[2], v[4]);
- e2 = get_edge(out, v[4], v[1]);
- e3 = get_edge(out, v[3], v[2]);
- e4 = get_edge(out, v[3], v[4]);
- EXPECT_TRUE(out_edge_has_input_id(out, e0, 0));
- EXPECT_TRUE(out_edge_has_input_id(out, e1, 0));
- EXPECT_TRUE(out_edge_has_input_id(out, e2, 0));
- EXPECT_TRUE(out_edge_has_input_id(out, e3, 1));
- EXPECT_TRUE(out_edge_has_input_id(out, e4, 2));
- }
- free_spec_arrays(&in);
- BLI_delaunay_2d_cdt_free(out);
+TEST(delaunay_d, Quad0)
+{
+ quad0_test<double>();
}
-TEST(delaunay, FaceNearSegs)
+TEST(delaunay_d, Quad1)
{
- CDT_input in;
- CDT_result *out;
- int v[9], e0, e1, e2, e3, i;
- const char *spec = R"(8 1 2
- 0.0 0.0
- 2.0 0.0
- 1.0 1.0
- 0.21 0.2
- 1.79 0.2
- 0.51 0.5
- 1.49 0.5
- 1.0 0.19
- 2 7
- 0 1 2
- 3 4 6 5
- )";
+ quad1_test<double>();
+}
- fill_input_from_string(&in, spec);
- in.epsilon = 0.05;
- out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS);
- EXPECT_EQ(out->verts_len, 9);
- EXPECT_EQ(out->edges_len, 13);
- EXPECT_EQ(out->faces_len, 5);
- if (out->verts_len == 9 && out->edges_len == 13) {
- for (i = 0; i < 8; i++) {
- v[i] = get_output_vert_index(out, i);
- EXPECT_NE(v[i], -1);
- }
- v[8] = 8;
- e0 = get_edge(out, v[0], v[1]);
- e1 = get_edge(out, v[4], v[6]);
- e2 = get_edge(out, v[3], v[0]);
- e3 = get_edge(out, v[2], v[8]);
-
- EXPECT_TRUE(out_edge_has_input_id(out, e0, 1));
- EXPECT_TRUE(out_edge_has_input_id(out, e1, 2));
- EXPECT_TRUE(out_edge_has_input_id(out, e1, 5));
- EXPECT_TRUE(out_edge_has_input_id(out, e2, 3));
- EXPECT_TRUE(out_edge_has_input_id(out, e3, 0));
- }
- free_spec_arrays(&in);
- BLI_delaunay_2d_cdt_free(out);
+TEST(delaunay_d, Quad2)
+{
+ quad2_test<double>();
}
-TEST(delaunay, ChainNearIntersects)
+TEST(delaunay_d, Quad3)
{
- CDT_input in;
- CDT_result *out;
- const char *spec = R"(6 10 0
- 0.8 1.25
- 1.25 0.75
- 3.25 1.25
- 5.0 1.9
- 2.5 4.0
- 1.0 2.25
- 0 1
- 1 2
- 2 3
- 3 4
- 4 5
- 5 0
- 0 2
- 5 2
- 4 2
- 1 3
- )";
+ quad3_test<double>();
+}
+
+TEST(delaunay_d, Quad4)
+{
+ quad4_test<double>();
+}
+
+TEST(delaunay_d, LineInSquare)
+{
+ lineinsquare_test<double>();
+}
+
+TEST(delaunay_d, CrossSegs)
+{
+ crosssegs_test<double>();
+}
+
+TEST(delaunay_d, DiamondCross)
+{
+ diamondcross_test<double>();
+}
+
+TEST(delaunay_d, TwoDiamondsCross)
+{
+ twodiamondscross_test<double>();
+}
+
+TEST(delaunay_d, ManyCross)
+{
+ manycross_test<double>();
+}
+
+TEST(delaunay_d, TwoFace)
+{
+ twoface_test<double>();
+}
+
+TEST(delaunay_d, TwoFace2)
+{
+ twoface2_test<double>();
+}
+
+TEST(delaunay_d, OverlapFaces)
+{
+ overlapfaces_test<double>();
+}
- fill_input_from_string(&in, spec);
- in.epsilon = 0.05;
- out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS);
- EXPECT_EQ(out->verts_len, 9);
- EXPECT_EQ(out->edges_len, 16);
- BLI_delaunay_2d_cdt_free(out);
- in.epsilon = 0.11;
- /* The chaining we want to test happens prematurely if modify input. */
- in.skip_input_modify = true;
- out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS);
- EXPECT_EQ(out->verts_len, 6);
- EXPECT_EQ(out->edges_len, 9);
- free_spec_arrays(&in);
- BLI_delaunay_2d_cdt_free(out);
+TEST(delaunay_d, TwoSquaresOverlap)
+{
+ twosquaresoverlap_test<double>();
+}
+
+TEST(delaunay_d, TwoFaceEdgeOverlap)
+{
+ twofaceedgeoverlap_test<double>();
+}
+
+TEST(delaunay_d, TriInTri)
+{
+ triintri_test<double>();
+}
+
+TEST(delaunay_d, DiamondInSquare)
+{
+ diamondinsquare_test<double>();
+}
+
+TEST(delaunay_d, DiamondInSquareWire)
+{
+ diamondinsquarewire_test<double>();
+}
+
+TEST(delaunay_d, RepeatEdge)
+{
+ repeatedge_test<double>();
+}
+
+TEST(delaunay_d, RepeatTri)
+{
+ repeattri_test<double>();
+}
+
+# ifdef WITH_GMP
+TEST(delaunay_m, Empty)
+{
+ empty_test<mpq_class>();
+}
+
+TEST(delaunay_m, OnePt)
+{
+ onept_test<mpq_class>();
+}
+TEST(delaunay_m, TwoPt)
+{
+ twopt_test<mpq_class>();
+}
+
+TEST(delaunay_m, ThreePt)
+{
+ threept_test<mpq_class>();
+}
+
+TEST(delaunay_m, MixedPts)
+{
+ mixedpts_test<mpq_class>();
+}
+
+TEST(delaunay_m, Quad0)
+{
+ quad0_test<mpq_class>();
+}
+
+TEST(delaunay_m, Quad1)
+{
+ quad1_test<mpq_class>();
+}
+
+TEST(delaunay_m, Quad2)
+{
+ quad2_test<mpq_class>();
+}
+
+TEST(delaunay_m, Quad3)
+{
+ quad3_test<mpq_class>();
+}
+
+TEST(delaunay_m, Quad4)
+{
+ quad4_test<mpq_class>();
+}
+
+TEST(delaunay_m, LineInSquare)
+{
+ lineinsquare_test<mpq_class>();
+}
+
+TEST(delaunay_m, CrossSegs)
+{
+ crosssegs_test<mpq_class>();
+}
+
+TEST(delaunay_m, DiamondCross)
+{
+ diamondcross_test<mpq_class>();
+}
+
+TEST(delaunay_m, TwoDiamondsCross)
+{
+ twodiamondscross_test<mpq_class>();
+}
+
+TEST(delaunay_m, ManyCross)
+{
+ manycross_test<mpq_class>();
+}
+
+TEST(delaunay_m, TwoFace)
+{
+ twoface_test<mpq_class>();
+}
+
+TEST(delaunay_m, TwoFace2)
+{
+ twoface2_test<mpq_class>();
+}
+
+TEST(delaunay_m, OverlapFaces)
+{
+ overlapfaces_test<mpq_class>();
+}
+
+TEST(delaunay_m, TwoSquaresOverlap)
+{
+ twosquaresoverlap_test<mpq_class>();
+}
+
+TEST(delaunay_m, TwoFaceEdgeOverlap)
+{
+ twofaceedgeoverlap_test<mpq_class>();
+}
+
+TEST(delaunay_m, TriInTri)
+{
+ triintri_test<mpq_class>();
+}
+
+TEST(delaunay_m, DiamondInSquare)
+{
+ diamondinsquare_test<mpq_class>();
+}
+
+TEST(delaunay_m, DiamondInSquareWire)
+{
+ diamondinsquarewire_test<mpq_class>();
+}
+
+TEST(delaunay_m, RepeatEdge)
+{
+ repeatedge_test<mpq_class>();
+}
+
+TEST(delaunay_m, RepeatTri)
+{
+ repeattri_test<mpq_class>();
+}
+# endif
+
+#endif
+
+#if DO_C_TESTS
+
+TEST(delaunay_d, CintTwoFace)
+{
+ float vert_coords[][2] = {
+ {0.0, 0.0}, {1.0, 0.0}, {0.5, 1.0}, {1.1, 1.0}, {1.1, 0.0}, {1.6, 1.0}};
+ int faces[] = {0, 1, 2, 3, 4, 5};
+ int faces_len[] = {3, 3};
+ int faces_start[] = {0, 3};
+
+ ::CDT_input input;
+ input.verts_len = 6;
+ input.edges_len = 0;
+ input.faces_len = 2;
+ input.vert_coords = vert_coords;
+ input.edges = NULL;
+ input.faces = faces;
+ input.faces_len_table = faces_len;
+ input.faces_start_table = faces_start;
+ input.epsilon = 1e-5f;
+ ::CDT_result *output = BLI_delaunay_2d_cdt_calc(&input, CDT_FULL);
+ BLI_delaunay_2d_cdt_free(output);
}
#endif
#if DO_RANDOM_TESTS
+
enum {
RANDOM_PTS,
RANDOM_SEGS,
@@ -1437,342 +1534,323 @@ enum {
RANDOM_TRI_BETWEEN_CIRCLES,
};
-# define DO_TIMING
-static void rand_delaunay_test(int test_kind,
- int start_lg_size,
- int max_lg_size,
- int reps_per_size,
- double param,
- CDT_output_type otype)
-{
- CDT_input in;
- CDT_result *out;
- int lg_size, size, rep, i, j, size_max, npts_max, nedges_max, nfaces_max, npts, nedges, nfaces;
- int ia, ib, ic;
- float(*p)[2];
- int(*e)[2];
- int *faces, *faces_start_table, *faces_len_table;
- double start_angle, angle_delta, angle1, angle2, angle3;
- float orient;
- double tstart;
- double *times;
- RNG *rng;
-
- rng = BLI_rng_new(0);
- e = NULL;
- faces = NULL;
- faces_start_table = NULL;
- faces_len_table = NULL;
- nedges_max = 0;
- nfaces_max = 0;
-
- /* Set up npts, nedges, nfaces, and allocate needed arrays at max length needed. */
- size_max = 1 << max_lg_size;
- switch (test_kind) {
- case RANDOM_PTS:
- case RANDOM_SEGS:
- case RANDOM_POLY:
- npts_max = size_max;
- if (test_kind == RANDOM_SEGS) {
- nedges_max = npts_max - 1;
- }
- else if (test_kind == RANDOM_POLY) {
- nedges_max = npts_max;
- }
- break;
-
- case RANDOM_TILTED_GRID:
- /* A 'size' x 'size' grid of points, tilted by angle 'param'.
- * Edges will go from left ends to right ends and tops to bottoms, so 2 x size of them.
- * Depending on epsilon, the vertical-ish edges may or may not go through the intermediate
- * vertices, but the horizontal ones always should.
- */
- npts_max = size_max * size_max;
- nedges_max = 2 * size_max;
- break;
-
- case RANDOM_CIRCLE:
- /* A circle with 'size' points, a random start angle, and equal spacing thereafter.
- * Will be input as one face.
- */
- npts_max = size_max;
- nfaces_max = 1;
- break;
-
- case RANDOM_TRI_BETWEEN_CIRCLES:
- /* A set of 'size' triangles, each has two random points on the unit circle,
- * and the third point is a random point on the circle with radius 'param'.
- * Each triangle will be input as a face.
- */
- npts_max = 3 * size_max;
- nfaces_max = size_max;
- break;
-
- default:
- fprintf(stderr, "unknown random delaunay test kind\n");
- return;
- }
- p = (float(*)[2])MEM_malloc_arrayN(npts_max, 2 * sizeof(float), __func__);
- if (nedges_max > 0) {
- e = (int(*)[2])MEM_malloc_arrayN(nedges_max, 2 * sizeof(int), __func__);
- }
- if (nfaces_max > 0) {
- faces_start_table = (int *)MEM_malloc_arrayN(nfaces_max, sizeof(int), __func__);
- faces_len_table = (int *)MEM_malloc_arrayN(nfaces_max, sizeof(int), __func__);
- faces = (int *)MEM_malloc_arrayN(npts_max, sizeof(int), __func__);
- }
-
- times = (double *)MEM_malloc_arrayN(max_lg_size + 1, sizeof(double), __func__);
+template<typename T>
+void rand_delaunay_test(int test_kind,
+ int start_lg_size,
+ int max_lg_size,
+ int reps_per_size,
+ double param,
+ CDT_output_type otype)
+{
+ constexpr bool print_timing = true;
+ RNG *rng = BLI_rng_new(0);
+ Array<double> times(max_lg_size + 1);
/* For powers of 2 sizes up to max_lg_size power of 2. */
- for (lg_size = start_lg_size; lg_size <= max_lg_size; lg_size++) {
- size = 1 << lg_size;
- nedges = 0;
- nfaces = 0;
+ for (int lg_size = start_lg_size; lg_size <= max_lg_size; ++lg_size) {
+ int size = 1 << lg_size;
times[lg_size] = 0.0;
if (size == 1 && test_kind != RANDOM_PTS) {
continue;
}
/* Do 'rep' repetitions. */
- for (rep = 0; rep < reps_per_size; rep++) {
+ for (int rep = 0; rep < reps_per_size; ++rep) {
+ /* First use test type and size to set npts, nedges, and nfaces. */
+ int npts = 0;
+ int nedges = 0;
+ int nfaces = 0;
+ std::string test_label;
+ switch (test_kind) {
+ case RANDOM_PTS: {
+ npts = size;
+ test_label = std::to_string(npts) + "Random points";
+ } break;
+ case RANDOM_SEGS: {
+ npts = size;
+ nedges = npts - 1;
+ test_label = std::to_string(nedges) + "Random edges";
+ } break;
+ case RANDOM_POLY: {
+ npts = size;
+ nedges = npts;
+ test_label = "Random poly with " + std::to_string(nedges) + " edges";
+ } break;
+ case RANDOM_TILTED_GRID: {
+ /* A 'size' x 'size' grid of points, tilted by angle 'param'.
+ * Edges will go from left ends to right ends and tops to bottoms,
+ * so 2 x size of them.
+ * Depending on epsilon, the vertical-ish edges may or may not go
+ * through the intermediate vertices, but the horizontal ones always should.
+ * 'param' is slope of tilt of vertical lines.
+ */
+ npts = size * size;
+ nedges = 2 * size;
+ test_label = "Tilted grid " + std::to_string(npts) + "x" + std::to_string(npts) +
+ " (tilt=" + std::to_string(param) + ")";
+ } break;
+ case RANDOM_CIRCLE: {
+ /* A circle with 'size' points, a random start angle,
+ * and equal spacing thereafter. Will be input as one face.
+ */
+ npts = size;
+ nfaces = 1;
+ test_label = "Circle with " + std::to_string(npts) + " points";
+ } break;
+ case RANDOM_TRI_BETWEEN_CIRCLES: {
+ /* A set of 'size' triangles, each has two random points on the unit circle,
+ * and the third point is a random point on the circle with radius 'param'.
+ * Each triangle will be input as a face.
+ */
+ npts = 3 * size;
+ nfaces = size;
+ test_label = "Random " + std::to_string(nfaces) +
+ " triangles between circles (inner radius=" + std::to_string(param) + ")";
+ } break;
+ default:
+ std::cout << "unknown delaunay test type\n";
+ return;
+ }
+ if (otype != CDT_FULL) {
+ if (otype == CDT_INSIDE) {
+ test_label += " (inside)";
+ }
+ else if (otype == CDT_CONSTRAINTS) {
+ test_label += " (constraints)";
+ }
+ else if (otype == CDT_CONSTRAINTS_VALID_BMESH) {
+ test_label += " (valid bmesh)";
+ }
+ }
+
+ CDT_input<T> in;
+ in.vert = Array<vec2<T>>(npts);
+ if (nedges > 0) {
+ in.edge = Array<std::pair<int, int>>(nedges);
+ }
+ if (nfaces > 0) {
+ in.face = Array<Vector<int>>(nfaces);
+ }
+
/* Make vertices and edges or faces. */
switch (test_kind) {
case RANDOM_PTS:
case RANDOM_SEGS:
- case RANDOM_POLY:
- npts = size;
- if (test_kind == RANDOM_SEGS) {
- nedges = npts - 1;
- }
- else if (test_kind == RANDOM_POLY) {
- nedges = npts;
- }
- for (i = 0; i < size; i++) {
- p[i][0] = (float)BLI_rng_get_double(rng); /* will be in range in [0,1) */
- p[i][1] = (float)BLI_rng_get_double(rng);
+ case RANDOM_POLY: {
+ for (int i = 0; i < size; i++) {
+ in.vert[i][0] = T(BLI_rng_get_double(rng)); /* will be in range in [0,1) */
+ in.vert[i][1] = T(BLI_rng_get_double(rng));
if (test_kind != RANDOM_PTS) {
if (i > 0) {
- e[i - 1][0] = i - 1;
- e[i - 1][1] = i;
+ in.edge[i - 1].first = i - 1;
+ in.edge[i - 1].second = i;
}
}
}
if (test_kind == RANDOM_POLY) {
- e[size - 1][0] = size - 1;
- e[size - 1][1] = 0;
+ in.edge[size - 1].first = size - 1;
+ in.edge[size - 1].second = 0;
}
- break;
+ } break;
- case RANDOM_TILTED_GRID:
- /* 'param' is slope of tilt of vertical lines. */
- npts = size * size;
- nedges = 2 * size;
- for (i = 0; i < size; i++) {
- for (j = 0; j < size; j++) {
- p[i * size + j][0] = i * param + j;
- p[i * size + j][1] = i;
+ case RANDOM_TILTED_GRID: {
+ for (int i = 0; i < size; ++i) {
+ for (int j = 0; j < size; ++j) {
+ in.vert[i * size + j][0] = T(i * param + j);
+ in.vert[i * size + j][1] = T(i);
}
}
- for (i = 0; i < size; i++) {
+ for (int i = 0; i < size; ++i) {
/* Horizontal edges: connect p(i,0) to p(i,size-1). */
- e[i][0] = i * size;
- e[i][1] = i * size + size - 1;
+ in.edge[i].first = i * size;
+ in.edge[i].second = i * size + size - 1;
/* Vertical edges: conntect p(0,i) to p(size-1,i). */
- e[size + i][0] = i;
- e[size + i][1] = (size - 1) * size + i;
+ in.edge[size + i].first = i;
+ in.edge[size + i].second = (size - 1) * size + i;
}
- break;
-
- case RANDOM_CIRCLE:
- npts = size;
- nfaces = 1;
- faces_start_table[0] = 0;
- faces_len_table[0] = npts;
- start_angle = BLI_rng_get_double(rng) * 2.0 * M_PI;
- angle_delta = 2.0 * M_PI / size;
- for (i = 0; i < size; i++) {
- p[i][0] = (float)cos(start_angle + i * angle_delta);
- p[i][1] = (float)sin(start_angle + i * angle_delta);
- faces[i] = i;
+ } break;
+
+ case RANDOM_CIRCLE: {
+ double start_angle = BLI_rng_get_double(rng) * 2.0 * M_PI;
+ double angle_delta = 2.0 * M_PI / size;
+ for (int i = 0; i < size; i++) {
+ in.vert[i][0] = T(cos(start_angle + i * angle_delta));
+ in.vert[i][1] = T(sin(start_angle + i * angle_delta));
+ in.face[0].append(i);
}
- break;
+ } break;
- case RANDOM_TRI_BETWEEN_CIRCLES:
- npts = 3 * size;
- nfaces = size;
- for (i = 0; i < size; i++) {
+ case RANDOM_TRI_BETWEEN_CIRCLES: {
+ for (int i = 0; i < size; i++) {
/* Get three random angles in [0, 2pi). */
- angle1 = BLI_rng_get_double(rng) * 2.0 * M_PI;
- angle2 = BLI_rng_get_double(rng) * 2.0 * M_PI;
- angle3 = BLI_rng_get_double(rng) * 2.0 * M_PI;
- ia = 3 * i;
- ib = 3 * i + 1;
- ic = 3 * i + 2;
- p[ia][0] = (float)cos(angle1);
- p[ia][1] = (float)sin(angle1);
- p[ib][0] = (float)cos(angle2);
- p[ib][1] = (float)sin(angle2);
- p[ic][0] = (float)(param * cos(angle3));
- p[ic][1] = (float)(param * sin(angle3));
- faces_start_table[i] = 3 * i;
- faces_len_table[i] = 3;
+ double angle1 = BLI_rng_get_double(rng) * 2.0 * M_PI;
+ double angle2 = BLI_rng_get_double(rng) * 2.0 * M_PI;
+ double angle3 = BLI_rng_get_double(rng) * 2.0 * M_PI;
+ int ia = 3 * i;
+ int ib = 3 * i + 1;
+ int ic = 3 * i + 2;
+ in.vert[ia][0] = T(cos(angle1));
+ in.vert[ia][1] = T(sin(angle1));
+ in.vert[ib][0] = T(cos(angle2));
+ in.vert[ib][1] = T(sin(angle2));
+ in.vert[ic][0] = T((param * cos(angle3)));
+ in.vert[ic][1] = T((param * sin(angle3)));
/* Put the coordinates in ccw order. */
- faces[ia] = ia;
- orient = (p[ia][0] - p[ic][0]) * (p[ib][1] - p[ic][1]) -
- (p[ib][0] - p[ic][0]) * (p[ia][1] - p[ic][1]);
- if (orient >= 0.0f) {
- faces[ib] = ib;
- faces[ic] = ic;
+ in.face[i].append(ia);
+ int orient = vec2<T>::orient2d(in.vert[ia], in.vert[ib], in.vert[ic]);
+ if (orient >= 0) {
+ in.face[i].append(ib);
+ in.face[i].append(ic);
}
else {
- faces[ib] = ic;
- faces[ic] = ib;
+ in.face[i].append(ic);
+ in.face[i].append(ib);
}
}
- break;
- }
- fill_input_verts(&in, p, npts);
- if (nedges > 0) {
- add_input_edges(&in, e, nedges);
- }
- if (nfaces > 0) {
- add_input_faces(&in, faces, faces_start_table, faces_len_table, nfaces);
+ } break;
}
/* Run the test. */
- tstart = PIL_check_seconds_timer();
- out = BLI_delaunay_2d_cdt_calc(&in, otype);
- EXPECT_NE(out->verts_len, 0);
- BLI_delaunay_2d_cdt_free(out);
+ double tstart = PIL_check_seconds_timer();
+ CDT_result<T> out = delaunay_2d_calc(in, otype);
+ EXPECT_NE(out.vert.size(), 0);
times[lg_size] += PIL_check_seconds_timer() - tstart;
+ if (DO_DRAW) {
+ graph_draw<T>(test_label, out.vert, out.edge, out.face);
+ }
}
}
-# ifdef DO_TIMING
- fprintf(stderr, "size,time\n");
- for (lg_size = 0; lg_size <= max_lg_size; lg_size++) {
- fprintf(stderr, "%d,%f\n", 1 << lg_size, times[lg_size] / reps_per_size);
- }
-# endif
- MEM_freeN(p);
- if (e) {
- MEM_freeN(e);
- }
- if (faces) {
- MEM_freeN(faces);
- MEM_freeN(faces_start_table);
- MEM_freeN(faces_len_table);
+ if (print_timing) {
+ std::cout << "\nsize,time\n";
+ for (int lg_size = 0; lg_size <= max_lg_size; lg_size++) {
+ int size = 1 << lg_size;
+ std::cout << size << "," << times[lg_size] << "\n";
+ }
}
- MEM_freeN(times);
BLI_rng_free(rng);
}
-TEST(delaunay, randompts)
+TEST(delaunay_d, RandomPts)
{
- rand_delaunay_test(RANDOM_PTS, 0, 7, 1, 0.0, CDT_FULL);
+ rand_delaunay_test<double>(RANDOM_PTS, 0, 7, 1, 0.0, CDT_FULL);
}
-TEST(delaunay, randomsegs)
+TEST(delaunay_d, RandomSegs)
{
- rand_delaunay_test(RANDOM_SEGS, 1, 7, 1, 0.0, CDT_FULL);
+ rand_delaunay_test<double>(RANDOM_SEGS, 1, 7, 1, 0.0, CDT_FULL);
}
-TEST(delaunay, randompoly)
+TEST(delaunay_d, RandomPoly)
{
- rand_delaunay_test(RANDOM_POLY, 1, 7, 1, 0.0, CDT_FULL);
+ rand_delaunay_test<double>(RANDOM_POLY, 1, 7, 1, 0.0, CDT_FULL);
}
-TEST(delaunay, randompoly_inside)
+TEST(delaunay_d, RandomPolyConstraints)
{
- rand_delaunay_test(RANDOM_POLY, 1, 7, 1, 0.0, CDT_INSIDE);
+ rand_delaunay_test<double>(RANDOM_POLY, 1, 7, 1, 0.0, CDT_CONSTRAINTS);
}
-TEST(delaunay, randompoly_constraints)
+TEST(delaunay_d, RandomPolyValidBmesh)
{
- rand_delaunay_test(RANDOM_POLY, 1, 7, 1, 0.0, CDT_CONSTRAINTS);
+ rand_delaunay_test<double>(RANDOM_POLY, 1, 7, 1, 0.0, CDT_CONSTRAINTS_VALID_BMESH);
}
-TEST(delaunay, randompoly_validbmesh)
+TEST(delaunay_d, Grid)
{
- rand_delaunay_test(RANDOM_POLY, 1, 7, 1, 0.0, CDT_CONSTRAINTS_VALID_BMESH);
+ rand_delaunay_test<double>(RANDOM_TILTED_GRID, 1, 6, 1, 0.0, CDT_FULL);
}
-TEST(delaunay, grid)
+TEST(delaunay_d, TiltedGridA)
{
- rand_delaunay_test(RANDOM_TILTED_GRID, 1, 6, 1, 0.0, CDT_FULL);
+ rand_delaunay_test<double>(RANDOM_TILTED_GRID, 1, 6, 1, 1.0, CDT_FULL);
}
-TEST(delaunay, tilted_grid_a)
+TEST(delaunay_d, TiltedGridB)
{
- rand_delaunay_test(RANDOM_TILTED_GRID, 1, 6, 1, 1.0, CDT_FULL);
+ rand_delaunay_test<double>(RANDOM_TILTED_GRID, 1, 6, 1, 0.01, CDT_FULL);
}
-TEST(delaunay, tilted_grid_b)
+TEST(delaunay_d, RandomCircle)
{
- rand_delaunay_test(RANDOM_TILTED_GRID, 1, 6, 1, 0.01, CDT_FULL);
+ rand_delaunay_test<double>(RANDOM_CIRCLE, 1, 7, 1, 0.0, CDT_FULL);
}
-TEST(delaunay, randomcircle)
+TEST(delaunay_d, RandomTrisCircle)
{
- rand_delaunay_test(RANDOM_CIRCLE, 1, 7, 1, 0.0, CDT_FULL);
+ rand_delaunay_test<double>(RANDOM_TRI_BETWEEN_CIRCLES, 1, 6, 1, 0.25, CDT_FULL);
}
-TEST(delaunay, random_tris_circle)
+TEST(delaunay_d, RandomTrisCircleB)
{
- rand_delaunay_test(RANDOM_TRI_BETWEEN_CIRCLES, 1, 6, 1, 0.25, CDT_FULL);
+ rand_delaunay_test<double>(RANDOM_TRI_BETWEEN_CIRCLES, 1, 6, 1, 1e-4, CDT_FULL);
}
-TEST(delaunay, random_tris_circle_b)
+# ifdef WITH_GMP
+TEST(delaunay_m, RandomPts)
{
- rand_delaunay_test(RANDOM_TRI_BETWEEN_CIRCLES, 1, 6, 1, 1e-4, CDT_FULL);
+ rand_delaunay_test<mpq_class>(RANDOM_PTS, 0, 7, 1, 0.0, CDT_FULL);
}
-#endif
-#if DO_FILE_TESTS
-/* For manually testing performance by timing a large number of points from a
- * file. See fill_input_from_file for file format.
- */
-static void points_from_file_test(const char *filename)
+TEST(delaunay_m, RandomSegs)
{
- CDT_input in;
- CDT_result *out;
- double tstart;
+ rand_delaunay_test<mpq_class>(RANDOM_SEGS, 1, 7, 1, 0.0, CDT_FULL);
+}
- fill_input_from_file(&in, filename);
- tstart = PIL_check_seconds_timer();
- out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL);
- fprintf(stderr, "time to triangulate=%f seconds\n", PIL_check_seconds_timer() - tstart);
- BLI_delaunay_2d_cdt_free(out);
- free_spec_arrays(&in);
+TEST(delaunay_m, RandomPoly)
+{
+ rand_delaunay_test<mpq_class>(RANDOM_POLY, 1, 7, 1, 0.0, CDT_FULL);
}
-# if 0
-TEST(delaunay, debug)
+TEST(delaunay_d, RandomPolyInside)
{
- CDT_input in;
- CDT_result *out;
- fill_input_from_file(&in, "/tmp/cdtinput.txt");
- out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS);
- BLI_delaunay_2d_cdt_free(out);
- free_spec_arrays(&in);
+ rand_delaunay_test<double>(RANDOM_POLY, 1, 7, 1, 0.0, CDT_INSIDE);
+}
+
+TEST(delaunay_m, RandomPolyInside)
+{
+ rand_delaunay_test<mpq_class>(RANDOM_POLY, 1, 7, 1, 0.0, CDT_INSIDE);
+}
+
+TEST(delaunay_m, RandomPolyConstraints)
+{
+ rand_delaunay_test<mpq_class>(RANDOM_POLY, 1, 7, 1, 0.0, CDT_CONSTRAINTS);
}
-# endif
-# if 1
-# define POINTFILEROOT "/tmp/"
+TEST(delaunay_m, RandomPolyValidBmesh)
+{
+ rand_delaunay_test<mpq_class>(RANDOM_POLY, 1, 7, 1, 0.0, CDT_CONSTRAINTS_VALID_BMESH);
+}
-TEST(delaunay, terrain1)
+TEST(delaunay_m, Grid)
{
- points_from_file_test(POINTFILEROOT "points1.txt");
+ rand_delaunay_test<mpq_class>(RANDOM_TILTED_GRID, 1, 6, 1, 0.0, CDT_FULL);
}
-TEST(delaunay, terrain2)
+TEST(delaunay_m, TiltedGridA)
{
- points_from_file_test(POINTFILEROOT "points2.txt");
+ rand_delaunay_test<mpq_class>(RANDOM_TILTED_GRID, 1, 6, 1, 1.0, CDT_FULL);
}
-TEST(delaunay, terrain3)
+TEST(delaunay_m, TiltedGridB)
{
- points_from_file_test(POINTFILEROOT "points3.txt");
+ rand_delaunay_test<mpq_class>(RANDOM_TILTED_GRID, 1, 6, 1, 0.01, CDT_FULL);
+}
+
+TEST(delaunay_m, RandomCircle)
+{
+ rand_delaunay_test<mpq_class>(RANDOM_CIRCLE, 1, 7, 1, 0.0, CDT_FULL);
+}
+
+TEST(delaunay_m, RandomTrisCircle)
+{
+ rand_delaunay_test<mpq_class>(RANDOM_TRI_BETWEEN_CIRCLES, 1, 6, 1, 0.25, CDT_FULL);
+}
+
+TEST(delaunay_m, RandomTrisCircleB)
+{
+ rand_delaunay_test<double>(RANDOM_TRI_BETWEEN_CIRCLES, 1, 6, 1, 1e-4, CDT_FULL);
}
# endif
+
#endif
+
+} // namespace blender::meshintersect
diff --git a/source/blender/blenlib/tests/BLI_exception_safety_test_utils.hh b/source/blender/blenlib/tests/BLI_exception_safety_test_utils.hh
new file mode 100644
index 00000000000..91270767a25
--- /dev/null
+++ b/source/blender/blenlib/tests/BLI_exception_safety_test_utils.hh
@@ -0,0 +1,102 @@
+#include "BLI_hash.hh"
+#include "BLI_utildefines.h"
+#include "MEM_guardedalloc.h"
+#include "testing/testing.h"
+
+namespace blender::tests {
+
+class ExceptionThrower {
+ private:
+ /* Use some random values that are unlikely to exist at the memory location already. */
+ static constexpr uint32_t is_alive_state = 0x21254634;
+ static constexpr uint32_t is_destructed_state = 0xFA4BC327;
+
+ uint32_t state_;
+
+ /* Make use of leak detector to check if this value has been destructed. */
+ void *my_memory_;
+
+ public:
+ mutable bool throw_during_copy;
+ mutable bool throw_during_move;
+ /* Used for hashing and comparing. */
+ int value;
+
+ ExceptionThrower(int value = 0)
+ : state_(is_alive_state),
+ my_memory_(MEM_mallocN(1, AT)),
+ throw_during_copy(false),
+ throw_during_move(false),
+ value(value)
+ {
+ }
+
+ ExceptionThrower(const ExceptionThrower &other) : ExceptionThrower(other.value)
+ {
+ EXPECT_EQ(other.state_, is_alive_state);
+ if (other.throw_during_copy) {
+ throw std::runtime_error("throwing during copy, as requested");
+ }
+ }
+
+ ExceptionThrower(ExceptionThrower &&other) : ExceptionThrower(other.value)
+ {
+ EXPECT_EQ(other.state_, is_alive_state);
+ if (other.throw_during_move) {
+ throw std::runtime_error("throwing during move, as requested");
+ }
+ }
+
+ ExceptionThrower &operator=(const ExceptionThrower &other)
+ {
+ EXPECT_EQ(other.state_, is_alive_state);
+ if (throw_during_copy || other.throw_during_copy) {
+ throw std::runtime_error("throwing during copy, as requested");
+ }
+ value = other.value;
+ return *this;
+ }
+
+ ExceptionThrower &operator=(ExceptionThrower &&other)
+ {
+ EXPECT_EQ(other.state_, is_alive_state);
+ if (throw_during_move || other.throw_during_move) {
+ throw std::runtime_error("throwing during move, as requested");
+ }
+ value = other.value;
+ return *this;
+ }
+
+ ~ExceptionThrower()
+ {
+ const char *message = "";
+ if (state_ != is_alive_state) {
+ if (state_ == is_destructed_state) {
+ message = "Trying to destruct an already destructed instance.";
+ }
+ else {
+ message = "Trying to destruct an uninitialized instance.";
+ }
+ }
+ EXPECT_EQ(state_, is_alive_state) << message;
+ state_ = is_destructed_state;
+ MEM_freeN(my_memory_);
+ }
+
+ uint64_t hash() const
+ {
+ return static_cast<uint64_t>(value);
+ }
+
+ friend bool operator==(const ExceptionThrower &a, const ExceptionThrower &b)
+ {
+ return a.value == b.value;
+ }
+
+ friend bool operator!=(const ExceptionThrower &a, const ExceptionThrower &b)
+ {
+ return !(a == b);
+ }
+};
+
+} // namespace blender::tests
diff --git a/source/blender/blenlib/tests/BLI_map_test.cc b/source/blender/blenlib/tests/BLI_map_test.cc
index fe7b0f01279..7b4a484e736 100644
--- a/source/blender/blenlib/tests/BLI_map_test.cc
+++ b/source/blender/blenlib/tests/BLI_map_test.cc
@@ -1,5 +1,6 @@
/* Apache License, Version 2.0 */
+#include "BLI_exception_safety_test_utils.hh"
#include "BLI_map.hh"
#include "BLI_rand.h"
#include "BLI_set.hh"
@@ -479,6 +480,72 @@ TEST(map, ForeachItem)
EXPECT_EQ(keys.first_index_of(1), values.first_index_of(8));
}
+TEST(map, CopyConstructorExceptions)
+{
+ using MapType = Map<ExceptionThrower, ExceptionThrower>;
+ MapType map;
+ map.add(2, 2);
+ map.add(4, 4);
+ map.lookup(2).throw_during_copy = true;
+ EXPECT_ANY_THROW({ MapType map_copy(map); });
+}
+
+TEST(map, MoveConstructorExceptions)
+{
+ using MapType = Map<ExceptionThrower, ExceptionThrower>;
+ MapType map;
+ map.add(1, 1);
+ map.add(2, 2);
+ map.lookup(1).throw_during_move = true;
+ EXPECT_ANY_THROW({ MapType map_moved(std::move(map)); });
+ map.add(5, 5);
+}
+
+TEST(map, AddNewExceptions)
+{
+ Map<ExceptionThrower, ExceptionThrower> map;
+ ExceptionThrower key1 = 1;
+ key1.throw_during_copy = true;
+ ExceptionThrower value1;
+ EXPECT_ANY_THROW({ map.add_new(key1, value1); });
+ EXPECT_EQ(map.size(), 0);
+ ExceptionThrower key2 = 2;
+ ExceptionThrower value2;
+ value2.throw_during_copy = true;
+ EXPECT_ANY_THROW({ map.add_new(key2, value2); });
+}
+
+TEST(map, ReserveExceptions)
+{
+ Map<ExceptionThrower, ExceptionThrower> map;
+ map.add(3, 3);
+ map.add(5, 5);
+ map.add(2, 2);
+ map.lookup(2).throw_during_move = true;
+ EXPECT_ANY_THROW({ map.reserve(100); });
+ map.add(1, 1);
+ map.add(5, 5);
+}
+
+TEST(map, PopExceptions)
+{
+ Map<ExceptionThrower, ExceptionThrower> map;
+ map.add(3, 3);
+ map.lookup(3).throw_during_move = true;
+ EXPECT_ANY_THROW({ map.pop(3); });
+ EXPECT_EQ(map.size(), 1);
+ map.add(1, 1);
+ EXPECT_EQ(map.size(), 2);
+}
+
+TEST(map, AddOrModifyExceptions)
+{
+ Map<ExceptionThrower, ExceptionThrower> map;
+ auto create_fn = [](ExceptionThrower *UNUSED(v)) { throw std::runtime_error(""); };
+ auto modify_fn = [](ExceptionThrower *UNUSED(v)) {};
+ EXPECT_ANY_THROW({ map.add_or_modify(3, create_fn, modify_fn); });
+}
+
/**
* Set this to 1 to activate the benchmark. It is disabled by default, because it prints a lot.
*/
diff --git a/source/blender/blenlib/tests/BLI_memory_utils_test.cc b/source/blender/blenlib/tests/BLI_memory_utils_test.cc
index f3cb02b63d7..fcef2f8688a 100644
--- a/source/blender/blenlib/tests/BLI_memory_utils_test.cc
+++ b/source/blender/blenlib/tests/BLI_memory_utils_test.cc
@@ -7,6 +7,7 @@
namespace blender::tests {
+namespace {
struct MyValue {
static inline int alive = 0;
@@ -33,6 +34,7 @@ struct MyValue {
alive--;
}
};
+} // namespace
TEST(memory_utils, DefaultConstructN_ActuallyCallsConstructor)
{
diff --git a/source/blender/blenlib/tests/BLI_mesh_boolean_test.cc b/source/blender/blenlib/tests/BLI_mesh_boolean_test.cc
new file mode 100644
index 00000000000..b212ddb8e63
--- /dev/null
+++ b/source/blender/blenlib/tests/BLI_mesh_boolean_test.cc
@@ -0,0 +1,910 @@
+/* Apache License, Version 2.0 */
+
+#include "testing/testing.h"
+
+#include <fstream>
+#include <iostream>
+#include <sstream>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_array.hh"
+#include "BLI_map.hh"
+#include "BLI_math_mpq.hh"
+#include "BLI_mesh_boolean.hh"
+#include "BLI_mpq3.hh"
+#include "BLI_vector.hh"
+
+#ifdef WITH_GMP
+namespace blender::meshintersect::tests {
+
+constexpr bool DO_OBJ = false;
+
+/* Build and hold an IMesh from a string spec. Also hold and own resources used by IMesh. */
+class IMeshBuilder {
+ public:
+ IMesh imesh;
+ IMeshArena arena;
+
+ /* "Edge orig" indices are an encoding of <input face#, position in face of start of edge>. */
+ static constexpr int MAX_FACE_LEN = 1000; /* Used for forming "orig edge" indices only. */
+
+ static int edge_index(int face_index, int facepos)
+ {
+ return face_index * MAX_FACE_LEN + facepos;
+ }
+
+ static std::pair<int, int> face_and_pos_for_edge_index(int e_index)
+ {
+ return std::pair<int, int>(e_index / MAX_FACE_LEN, e_index % MAX_FACE_LEN);
+ }
+
+ /*
+ * Spec should have form:
+ * #verts #faces
+ * mpq_class mpq_class mpq_clas [#verts lines]
+ * int int int ... [#faces lines; indices into verts for given face]
+ */
+ IMeshBuilder(const char *spec)
+ {
+ std::istringstream ss(spec);
+ std::string line;
+ getline(ss, line);
+ std::istringstream hdrss(line);
+ int nv, nf;
+ hdrss >> nv >> nf;
+ if (nv == 0 || nf == 0) {
+ return;
+ }
+ arena.reserve(nv, nf);
+ Vector<const Vert *> verts;
+ Vector<Face *> faces;
+ bool spec_ok = true;
+ int v_index = 0;
+ while (v_index < nv && spec_ok && getline(ss, line)) {
+ std::istringstream iss(line);
+ mpq_class p0;
+ mpq_class p1;
+ mpq_class p2;
+ iss >> p0 >> p1 >> p2;
+ spec_ok = !iss.fail();
+ verts.append(arena.add_or_find_vert(mpq3(p0, p1, p2), v_index));
+ ++v_index;
+ }
+ if (v_index != nv) {
+ spec_ok = false;
+ }
+ int f_index = 0;
+ while (f_index < nf && spec_ok && getline(ss, line)) {
+ std::istringstream fss(line);
+ Vector<const Vert *> face_verts;
+ Vector<int> edge_orig;
+ int fpos = 0;
+ while (spec_ok && fss >> v_index) {
+ if (v_index < 0 || v_index >= nv) {
+ spec_ok = false;
+ continue;
+ }
+ face_verts.append(verts[v_index]);
+ edge_orig.append(edge_index(f_index, fpos));
+ ++fpos;
+ }
+ Face *facep = arena.add_face(face_verts, f_index, edge_orig);
+ faces.append(facep);
+ ++f_index;
+ }
+ if (f_index != nf) {
+ spec_ok = false;
+ }
+ if (!spec_ok) {
+ std::cout << "Bad spec: " << spec;
+ return;
+ }
+ imesh = IMesh(faces);
+ }
+};
+
+static int all_shape_zero(int UNUSED(t))
+{
+ return 0;
+}
+
+TEST(boolean_trimesh, Empty)
+{
+ IMeshArena arena;
+ IMesh in;
+ IMesh out = boolean_trimesh(in, BoolOpType::None, 1, all_shape_zero, true, &arena);
+ out.populate_vert();
+ EXPECT_EQ(out.vert_size(), 0);
+ EXPECT_EQ(out.face_size(), 0);
+}
+
+TEST(boolean_trimesh, TetTetTrimesh)
+{
+ const char *spec = R"(8 8
+ 0 0 0
+ 2 0 0
+ 1 2 0
+ 1 1 2
+ 0 0 1
+ 2 0 1
+ 1 2 1
+ 1 1 3
+ 0 2 1
+ 0 1 3
+ 1 2 3
+ 2 0 3
+ 4 6 5
+ 4 5 7
+ 5 6 7
+ 6 4 7
+ )";
+
+ IMeshBuilder mb(spec);
+ IMesh out = boolean_trimesh(mb.imesh, BoolOpType::None, 1, all_shape_zero, true, &mb.arena);
+ out.populate_vert();
+ EXPECT_EQ(out.vert_size(), 11);
+ EXPECT_EQ(out.face_size(), 20);
+ if (DO_OBJ) {
+ write_obj_mesh(out, "tettet_tm");
+ }
+
+ IMeshBuilder mb2(spec);
+ IMesh out2 = boolean_trimesh(mb2.imesh, BoolOpType::Union, 1, all_shape_zero, true, &mb2.arena);
+ out2.populate_vert();
+ EXPECT_EQ(out2.vert_size(), 10);
+ EXPECT_EQ(out2.face_size(), 16);
+ if (DO_OBJ) {
+ write_obj_mesh(out2, "tettet_union_tm");
+ }
+
+ IMeshBuilder mb3(spec);
+ IMesh out3 = boolean_trimesh(
+ mb3.imesh, BoolOpType::Union, 2, [](int t) { return t < 4 ? 0 : 1; }, false, &mb3.arena);
+ out3.populate_vert();
+ EXPECT_EQ(out3.vert_size(), 10);
+ EXPECT_EQ(out3.face_size(), 16);
+ if (DO_OBJ) {
+ write_obj_mesh(out3, "tettet_union_binary_tm");
+ }
+
+ IMeshBuilder mb4(spec);
+ IMesh out4 = boolean_trimesh(
+ mb4.imesh, BoolOpType::Union, 2, [](int t) { return t < 4 ? 0 : 1; }, true, &mb4.arena);
+ out4.populate_vert();
+ EXPECT_EQ(out4.vert_size(), 10);
+ EXPECT_EQ(out4.face_size(), 16);
+ if (DO_OBJ) {
+ write_obj_mesh(out4, "tettet_union_binary_self_tm");
+ }
+
+ IMeshBuilder mb5(spec);
+ IMesh out5 = boolean_trimesh(
+ mb5.imesh, BoolOpType::Intersect, 2, [](int t) { return t < 4 ? 0 : 1; }, false, &mb5.arena);
+ out5.populate_vert();
+ EXPECT_EQ(out5.vert_size(), 4);
+ EXPECT_EQ(out5.face_size(), 4);
+ if (DO_OBJ) {
+ write_obj_mesh(out5, "tettet_intersect_binary_tm");
+ }
+
+ IMeshBuilder mb6(spec);
+ IMesh out6 = boolean_trimesh(
+ mb6.imesh,
+ BoolOpType::Difference,
+ 2,
+ [](int t) { return t < 4 ? 0 : 1; },
+ false,
+ &mb6.arena);
+ out6.populate_vert();
+ EXPECT_EQ(out6.vert_size(), 6);
+ EXPECT_EQ(out6.face_size(), 8);
+ if (DO_OBJ) {
+ write_obj_mesh(out6, "tettet_difference_binary_tm");
+ }
+
+ IMeshBuilder mb7(spec);
+ IMesh out7 = boolean_trimesh(
+ mb7.imesh,
+ BoolOpType::Difference,
+ 2,
+ [](int t) { return t < 4 ? 1 : 0; },
+ false,
+ &mb7.arena);
+ out7.populate_vert();
+ EXPECT_EQ(out7.vert_size(), 8);
+ EXPECT_EQ(out7.face_size(), 12);
+ if (DO_OBJ) {
+ write_obj_mesh(out7, "tettet_difference_rev_binary_tm");
+ }
+}
+
+TEST(boolean_trimesh, TetTet2Trimesh)
+{
+ const char *spec = R"(8 8
+ 0 1 -1
+ 7/8 -1/2 -1
+ -7/8 -1/2 -1
+ 0 0 1
+ 0 1 0
+ 7/8 -1/2 0
+ -7/8 -1/2 0
+ 0 0 2
+ 0 3 1
+ 0 1 2
+ 1 3 2
+ 2 3 0
+ 4 7 5
+ 4 5 6
+ 5 7 6
+ 6 7 4
+ )";
+
+ IMeshBuilder mb(spec);
+ IMesh out = boolean_trimesh(mb.imesh, BoolOpType::Union, 1, all_shape_zero, true, &mb.arena);
+ out.populate_vert();
+ EXPECT_EQ(out.vert_size(), 10);
+ EXPECT_EQ(out.face_size(), 16);
+ if (DO_OBJ) {
+ write_obj_mesh(out, "tettet2_union_tm");
+ }
+}
+
+TEST(boolean_trimesh, CubeTetTrimesh)
+{
+ const char *spec = R"(12 16
+ -1 -1 -1
+ -1 -1 1
+ -1 1 -1
+ -1 1 1
+ 1 -1 -1
+ 1 -1 1
+ 1 1 -1
+ 1 1 1
+ 0 1/2 1/2
+ 1/2 -1/4 1/2
+ -1/2 -1/4 1/2
+ 0 0 3/2
+ 0 1 3
+ 0 3 2
+ 2 3 7
+ 2 7 6
+ 6 7 5
+ 6 5 4
+ 4 5 1
+ 4 1 0
+ 2 6 4
+ 2 4 0
+ 7 3 1
+ 7 1 5
+ 8 11 9
+ 8 9 10
+ 9 11 10
+ 10 11 8
+ )";
+
+ IMeshBuilder mb(spec);
+ IMesh out = boolean_trimesh(mb.imesh, BoolOpType::Union, 1, all_shape_zero, true, &mb.arena);
+ out.populate_vert();
+ EXPECT_EQ(out.vert_size(), 14);
+ EXPECT_EQ(out.face_size(), 24);
+ if (DO_OBJ) {
+ write_obj_mesh(out, "cubetet_union_tm");
+ }
+}
+
+TEST(boolean_trimesh, BinaryTetTetTrimesh)
+{
+ const char *spec = R"(8 8
+ 0 0 0
+ 2 0 0
+ 1 2 0
+ 1 1 2
+ 0 0 1
+ 2 0 1
+ 1 2 1
+ 1 1 3
+ 0 2 1
+ 0 1 3
+ 1 2 3
+ 2 0 3
+ 4 6 5
+ 4 5 7
+ 5 6 7
+ 6 4 7
+ )";
+
+ IMeshBuilder mb(spec);
+ IMesh out = boolean_trimesh(
+ mb.imesh, BoolOpType::Intersect, 2, [](int t) { return t < 4 ? 0 : 1; }, false, &mb.arena);
+ out.populate_vert();
+ EXPECT_EQ(out.vert_size(), 4);
+ EXPECT_EQ(out.face_size(), 4);
+ if (DO_OBJ) {
+ write_obj_mesh(out, "binary_tettet_isect_tm");
+ }
+}
+
+TEST(boolean_trimesh, TetTetCoplanarTrimesh)
+{
+ const char *spec = R"(8 8
+ 0 1 0
+ 7/8 -1/2 0
+ -7/8 -1/2 0
+ 0 0 1
+ 0 1 0
+ 7/8 -1/2 0
+ -7/8 -1/2 0
+ 0 0 -1
+ 0 3 1
+ 0 1 2
+ 1 3 2
+ 2 3 0
+ 4 5 7
+ 4 6 5
+ 5 6 7
+ 6 4 7
+ )";
+
+ IMeshBuilder mb(spec);
+ IMesh out = boolean_trimesh(mb.imesh, BoolOpType::Union, 1, all_shape_zero, true, &mb.arena);
+ out.populate_vert();
+ EXPECT_EQ(out.vert_size(), 5);
+ EXPECT_EQ(out.face_size(), 6);
+ if (DO_OBJ) {
+ write_obj_mesh(out, "tettet_coplanar_tm");
+ }
+}
+
+TEST(boolean_trimesh, TetInsideTetTrimesh)
+{
+ const char *spec = R"(8 8
+ 0 0 0
+ 2 0 0
+ 1 2 0
+ 1 1 2
+ -1 -3/4 -1/2
+ 3 -3/4 -1/2
+ 1 13/4 -1/2
+ 1 5/4 7/2
+ 0 2 1
+ 0 1 3
+ 1 2 3
+ 2 0 3
+ 4 6 5
+ 4 5 7
+ 5 6 7
+ 6 4 7
+ )";
+
+ IMeshBuilder mb(spec);
+ IMesh out = boolean_trimesh(mb.imesh, BoolOpType::Union, 1, all_shape_zero, true, &mb.arena);
+ out.populate_vert();
+ EXPECT_EQ(out.vert_size(), 4);
+ EXPECT_EQ(out.face_size(), 4);
+ if (DO_OBJ) {
+ write_obj_mesh(out, "tetinsidetet_tm");
+ }
+}
+
+TEST(boolean_trimesh, TetBesideTetTrimesh)
+{
+ const char *spec = R"(8 8
+ 0 0 0
+ 2 0 0
+ 1 2 0
+ 1 1 2
+ 3 0 0
+ 5 0 0
+ 4 2 0
+ 4 1 2
+ 0 2 1
+ 0 1 3
+ 1 2 3
+ 2 0 3
+ 4 6 5
+ 4 5 7
+ 5 6 7
+ 6 4 7
+ )";
+
+ IMeshBuilder mb(spec);
+ IMesh out = boolean_trimesh(mb.imesh, BoolOpType::Union, 1, all_shape_zero, true, &mb.arena);
+ out.populate_vert();
+ EXPECT_EQ(out.vert_size(), 8);
+ EXPECT_EQ(out.face_size(), 8);
+ if (DO_OBJ) {
+ write_obj_mesh(out, "tetbesidetet_tm");
+ }
+}
+
+TEST(boolean_trimesh, DegenerateTris)
+{
+ const char *spec = R"(10 10
+ 0 0 0
+ 2 0 0
+ 1 2 0
+ 1 1 2
+ 0 0 1
+ 2 0 1
+ 1 2 1
+ 1 1 3
+ 0 0 0
+ 1 0 0
+ 0 2 1
+ 0 8 1
+ 0 1 3
+ 1 2 3
+ 2 0 3
+ 4 6 5
+ 4 5 7
+ 5 6 7
+ 6 4 7
+ 0 1 9
+ )";
+
+ IMeshBuilder mb(spec);
+ IMesh out = boolean_trimesh(
+ mb.imesh, BoolOpType::Intersect, 2, [](int t) { return t < 5 ? 0 : 1; }, false, &mb.arena);
+ out.populate_vert();
+ EXPECT_EQ(out.vert_size(), 4);
+ EXPECT_EQ(out.face_size(), 4);
+ if (DO_OBJ) {
+ write_obj_mesh(out, "degenerate_tris_tm");
+ }
+}
+
+TEST(boolean_polymesh, TetTet)
+{
+ const char *spec = R"(8 8
+ 0 0 0
+ 2 0 0
+ 1 2 0
+ 1 1 2
+ 0 0 1
+ 2 0 1
+ 1 2 1
+ 1 1 3
+ 0 2 1
+ 0 1 3
+ 1 2 3
+ 2 0 3
+ 4 6 5
+ 4 5 7
+ 5 6 7
+ 6 4 7
+ )";
+
+ IMeshBuilder mb(spec);
+ IMesh out = boolean_mesh(
+ mb.imesh, BoolOpType::None, 1, all_shape_zero, true, nullptr, &mb.arena);
+ out.populate_vert();
+ EXPECT_EQ(out.vert_size(), 11);
+ EXPECT_EQ(out.face_size(), 13);
+ if (DO_OBJ) {
+ write_obj_mesh(out, "tettet");
+ }
+
+ IMeshBuilder mb2(spec);
+ IMesh out2 = boolean_mesh(
+ mb2.imesh,
+ BoolOpType::None,
+ 2,
+ [](int t) { return t < 4 ? 0 : 1; },
+ false,
+ nullptr,
+ &mb2.arena);
+ out2.populate_vert();
+ EXPECT_EQ(out2.vert_size(), 11);
+ EXPECT_EQ(out2.face_size(), 13);
+ if (DO_OBJ) {
+ write_obj_mesh(out, "tettet2");
+ }
+}
+
+TEST(boolean_polymesh, CubeCube)
+{
+ const char *spec = R"(16 12
+ -1 -1 -1
+ -1 -1 1
+ -1 1 -1
+ -1 1 1
+ 1 -1 -1
+ 1 -1 1
+ 1 1 -1
+ 1 1 1
+ 1/2 1/2 1/2
+ 1/2 1/2 5/2
+ 1/2 5/2 1/2
+ 1/2 5/2 5/2
+ 5/2 1/2 1/2
+ 5/2 1/2 5/2
+ 5/2 5/2 1/2
+ 5/2 5/2 5/2
+ 0 1 3 2
+ 6 2 3 7
+ 4 6 7 5
+ 0 4 5 1
+ 0 2 6 4
+ 3 1 5 7
+ 8 9 11 10
+ 14 10 11 15
+ 12 14 15 13
+ 8 12 13 9
+ 8 10 14 12
+ 11 9 13 15
+ )";
+
+ IMeshBuilder mb(spec);
+ if (DO_OBJ) {
+ write_obj_mesh(mb.imesh, "cube_cube_in");
+ }
+ IMesh out = boolean_mesh(
+ mb.imesh, BoolOpType::Union, 1, all_shape_zero, true, nullptr, &mb.arena);
+ out.populate_vert();
+ EXPECT_EQ(out.vert_size(), 20);
+ EXPECT_EQ(out.face_size(), 12);
+ if (DO_OBJ) {
+ write_obj_mesh(out, "cubecube_union");
+ }
+
+ IMeshBuilder mb2(spec);
+ IMesh out2 = boolean_mesh(
+ mb2.imesh,
+ BoolOpType::None,
+ 2,
+ [](int t) { return t < 6 ? 0 : 1; },
+ false,
+ nullptr,
+ &mb2.arena);
+ out2.populate_vert();
+ EXPECT_EQ(out2.vert_size(), 22);
+ EXPECT_EQ(out2.face_size(), 18);
+ if (DO_OBJ) {
+ write_obj_mesh(out2, "cubecube_none");
+ }
+}
+
+TEST(boolean_polymesh, CubeCone)
+{
+ const char *spec = R"(14 12
+ -1 -1 -1
+ -1 -1 1
+ -1 1 -1
+ -1 1 1
+ 1 -1 -1
+ 1 -1 1
+ 1 1 -1
+ 1 1 1
+ 0 1/2 3/4
+ 119/250 31/200 3/4
+ 147/500 -81/200 3/4
+ 0 0 7/4
+ -147/500 -81/200 3/4
+ -119/250 31/200 3/4
+ 0 1 3 2
+ 2 3 7 6
+ 6 7 5 4
+ 4 5 1 0
+ 2 6 4 0
+ 7 3 1 5
+ 8 11 9
+ 9 11 10
+ 10 11 12
+ 12 11 13
+ 13 11 8
+ 8 9 10 12 13)";
+
+ IMeshBuilder mb(spec);
+ IMesh out = boolean_mesh(
+ mb.imesh, BoolOpType::Union, 1, all_shape_zero, true, nullptr, &mb.arena);
+ out.populate_vert();
+ EXPECT_EQ(out.vert_size(), 14);
+ EXPECT_EQ(out.face_size(), 12);
+ if (DO_OBJ) {
+ write_obj_mesh(out, "cubeccone");
+ }
+}
+
+TEST(boolean_polymesh, CubeCubeCoplanar)
+{
+ const char *spec = R"(16 12
+ -1 -1 -1
+ -1 -1 1
+ -1 1 -1
+ -1 1 1
+ 1 -1 -1
+ 1 -1 1
+ 1 1 -1
+ 1 1 1
+ -1/2 -1/2 1
+ -1/2 -1/2 2
+ -1/2 1/2 1
+ -1/2 1/2 2
+ 1/2 -1/2 1
+ 1/2 -1/2 2
+ 1/2 1/2 1
+ 1/2 1/2 2
+ 0 1 3 2
+ 2 3 7 6
+ 6 7 5 4
+ 4 5 1 0
+ 2 6 4 0
+ 7 3 1 5
+ 8 9 11 10
+ 10 11 15 14
+ 14 15 13 12
+ 12 13 9 8
+ 10 14 12 8
+ 15 11 9 13
+ )";
+
+ IMeshBuilder mb(spec);
+ IMesh out = boolean_mesh(
+ mb.imesh,
+ BoolOpType::Union,
+ 2,
+ [](int t) { return t < 6 ? 0 : 1; },
+ false,
+ nullptr,
+ &mb.arena);
+ out.populate_vert();
+ EXPECT_EQ(out.vert_size(), 16);
+ EXPECT_EQ(out.face_size(), 12);
+ if (DO_OBJ) {
+ write_obj_mesh(out, "cubecube_coplanar");
+ }
+}
+
+TEST(boolean_polymesh, TetTeTCoplanarDiff)
+{
+ const char *spec = R"(8 8
+ 0 1 0
+ 7/8 -1/2 0
+ -7/8 -1/2 0
+ 0 0 1
+ 0 1 0
+ 7/8 -1/2 0
+ -7/8 -1/2 0
+ 0 0 -1
+ 0 3 1
+ 0 1 2
+ 1 3 2
+ 2 3 0
+ 4 5 7
+ 4 6 5
+ 5 6 7
+ 6 4 7
+ )";
+
+ IMeshBuilder mb(spec);
+ IMesh out = boolean_mesh(
+ mb.imesh,
+ BoolOpType::Difference,
+ 2,
+ [](int t) { return t < 4 ? 0 : 1; },
+ false,
+ nullptr,
+ &mb.arena);
+ out.populate_vert();
+ EXPECT_EQ(out.vert_size(), 4);
+ EXPECT_EQ(out.face_size(), 4);
+ if (DO_OBJ) {
+ write_obj_mesh(out, "tettet_coplanar_diff");
+ }
+}
+
+TEST(boolean_polymesh, CubeCubeStep)
+{
+ const char *spec = R"(16 12
+ 0 -1 0
+ 0 -1 2
+ 0 1 0
+ 0 1 2
+ 2 -1 0
+ 2 -1 2
+ 2 1 0
+ 2 1 2
+ -1 -1 -1
+ -1 -1 1
+ -1 1 -1
+ -1 1 1
+ 1 -1 -1
+ 1 -1 1
+ 1 1 -1
+ 1 1 1
+ 0 1 3 2
+ 2 3 7 6
+ 6 7 5 4
+ 4 5 1 0
+ 2 6 4 0
+ 7 3 1 5
+ 8 9 11 10
+ 10 11 15 14
+ 14 15 13 12
+ 12 13 9 8
+ 10 14 12 8
+ 15 11 9 13
+ )";
+
+ IMeshBuilder mb(spec);
+ IMesh out = boolean_mesh(
+ mb.imesh,
+ BoolOpType::Difference,
+ 2,
+ [](int t) { return t < 6 ? 0 : 1; },
+ false,
+ nullptr,
+ &mb.arena);
+ out.populate_vert();
+ EXPECT_EQ(out.vert_size(), 12);
+ EXPECT_EQ(out.face_size(), 8);
+ if (DO_OBJ) {
+ write_obj_mesh(out, "cubecubestep");
+ }
+}
+
+TEST(boolean_polymesh, CubeCyl4)
+{
+ const char *spec = R"(16 12
+ 0 1 -1
+ 0 1 1
+ 1 0 -1
+ 1 0 1
+ 0 -1 -1
+ 0 -1 1
+ -1 0 -1
+ -1 0 1
+ -1 -1 -1
+ -1 -1 1
+ -1 1 -1
+ -1 1 1
+ 1 -1 -1
+ 1 -1 1
+ 1 1 -1
+ 1 1 1
+ 0 1 3 2
+ 2 3 5 4
+ 3 1 7 5
+ 4 5 7 6
+ 6 7 1 0
+ 0 2 4 6
+ 8 9 11 10
+ 10 11 15 14
+ 14 15 13 12
+ 12 13 9 8
+ 10 14 12 8
+ 15 11 9 13
+ )";
+
+ IMeshBuilder mb(spec);
+ IMesh out = boolean_mesh(
+ mb.imesh,
+ BoolOpType::Difference,
+ 2,
+ [](int t) { return t < 6 ? 1 : 0; },
+ false,
+ nullptr,
+ &mb.arena);
+ out.populate_vert();
+ EXPECT_EQ(out.vert_size(), 16);
+ EXPECT_EQ(out.face_size(), 20);
+ if (DO_OBJ) {
+ write_obj_mesh(out, "cubecyl4");
+ }
+}
+
+TEST(boolean_polymesh, CubeCubesubdivDiff)
+{
+ /* A cube intersected by a subdivided cube that intersects first cubes edges exactly. */
+ const char *spec = R"(26 22
+ 2 1/3 2
+ 2 -1/3 2
+ 2 -1/3 0
+ 2 1/3 0
+ 0 -1/3 2
+ 0 1/3 2
+ 0 1/3 0
+ 0 -1/3 0
+ 1 1/3 2
+ 1 -1/3 2
+ 1 1/3 0
+ 1 -1/3 0
+ 0 -1/3 1
+ 0 1/3 1
+ 2 1/3 1
+ 2 -1/3 1
+ 1 1/3 1
+ 1 -1/3 1
+ -1 -1 -1
+ -1 -1 1
+ -1 1 -1
+ -1 1 1
+ 1 -1 -1
+ 1 -1 1
+ 1 1 -1
+ 1 1 1
+ 17 9 4 12
+ 13 6 7 12
+ 15 2 3 14
+ 11 7 6 10
+ 16 13 5 8
+ 9 1 0 8
+ 4 9 8 5
+ 14 16 8 0
+ 2 11 10 3
+ 15 1 9 17
+ 2 15 17 11
+ 3 10 16 14
+ 10 6 13 16
+ 1 15 14 0
+ 5 13 12 4
+ 11 17 12 7
+ 19 21 20 18
+ 21 25 24 20
+ 25 23 22 24
+ 23 19 18 22
+ 18 20 24 22
+ 23 25 21 19
+ )";
+
+ IMeshBuilder mb(spec);
+ IMesh out = boolean_mesh(
+ mb.imesh,
+ BoolOpType::Difference,
+ 2,
+ [](int t) { return t < 16 ? 1 : 0; },
+ false,
+ nullptr,
+ &mb.arena);
+ out.populate_vert();
+ EXPECT_EQ(out.vert_size(), 16);
+ EXPECT_EQ(out.face_size(), 10);
+ if (DO_OBJ) {
+ write_obj_mesh(out, "cubecubesubdivdiff");
+ }
+}
+
+TEST(boolean_polymesh, CubePlane)
+{
+ const char *spec = R"(12 7
+ -2 -2 0
+ 2 -2 0
+ -2 2 0
+ 2 2 0
+ -1 -1 -1
+ -1 -1 1
+ -1 1 -1
+ -1 1 1
+ 1 -1 -1
+ 1 -1 1
+ 1 1 -1
+ 1 1 1
+ 0 1 3 2
+ 4 5 7 6
+ 6 7 11 10
+ 10 11 9 8
+ 8 9 5 4
+ 6 10 8 4
+ 11 7 5 9
+)";
+
+ IMeshBuilder mb(spec);
+ IMesh out = boolean_mesh(
+ mb.imesh,
+ BoolOpType::Difference,
+ 2,
+ [](int t) { return t >= 1 ? 0 : 1; },
+ false,
+ nullptr,
+ &mb.arena);
+ out.populate_vert();
+ EXPECT_EQ(out.vert_size(), 8);
+ EXPECT_EQ(out.face_size(), 6);
+ if (DO_OBJ) {
+ write_obj_mesh(out, "cubeplane");
+ }
+}
+
+} // namespace blender::meshintersect::tests
+#endif
diff --git a/source/blender/blenlib/tests/BLI_mesh_intersect_test.cc b/source/blender/blenlib/tests/BLI_mesh_intersect_test.cc
new file mode 100644
index 00000000000..a9b916f6489
--- /dev/null
+++ b/source/blender/blenlib/tests/BLI_mesh_intersect_test.cc
@@ -0,0 +1,1074 @@
+/* Apache License, Version 2.0 */
+
+#include "testing/testing.h"
+
+#include <algorithm>
+#include <fstream>
+#include <iostream>
+
+#include "PIL_time.h"
+
+#include "BLI_array.hh"
+#include "BLI_math_mpq.hh"
+#include "BLI_mesh_intersect.hh"
+#include "BLI_mpq3.hh"
+#include "BLI_task.h"
+#include "BLI_vector.hh"
+
+#define DO_REGULAR_TESTS 1
+#define DO_PERF_TESTS 0
+
+#ifdef WITH_GMP
+namespace blender::meshintersect::tests {
+
+constexpr bool DO_OBJ = false;
+
+/* Build and hold an IMesh from a string spec. Also hold and own resources used by IMesh. */
+class IMeshBuilder {
+ public:
+ IMesh imesh;
+ IMeshArena arena;
+
+ /* "Edge orig" indices are an encoding of <input face#, position in face of start of edge>. */
+ static constexpr int MAX_FACE_LEN = 1000; /* Used for forming "orig edge" indices only. */
+
+ static int edge_index(int face_index, int facepos)
+ {
+ return face_index * MAX_FACE_LEN + facepos;
+ }
+
+ static std::pair<int, int> face_and_pos_for_edge_index(int e_index)
+ {
+ return std::pair<int, int>(e_index / MAX_FACE_LEN, e_index % MAX_FACE_LEN);
+ }
+
+ /*
+ * Spec should have form:
+ * #verts #faces
+ * mpq_class mpq_class mpq_clas [#verts lines]
+ * int int int ... [#faces lines; indices into verts for given face]
+ */
+ IMeshBuilder(const char *spec)
+ {
+ std::istringstream ss(spec);
+ std::string line;
+ getline(ss, line);
+ std::istringstream hdrss(line);
+ int nv, nf;
+ hdrss >> nv >> nf;
+ if (nv == 0 || nf == 0) {
+ return;
+ }
+ arena.reserve(nv, nf);
+ Vector<const Vert *> verts;
+ Vector<Face *> faces;
+ bool spec_ok = true;
+ int v_index = 0;
+ while (v_index < nv && spec_ok && getline(ss, line)) {
+ std::istringstream iss(line);
+ mpq_class p0;
+ mpq_class p1;
+ mpq_class p2;
+ iss >> p0 >> p1 >> p2;
+ spec_ok = !iss.fail();
+ if (spec_ok) {
+ verts.append(arena.add_or_find_vert(mpq3(p0, p1, p2), v_index));
+ }
+ ++v_index;
+ }
+ if (v_index != nv) {
+ spec_ok = false;
+ }
+ int f_index = 0;
+ while (f_index < nf && spec_ok && getline(ss, line)) {
+ std::istringstream fss(line);
+ Vector<const Vert *> face_verts;
+ Vector<int> edge_orig;
+ int fpos = 0;
+ while (spec_ok && fss >> v_index) {
+ if (v_index < 0 || v_index >= nv) {
+ spec_ok = false;
+ continue;
+ }
+ face_verts.append(verts[v_index]);
+ edge_orig.append(edge_index(f_index, fpos));
+ ++fpos;
+ }
+ if (fpos < 3) {
+ spec_ok = false;
+ }
+ if (spec_ok) {
+ Face *facep = arena.add_face(face_verts, f_index, edge_orig);
+ faces.append(facep);
+ }
+ ++f_index;
+ }
+ if (f_index != nf) {
+ spec_ok = false;
+ }
+ if (!spec_ok) {
+ std::cout << "Bad spec: " << spec;
+ return;
+ }
+ imesh = IMesh(faces);
+ }
+};
+
+/* Return a const Face * in mesh with verts equal to v0, v1, and v2, in
+ * some cyclic order; return nullptr if not found.
+ */
+static const Face *find_tri_with_verts(const IMesh &mesh,
+ const Vert *v0,
+ const Vert *v1,
+ const Vert *v2)
+{
+ Face f_arg({v0, v1, v2}, 0, NO_INDEX);
+ for (const Face *f : mesh.faces()) {
+ if (f->cyclic_equal(f_arg)) {
+ return f;
+ }
+ }
+ return nullptr;
+}
+
+/* How many instances of a triangle with v0, v1, v2 are in the mesh? */
+static int count_tris_with_verts(const IMesh &mesh, const Vert *v0, const Vert *v1, const Vert *v2)
+{
+ Face f_arg({v0, v1, v2}, 0, NO_INDEX);
+ int ans = 0;
+ for (const Face *f : mesh.faces()) {
+ if (f->cyclic_equal(f_arg)) {
+ ++ans;
+ }
+ }
+ return ans;
+}
+
+/* What is the starting position, if any, of the edge (v0, v1), in either order, in f? -1 if none.
+ */
+static int find_edge_pos_in_tri(const Vert *v0, const Vert *v1, const Face *f)
+{
+ for (int pos : f->index_range()) {
+ int nextpos = f->next_pos(pos);
+ if (((*f)[pos] == v0 && (*f)[nextpos] == v1) || ((*f)[pos] == v1 && (*f)[nextpos] == v0)) {
+ return static_cast<int>(pos);
+ }
+ }
+ return -1;
+}
+
+# if DO_REGULAR_TESTS
+TEST(mesh_intersect, Mesh)
+{
+ Vector<const Vert *> verts;
+ Vector<Face *> faces;
+ IMeshArena arena;
+
+ verts.append(arena.add_or_find_vert(mpq3(0, 0, 1), 0));
+ verts.append(arena.add_or_find_vert(mpq3(1, 0, 1), 1));
+ verts.append(arena.add_or_find_vert(mpq3(0.5, 1, 1), 2));
+ faces.append(arena.add_face(verts, 0, {10, 11, 12}));
+
+ IMesh mesh(faces);
+ const Face *f = mesh.face(0);
+ EXPECT_TRUE(f->is_tri());
+}
+
+TEST(mesh_intersect, OneTri)
+{
+ const char *spec = R"(3 1
+ 0 0 0
+ 1 0 0
+ 1/2 1 0
+ 0 1 2
+ )";
+
+ IMeshBuilder mb(spec);
+ IMesh imesh = trimesh_self_intersect(mb.imesh, &mb.arena);
+ imesh.populate_vert();
+ EXPECT_EQ(imesh.vert_size(), 3);
+ EXPECT_EQ(imesh.face_size(), 1);
+ const Face &f_in = *mb.imesh.face(0);
+ const Face &f_out = *imesh.face(0);
+ EXPECT_EQ(f_in.orig, f_out.orig);
+ for (int i = 0; i < 3; ++i) {
+ EXPECT_EQ(f_in[i], f_out[i]);
+ EXPECT_EQ(f_in.edge_orig[i], f_out.edge_orig[i]);
+ }
+}
+
+TEST(mesh_intersect, TriTri)
+{
+ const char *spec = R"(6 2
+ 0 0 0
+ 4 0 0
+ 0 4 0
+ 1 0 0
+ 2 0 0
+ 1 1 0
+ 0 1 2
+ 3 4 5
+ )";
+
+ /* Second triangle is smaller and congruent to first, resting on same base, partway along. */
+ IMeshBuilder mb(spec);
+ IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena);
+ out.populate_vert();
+ EXPECT_EQ(out.vert_size(), 6);
+ EXPECT_EQ(out.face_size(), 6);
+ if (out.vert_size() == 6 && out.face_size() == 6) {
+ const Vert *v0 = mb.arena.find_vert(mpq3(0, 0, 0));
+ const Vert *v1 = mb.arena.find_vert(mpq3(4, 0, 0));
+ const Vert *v2 = mb.arena.find_vert(mpq3(0, 4, 0));
+ const Vert *v3 = mb.arena.find_vert(mpq3(1, 0, 0));
+ const Vert *v4 = mb.arena.find_vert(mpq3(2, 0, 0));
+ const Vert *v5 = mb.arena.find_vert(mpq3(1, 1, 0));
+ EXPECT_TRUE(v0 != nullptr && v1 != nullptr && v2 != nullptr);
+ EXPECT_TRUE(v3 != nullptr && v4 != nullptr && v5 != nullptr);
+ if (v0 != nullptr && v1 != nullptr && v2 != nullptr && v3 != nullptr && v4 != nullptr &&
+ v5 != nullptr) {
+ EXPECT_EQ(v0->orig, 0);
+ EXPECT_EQ(v1->orig, 1);
+ const Face *f0 = find_tri_with_verts(out, v4, v1, v5);
+ const Face *f1 = find_tri_with_verts(out, v3, v4, v5);
+ const Face *f2 = find_tri_with_verts(out, v0, v3, v5);
+ const Face *f3 = find_tri_with_verts(out, v0, v5, v2);
+ const Face *f4 = find_tri_with_verts(out, v5, v1, v2);
+ EXPECT_TRUE(f0 != nullptr && f1 != nullptr && f2 != nullptr && f3 != nullptr &&
+ f4 != nullptr);
+ /* For boolean to work right, there need to be two copies of the smaller triangle in the
+ * output. */
+ EXPECT_EQ(count_tris_with_verts(out, v3, v4, v5), 2);
+ if (f0 != nullptr && f1 != nullptr && f2 != nullptr && f3 != nullptr && f4 != nullptr) {
+ EXPECT_EQ(f0->orig, 0);
+ EXPECT_TRUE(f1->orig == 0 || f1->orig == 1);
+ EXPECT_EQ(f2->orig, 0);
+ EXPECT_EQ(f3->orig, 0);
+ EXPECT_EQ(f4->orig, 0);
+ }
+ int e03 = find_edge_pos_in_tri(v0, v3, f2);
+ int e34 = find_edge_pos_in_tri(v3, v4, f1);
+ int e45 = find_edge_pos_in_tri(v4, v5, f1);
+ int e05 = find_edge_pos_in_tri(v0, v5, f3);
+ int e15 = find_edge_pos_in_tri(v1, v5, f0);
+ EXPECT_TRUE(e03 != -1 && e34 != -1 && e45 != -1 && e05 != -1 && e15 != -1);
+ if (e03 != -1 && e34 != -1 && e45 != -1 && e05 != -1 && e15 != -1) {
+ EXPECT_EQ(f2->edge_orig[e03], 0);
+ EXPECT_TRUE(f1->edge_orig[e34] == 0 ||
+ f1->edge_orig[e34] == 1 * IMeshBuilder::MAX_FACE_LEN);
+ EXPECT_EQ(f1->edge_orig[e45], 1 * IMeshBuilder::MAX_FACE_LEN + 1);
+ EXPECT_EQ(f3->edge_orig[e05], NO_INDEX);
+ EXPECT_EQ(f0->edge_orig[e15], NO_INDEX);
+ }
+ }
+ }
+ if (DO_OBJ) {
+ write_obj_mesh(out, "tritri");
+ }
+}
+
+TEST(mesh_intersect, TriTriReversed)
+{
+ /* Like TriTri but with triangles of opposite orientation.
+ * This matters because projection to 2D will now need reversed triangles. */
+ const char *spec = R"(6 2
+ 0 0 0
+ 4 0 0
+ 0 4 0
+ 1 0 0
+ 2 0 0
+ 1 1 0
+ 0 2 1
+ 3 5 4
+ )";
+
+ IMeshBuilder mb(spec);
+ IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena);
+ out.populate_vert();
+ EXPECT_EQ(out.vert_size(), 6);
+ EXPECT_EQ(out.face_size(), 6);
+ if (out.vert_size() == 6 && out.face_size() == 6) {
+ const Vert *v0 = mb.arena.find_vert(mpq3(0, 0, 0));
+ const Vert *v1 = mb.arena.find_vert(mpq3(4, 0, 0));
+ const Vert *v2 = mb.arena.find_vert(mpq3(0, 4, 0));
+ const Vert *v3 = mb.arena.find_vert(mpq3(1, 0, 0));
+ const Vert *v4 = mb.arena.find_vert(mpq3(2, 0, 0));
+ const Vert *v5 = mb.arena.find_vert(mpq3(1, 1, 0));
+ EXPECT_TRUE(v0 != nullptr && v1 != nullptr && v2 != nullptr);
+ EXPECT_TRUE(v3 != nullptr && v4 != nullptr && v5 != nullptr);
+ if (v0 != nullptr && v1 != nullptr && v2 != nullptr && v3 != nullptr && v4 != nullptr &&
+ v5 != nullptr) {
+ EXPECT_EQ(v0->orig, 0);
+ EXPECT_EQ(v1->orig, 1);
+ const Face *f0 = find_tri_with_verts(out, v4, v5, v1);
+ const Face *f1 = find_tri_with_verts(out, v3, v5, v4);
+ const Face *f2 = find_tri_with_verts(out, v0, v5, v3);
+ const Face *f3 = find_tri_with_verts(out, v0, v2, v5);
+ const Face *f4 = find_tri_with_verts(out, v5, v2, v1);
+ EXPECT_TRUE(f0 != nullptr && f1 != nullptr && f2 != nullptr && f3 != nullptr &&
+ f4 != nullptr);
+ /* For boolean to work right, there need to be two copies of the smaller triangle in the
+ * output. */
+ EXPECT_EQ(count_tris_with_verts(out, v3, v5, v4), 2);
+ if (f0 != nullptr && f1 != nullptr && f2 != nullptr && f3 != nullptr && f4 != nullptr) {
+ EXPECT_EQ(f0->orig, 0);
+ EXPECT_TRUE(f1->orig == 0 || f1->orig == 1);
+ EXPECT_EQ(f2->orig, 0);
+ EXPECT_EQ(f3->orig, 0);
+ EXPECT_EQ(f4->orig, 0);
+ }
+ int e03 = find_edge_pos_in_tri(v0, v3, f2);
+ int e34 = find_edge_pos_in_tri(v3, v4, f1);
+ int e45 = find_edge_pos_in_tri(v4, v5, f1);
+ int e05 = find_edge_pos_in_tri(v0, v5, f3);
+ int e15 = find_edge_pos_in_tri(v1, v5, f0);
+ EXPECT_TRUE(e03 != -1 && e34 != -1 && e45 != -1 && e05 != -1 && e15 != -1);
+ if (e03 != -1 && e34 != -1 && e45 != -1 && e05 != -1 && e15 != -1) {
+ EXPECT_EQ(f2->edge_orig[e03], 2);
+ EXPECT_TRUE(f1->edge_orig[e34] == 2 ||
+ f1->edge_orig[e34] == 1 * IMeshBuilder::MAX_FACE_LEN + 2);
+ EXPECT_EQ(f1->edge_orig[e45], 1 * IMeshBuilder::MAX_FACE_LEN + 1);
+ EXPECT_EQ(f3->edge_orig[e05], NO_INDEX);
+ EXPECT_EQ(f0->edge_orig[e15], NO_INDEX);
+ }
+ }
+ }
+ if (DO_OBJ) {
+ write_obj_mesh(out, "tritrirev");
+ }
+}
+
+TEST(mesh_intersect, TwoTris)
+{
+ Array<mpq3> verts = {
+ mpq3(1, 1, 1), mpq3(1, 4, 1), mpq3(1, 1, 4), /* T0 */
+ mpq3(2, 2, 2), mpq3(-3, 3, 2), mpq3(-4, 1, 3), /* T1 */
+ mpq3(2, 2, 2), mpq3(-3, 3, 2), mpq3(0, 3, 5), /* T2 */
+ mpq3(2, 2, 2), mpq3(-3, 3, 2), mpq3(0, 3, 3), /* T3 */
+ mpq3(1, 0, 0), mpq3(2, 4, 1), mpq3(-3, 2, 2), /* T4 */
+ mpq3(0, 2, 1), mpq3(-2, 3, 3), mpq3(0, 1, 3), /* T5 */
+ mpq3(1.5, 2, 0.5), mpq3(-2, 3, 3), mpq3(0, 1, 3), /* T6 */
+ mpq3(1, 0, 0), mpq3(-2, 3, 3), mpq3(0, 1, 3), /* T7 */
+ mpq3(1, 0, 0), mpq3(-3, 2, 2), mpq3(0, 1, 3), /* T8 */
+ mpq3(1, 0, 0), mpq3(-1, 1, 1), mpq3(0, 1, 3), /* T9 */
+ mpq3(3, -1, -1), mpq3(-1, 1, 1), mpq3(0, 1, 3), /* T10 */
+ mpq3(0, 0.5, 0.5), mpq3(-1, 1, 1), mpq3(0, 1, 3), /* T11 */
+ mpq3(2, 1, 1), mpq3(3, 5, 2), mpq3(-2, 3, 3), /* T12 */
+ mpq3(2, 1, 1), mpq3(3, 5, 2), mpq3(-2, 3, 4), /* T13 */
+ mpq3(2, 2, 5), mpq3(-3, 3, 5), mpq3(0, 3, 10), /* T14 */
+ mpq3(0, 0, 0), mpq3(4, 4, 0), mpq3(-4, 2, 4), /* T15 */
+ mpq3(0, 1.5, 1), mpq3(1, 2.5, 1), mpq3(-1, 2, 2), /* T16 */
+ mpq3(3, 0, -2), mpq3(7, 4, -2), mpq3(-1, 2, 2), /* T17 */
+ mpq3(3, 0, -2), mpq3(3, 6, 2), mpq3(-1, 2, 2), /* T18 */
+ mpq3(7, 4, -2), mpq3(3, 6, 2), mpq3(-1, 2, 2), /* T19 */
+ mpq3(5, 2, -2), mpq3(1, 4, 2), mpq3(-3, 0, 2), /* T20 */
+ mpq3(2, 2, 0), mpq3(1, 4, 2), mpq3(-3, 0, 2), /* T21 */
+ mpq3(0, 0, 0), mpq3(4, 4, 0), mpq3(-3, 0, 2), /* T22 */
+ mpq3(0, 0, 0), mpq3(4, 4, 0), mpq3(-1, 2, 2), /* T23 */
+ mpq3(2, 2, 0), mpq3(4, 4, 0), mpq3(0, 3, 2), /* T24 */
+ mpq3(0, 0, 0), mpq3(-4, 2, 4), mpq3(4, 4, 0), /* T25 */
+ };
+ struct two_tri_test_spec {
+ int t0;
+ int t1;
+ int nv_out;
+ int nf_out;
+ };
+ Array<two_tri_test_spec> test_tris = Span<two_tri_test_spec>{
+ {0, 1, 8, 8}, /* 0: T1 pierces T0 inside at (1,11/6,13/6) and (1,11/5,2). */
+ {0, 2, 8, 8}, /* 1: T2 intersects T0 inside (1,11/5,2) and edge (1,7/3,8/3). */
+ {0, 3, 8, 7}, /* 2: T3 intersects T0 (1,11/5,2) and edge-edge (1,5/2,5/2). */
+ {4, 5, 6, 4}, /* 3: T5 touches T4 inside (0,2,1). */
+ {4, 6, 6, 3}, /* 4: T6 touches T4 on edge (3/2,2/1/2). */
+ {4, 7, 5, 2}, /* 5: T7 touches T4 on vert (1,0,0). */
+ {4, 8, 4, 2}, /* 6: T8 shared edge with T4 (1,0,0)(-3,2,2). */
+ {4, 9, 5, 3}, /* 7: T9 edge (1,0,0)(-1,1,1) is subset of T4 edge. */
+ {4, 10, 6, 4}, /* 8: T10 edge overlaps T4 edge with seg (-1,1,0)(1,0,0). */
+ {4, 11, 6, 4}, /* 9: T11 edge (-1,1,1)(0,1/2,1/2) inside T4 edge. */
+ {4, 12, 6, 2}, /* 10: parallel planes, not intersecting. */
+ {4, 13, 6, 2}, /* 11: non-parallel planes, not intersecting, all one side. */
+ {0, 14, 6, 2}, /* 12: non-paralel planes, not intersecting, alternate sides. */
+ /* Following are all coplanar cases. */
+ {15, 16, 6, 8}, /* 13: T16 inside T15. Note: dup'd tri is expected. */
+ {15, 17, 8, 8}, /* 14: T17 intersects one edge of T15 at (1,1,0)(3,3,0). */
+ {15, 18, 10, 12}, /* 15: T18 intersects T15 at (1,1,0)(3,3,0)(3,15/4,1/2)(0,3,2). */
+ {15, 19, 8, 10}, /* 16: T19 intersects T15 at (3,3,0)(0,3,2). */
+ {15, 20, 12, 14}, /* 17: T20 intersects T15 on three edges, six intersects. */
+ {15, 21, 10, 11}, /* 18: T21 intersects T15 on three edges, touching one. */
+ {15, 22, 5, 4}, /* 19: T22 shares edge T15, one other outside. */
+ {15, 23, 4, 4}, /* 20: T23 shares edge T15, one other outside. */
+ {15, 24, 5, 4}, /* 21: T24 shares two edges with T15. */
+ {15, 25, 3, 2}, /* 22: T25 same T15, reverse orientation. */
+ };
+ static int perms[6][3] = {{0, 1, 2}, {0, 2, 1}, {1, 0, 2}, {1, 2, 0}, {2, 0, 1}, {2, 1, 0}};
+
+ const int do_only_test = -1; /* Make this negative to do all tests. */
+ for (int test = 0; test < test_tris.size(); ++test) {
+ if (do_only_test >= 0 && test != do_only_test) {
+ continue;
+ }
+ int tri1_index = test_tris[test].t0;
+ int tri2_index = test_tris[test].t1;
+ int co1_i = 3 * tri1_index;
+ int co2_i = 3 * tri2_index;
+
+ const bool verbose = false;
+
+ if (verbose) {
+ std::cout << "\nTest " << test << ": T" << tri1_index << " intersect T" << tri2_index
+ << "\n";
+ }
+
+ const bool do_all_perms = true;
+ const int perm_limit = do_all_perms ? 3 : 1;
+
+ for (int i = 0; i < perm_limit; ++i) {
+ for (int j = 0; j < perm_limit; ++j) {
+ if (do_all_perms && verbose) {
+ std::cout << "\nperms " << i << " " << j << "\n";
+ }
+ IMeshArena arena;
+ arena.reserve(2 * 3, 2);
+ Array<const Vert *> f0_verts(3);
+ Array<const Vert *> f1_verts(3);
+ for (int k = 0; k < 3; ++k) {
+ f0_verts[k] = arena.add_or_find_vert(verts[co1_i + perms[i][k]], k);
+ }
+ for (int k = 0; k < 3; ++k) {
+ f1_verts[k] = arena.add_or_find_vert(verts[co2_i + perms[i][k]], k + 3);
+ }
+ Face *f0 = arena.add_face(f0_verts, 0, {0, 1, 2});
+ Face *f1 = arena.add_face(f1_verts, 1, {3, 4, 5});
+ IMesh in_mesh({f0, f1});
+ IMesh out_mesh = trimesh_self_intersect(in_mesh, &arena);
+ out_mesh.populate_vert();
+ EXPECT_EQ(out_mesh.vert_size(), test_tris[test].nv_out);
+ EXPECT_EQ(out_mesh.face_size(), test_tris[test].nf_out);
+ bool constexpr dump_input = true;
+ if (DO_OBJ && i == 0 && j == 0) {
+ if (dump_input) {
+ std::string name = "test_tt_in" + std::to_string(test);
+ write_obj_mesh(in_mesh, name);
+ }
+ std::string name = "test_tt" + std::to_string(test);
+ write_obj_mesh(out_mesh, name);
+ }
+ }
+ }
+ }
+}
+
+TEST(mesh_intersect, OverlapCluster)
+{
+ /* Chain of 5 overlapping coplanar tris.
+ * Ordered so that clustering will make two separate clusters
+ * that it will have to merge into one cluster with everything. */
+ const char *spec = R"(15 5
+ 0 0 0
+ 1 0 0
+ 1/2 1 0
+ 1/2 0 0
+ 3/2 0 0
+ 1 1 0
+ 1 0 0
+ 2 0 0
+ 3/2 1 0
+ 3/2 0 0
+ 5/2 0 0
+ 2 1 0
+ 2 0 0
+ 3 0 0
+ 5/2 1 0
+ 0 1 2
+ 3 4 5
+ 9 10 11
+ 12 13 14
+ 6 7 8
+ )";
+
+ IMeshBuilder mb(spec);
+ IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena);
+ out.populate_vert();
+ EXPECT_EQ(out.vert_size(), 16);
+ EXPECT_EQ(out.face_size(), 18);
+ if (DO_OBJ) {
+ write_obj_mesh(out, "overlapcluster");
+ }
+}
+
+TEST(mesh_intersect, TriCornerCross1)
+{
+ /* A corner formed by 3 tris, and a 4th crossing two of them. */
+ const char *spec = R"(12 4
+ 0 0 0
+ 1 0 0
+ 0 0 1
+ 0 0 0
+ 0 1 0
+ 0 0 1
+ 0 0 0
+ 1 0 0
+ 0 1 0
+ 1 1 1/2
+ 1 -2 1/2
+ -2 1 1/2
+ 0 1 2
+ 3 4 5
+ 6 7 8
+ 9 10 11
+ )";
+
+ IMeshBuilder mb(spec);
+ IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena);
+ out.populate_vert();
+ EXPECT_EQ(out.vert_size(), 10);
+ EXPECT_EQ(out.face_size(), 14);
+ if (DO_OBJ) {
+ write_obj_mesh(out, "test_tc_1");
+ }
+}
+
+TEST(mesh_intersect, TriCornerCross2)
+{
+ /* A corner formed by 3 tris, and a 4th coplanar with base. */
+ const char *spec = R"(12 4
+ 0 0 0
+ 1 0 0
+ 0 0 1
+ 0 0 0
+ 0 1 0
+ 0 0 1
+ 0 0 0
+ 1 0 0
+ 0 1 0
+ 1 1 0
+ 1 -2 0
+ -2 1 0
+ 0 1 2
+ 3 4 5
+ 6 7 8
+ 9 10 11
+ )";
+
+ IMeshBuilder mb(spec);
+ IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena);
+ out.populate_vert();
+ EXPECT_EQ(out.vert_size(), 7);
+ EXPECT_EQ(out.face_size(), 8);
+ if (DO_OBJ) {
+ write_obj_mesh(out, "test_tc_2");
+ }
+}
+
+TEST(mesh_intersect, TriCornerCross3)
+{
+ /* A corner formed by 3 tris, and a 4th crossing all 3. */
+ const char *spec = R"(12 4
+ 0 0 0
+ 1 0 0
+ 0 0 1
+ 0 0 0
+ 0 1 0
+ 0 0 1
+ 0 0 0
+ 1 0 0
+ 0 1 0
+ 3/2 -1/2 -1/4
+ -1/2 3/2 -1/4
+ -1/2 -1/2 3/4
+ 0 1 2
+ 3 4 5
+ 6 7 8
+ 9 10 11
+ )";
+
+ IMeshBuilder mb(spec);
+ IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena);
+ out.populate_vert();
+ EXPECT_EQ(out.vert_size(), 10);
+ EXPECT_EQ(out.face_size(), 16);
+ if (DO_OBJ) {
+ write_obj_mesh(out, "test_tc_3");
+ }
+}
+
+TEST(mesh_intersect, TetTet)
+{
+ const char *spec = R"(8 8
+ 0 0 0
+ 2 0 0
+ 1 2 0
+ 1 1 2
+ 0 0 1
+ 2 0 1
+ 1 2 1
+ 1 1 3
+ 0 1 2
+ 0 3 1
+ 1 3 2
+ 2 3 0
+ 4 5 6
+ 4 7 5
+ 5 7 6
+ 6 7 4
+ )";
+
+ IMeshBuilder mb(spec);
+ IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena);
+ out.populate_vert();
+ EXPECT_EQ(out.vert_size(), 11);
+ EXPECT_EQ(out.face_size(), 20);
+ /* Expect there to be a triangle with these three verts, oriented this way, with original face 1.
+ */
+ const Vert *v1 = mb.arena.find_vert(mpq3(2, 0, 0));
+ const Vert *v8 = mb.arena.find_vert(mpq3(0.5, 0.5, 1));
+ const Vert *v9 = mb.arena.find_vert(mpq3(1.5, 0.5, 1));
+ EXPECT_TRUE(v1 != nullptr && v8 != nullptr && v9 != nullptr);
+ const Face *f = mb.arena.find_face({v1, v8, v9});
+ EXPECT_NE(f, nullptr);
+ EXPECT_EQ(f->orig, 1);
+ int v1pos = f->vert[0] == v1 ? 0 : (f->vert[1] == v1 ? 1 : 2);
+ EXPECT_EQ(f->edge_orig[v1pos], NO_INDEX);
+ EXPECT_EQ(f->edge_orig[(v1pos + 1) % 3], NO_INDEX);
+ EXPECT_EQ(f->edge_orig[(v1pos + 2) % 3], 1001);
+ EXPECT_EQ(f->is_intersect[v1pos], false);
+ EXPECT_EQ(f->is_intersect[(v1pos + 1) % 3], true);
+ EXPECT_EQ(f->is_intersect[(v1pos + 2) % 3], false);
+ if (DO_OBJ) {
+ write_obj_mesh(out, "test_tc_3");
+ }
+}
+
+TEST(mesh_intersect, CubeCubeStep)
+{
+ const char *spec = R"(16 24
+ 0 -1 0
+ 0 -1 2
+ 0 1 0
+ 0 1 2
+ 2 -1 0
+ 2 -1 2
+ 2 1 0
+ 2 1 2
+ -1 -1 -1
+ -1 -1 1
+ -1 1 -1
+ -1 1 1
+ 1 -1 -1
+ 1 -1 1
+ 1 1 -1
+ 1 1 1
+ 0 1 3
+ 0 3 2
+ 2 3 7
+ 2 7 6
+ 6 7 5
+ 6 5 4
+ 4 5 1
+ 4 1 0
+ 2 6 4
+ 2 4 0
+ 7 3 1
+ 7 1 5
+ 8 9 11
+ 8 11 10
+ 10 11 15
+ 10 15 14
+ 14 15 13
+ 14 13 12
+ 12 13 9
+ 12 9 8
+ 10 14 12
+ 10 12 8
+ 15 11 9
+ 15 9 13
+ )";
+
+ IMeshBuilder mb(spec);
+ IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena);
+ out.populate_vert();
+ EXPECT_EQ(out.vert_size(), 22);
+ EXPECT_EQ(out.face_size(), 56);
+ if (DO_OBJ) {
+ write_obj_mesh(out, "test_cubecubestep");
+ }
+
+ IMeshBuilder mb2(spec);
+ IMesh out2 = trimesh_nary_intersect(
+ mb2.imesh, 2, [](int t) { return t < 12 ? 0 : 1; }, false, &mb2.arena);
+ out2.populate_vert();
+ EXPECT_EQ(out2.vert_size(), 22);
+ EXPECT_EQ(out2.face_size(), 56);
+ if (DO_OBJ) {
+ write_obj_mesh(out2, "test_cubecubestep_nary");
+ }
+}
+# endif
+
+# if DO_PERF_TESTS
+
+static void get_sphere_params(
+ int nrings, int nsegs, bool triangulate, int *r_num_verts, int *r_num_faces)
+{
+ *r_num_verts = nsegs * (nrings - 1) + 2;
+ if (triangulate) {
+ *r_num_faces = 2 * nsegs + 2 * nsegs * (nrings - 2);
+ }
+ else {
+ *r_num_faces = nsegs * nrings;
+ }
+}
+
+static void fill_sphere_data(int nrings,
+ int nsegs,
+ const double3 &center,
+ double radius,
+ bool triangulate,
+ MutableSpan<Face *> face,
+ int vid_start,
+ int fid_start,
+ IMeshArena *arena)
+{
+ int num_verts;
+ int num_faces;
+ get_sphere_params(nrings, nsegs, triangulate, &num_verts, &num_faces);
+ BLI_assert(num_faces == face.size());
+ Array<const Vert *> vert(num_verts);
+ const bool nrings_even = (nrings % 2 == 0);
+ int half_nrings = nrings / 2;
+ const bool nsegs_even = (nsegs % 2) == 0;
+ const bool nsegs_four_divisible = (nsegs % 4 == 0);
+ int half_nsegs = nrings;
+ int quarter_nsegs = half_nsegs / 2;
+ double delta_phi = 2 * M_PI / nsegs;
+ double delta_theta = M_PI / nrings;
+ int fid = fid_start;
+ int vid = vid_start;
+ auto vert_index_fn = [nrings, num_verts](int seg, int ring) {
+ if (ring == 0) { /* Top vert. */
+ return num_verts - 2;
+ }
+ if (ring == nrings) { /* Bottom vert. */
+ return num_verts - 1;
+ }
+ return seg * (nrings - 1) + (ring - 1);
+ };
+ auto face_index_fn = [nrings](int seg, int ring) { return seg * nrings + ring; };
+ auto tri_index_fn = [nrings, nsegs](int seg, int ring, int tri) {
+ if (ring == 0) {
+ return seg;
+ }
+ if (ring < nrings - 1) {
+ return nsegs + 2 * (ring - 1) * nsegs + 2 * seg + tri;
+ }
+ return nsegs + 2 * (nrings - 2) * nsegs + seg;
+ };
+ Array<int> eid = {0, 0, 0, 0}; /* Don't care about edge ids. */
+ /*
+ * (x, y , z) is given from inclination theta and azimuth phi,
+ * where 0 <= theta <= pi; 0 <= phi <= 2pi.
+ * x = radius * sin(theta) cos(phi)
+ * y = radius * sin(theta) sin(phi)
+ * z = radius * cos(theta)
+ */
+ for (int s = 0; s < nsegs; ++s) {
+ double phi = s * delta_phi;
+ double sin_phi;
+ double cos_phi;
+ /* Avoid use of trig functions for pi/2 divisible angles. */
+ if (s == 0) {
+ /* phi = 0. */
+ sin_phi = 0.0;
+ cos_phi = 1.0;
+ }
+ else if (nsegs_even && s == half_nsegs) {
+ /* phi = pi. */
+ sin_phi = 0.0;
+ cos_phi = -1.0;
+ }
+ else if (nsegs_four_divisible && s == quarter_nsegs) {
+ /* phi = pi/2. */
+ sin_phi = 1.0;
+ cos_phi = 0.0;
+ }
+ else if (nsegs_four_divisible && s == 3 * quarter_nsegs) {
+ /* phi = 3pi/2. */
+ sin_phi = -1.0;
+ cos_phi = 0.0;
+ }
+ else {
+ sin_phi = sin(phi);
+ cos_phi = cos(phi);
+ }
+ for (int r = 1; r < nrings; ++r) {
+ double theta = r * delta_theta;
+ double r_sin_theta;
+ double r_cos_theta;
+ if (nrings_even && r == half_nrings) {
+ /* theta = pi/2. */
+ r_sin_theta = radius;
+ r_cos_theta = 0.0;
+ }
+ else {
+ r_sin_theta = radius * sin(theta);
+ r_cos_theta = radius * cos(theta);
+ }
+ double x = r_sin_theta * cos_phi + center[0];
+ double y = r_sin_theta * sin_phi + center[1];
+ double z = r_cos_theta + center[2];
+ const Vert *v = arena->add_or_find_vert(mpq3(x, y, z), vid++);
+ vert[vert_index_fn(s, r)] = v;
+ }
+ }
+ const Vert *vtop = arena->add_or_find_vert(mpq3(center[0], center[1], center[2] + radius),
+ vid++);
+ const Vert *vbot = arena->add_or_find_vert(mpq3(center[0], center[1], center[2] - radius),
+ vid++);
+ vert[vert_index_fn(0, 0)] = vtop;
+ vert[vert_index_fn(0, nrings)] = vbot;
+ for (int s = 0; s < nsegs; ++s) {
+ int snext = (s + 1) % nsegs;
+ for (int r = 0; r < nrings; ++r) {
+ int rnext = r + 1;
+ int i0 = vert_index_fn(s, r);
+ int i1 = vert_index_fn(s, rnext);
+ int i2 = vert_index_fn(snext, rnext);
+ int i3 = vert_index_fn(snext, r);
+ Face *f;
+ Face *f2 = nullptr;
+ if (r == 0) {
+ f = arena->add_face({vert[i0], vert[i1], vert[i2]}, fid++, eid);
+ }
+ else if (r == nrings - 1) {
+ f = arena->add_face({vert[i0], vert[i1], vert[i3]}, fid++, eid);
+ }
+ else {
+ if (triangulate) {
+ f = arena->add_face({vert[i0], vert[i1], vert[i2]}, fid++, eid);
+ f2 = arena->add_face({vert[i2], vert[i3], vert[i0]}, fid++, eid);
+ }
+ else {
+ f = arena->add_face({vert[i0], vert[i1], vert[i2], vert[i3]}, fid++, eid);
+ }
+ }
+ if (triangulate) {
+ int f_index = tri_index_fn(s, r, 0);
+ face[f_index] = f;
+ if (r != 0 && r != nrings - 1) {
+ int f_index2 = tri_index_fn(s, r, 1);
+ face[f_index2] = f2;
+ }
+ }
+ else {
+ int f_index = face_index_fn(s, r);
+ face[f_index] = f;
+ }
+ }
+ }
+}
+
+static void spheresphere_test(int nrings, double y_offset, bool use_self)
+{
+ /* Make two uvspheres with nrings rings ad 2*nrings segments. */
+ if (nrings < 2) {
+ return;
+ }
+ BLI_task_scheduler_init(); /* Without this, no parallelism. */
+ double time_start = PIL_check_seconds_timer();
+ IMeshArena arena;
+ int nsegs = 2 * nrings;
+ int num_sphere_verts;
+ int num_sphere_tris;
+ get_sphere_params(nrings, nsegs, true, &num_sphere_verts, &num_sphere_tris);
+ Array<Face *> tris(2 * num_sphere_tris);
+ arena.reserve(2 * num_sphere_verts, 2 * num_sphere_tris);
+ double3 center1(0.0, 0.0, 0.0);
+ fill_sphere_data(nrings,
+ nsegs,
+ center1,
+ 1.0,
+ true,
+ MutableSpan<Face *>(tris.begin(), num_sphere_tris),
+ 0,
+ 0,
+ &arena);
+ double3 center2(0.0, y_offset, 0.0);
+ fill_sphere_data(nrings,
+ nsegs,
+ center2,
+ 1.0,
+ true,
+ MutableSpan<Face *>(tris.begin() + num_sphere_tris, num_sphere_tris),
+ num_sphere_verts,
+ num_sphere_verts,
+ &arena);
+ IMesh mesh(tris);
+ double time_create = PIL_check_seconds_timer();
+ // write_obj_mesh(mesh, "spheresphere_in");
+ IMesh out;
+ if (use_self) {
+ out = trimesh_self_intersect(mesh, &arena);
+ }
+ else {
+ int nf = num_sphere_tris;
+ out = trimesh_nary_intersect(
+ mesh, 2, [nf](int t) { return t < nf ? 0 : 1; }, false, &arena);
+ }
+ double time_intersect = PIL_check_seconds_timer();
+ std::cout << "Create time: " << time_create - time_start << "\n";
+ std::cout << "Intersect time: " << time_intersect - time_create << "\n";
+ std::cout << "Total time: " << time_intersect - time_start << "\n";
+ if (DO_OBJ) {
+ write_obj_mesh(out, "spheresphere");
+ }
+ BLI_task_scheduler_exit();
+}
+
+static void get_grid_params(
+ int x_subdiv, int y_subdiv, bool triangulate, int *r_num_verts, int *r_num_faces)
+{
+ *r_num_verts = x_subdiv * y_subdiv;
+ if (triangulate) {
+ *r_num_faces = 2 * (x_subdiv - 1) * (y_subdiv - 1);
+ }
+ else {
+ *r_num_faces = (x_subdiv - 1) * (y_subdiv - 1);
+ }
+}
+
+static void fill_grid_data(int x_subdiv,
+ int y_subdiv,
+ bool triangulate,
+ double size,
+ const double3 &center,
+ MutableSpan<Face *> face,
+ int vid_start,
+ int fid_start,
+ IMeshArena *arena)
+{
+ if (x_subdiv <= 1 || y_subdiv <= 1) {
+ return;
+ }
+ int num_verts;
+ int num_faces;
+ get_grid_params(x_subdiv, y_subdiv, triangulate, &num_verts, &num_faces);
+ BLI_assert(face.size() == num_faces);
+ Array<const Vert *> vert(num_verts);
+ auto vert_index_fn = [x_subdiv](int ix, int iy) { return iy * x_subdiv + ix; };
+ auto face_index_fn = [x_subdiv](int ix, int iy) { return iy * (x_subdiv - 1) + ix; };
+ auto tri_index_fn = [x_subdiv](int ix, int iy, int tri) {
+ return 2 * iy * (x_subdiv - 1) + 2 * ix + tri;
+ };
+ Array<int> eid = {0, 0, 0, 0}; /* Don't care about edge ids. */
+ double r = size / 2.0;
+ double delta_x = size / (x_subdiv - 1);
+ double delta_y = size / (y_subdiv - 1);
+ int vid = vid_start;
+ for (int iy = 0; iy < y_subdiv; ++iy) {
+ for (int ix = 0; ix < x_subdiv; ++ix) {
+ double x = center[0] - r + ix * delta_x;
+ double y = center[1] - r + iy * delta_y;
+ double z = center[2];
+ const Vert *v = arena->add_or_find_vert(mpq3(x, y, z), vid++);
+ vert[vert_index_fn(ix, iy)] = v;
+ }
+ }
+ int fid = fid_start;
+ for (int iy = 0; iy < y_subdiv - 1; ++iy) {
+ for (int ix = 0; ix < x_subdiv - 1; ++ix) {
+ int i0 = vert_index_fn(ix, iy);
+ int i1 = vert_index_fn(ix, iy + 1);
+ int i2 = vert_index_fn(ix + 1, iy + 1);
+ int i3 = vert_index_fn(ix + 1, iy);
+ if (triangulate) {
+ Face *f = arena->add_face({vert[i0], vert[i1], vert[i2]}, fid++, eid);
+ Face *f2 = arena->add_face({vert[i2], vert[i3], vert[i0]}, fid++, eid);
+ face[tri_index_fn(ix, iy, 0)] = f;
+ face[tri_index_fn(ix, iy, 1)] = f2;
+ }
+ else {
+ Face *f = arena->add_face({vert[i0], vert[i1], vert[i2], vert[i3]}, fid++, eid);
+ face[face_index_fn(ix, iy)] = f;
+ }
+ }
+ }
+}
+
+static void spheregrid_test(int nrings, int grid_level, double z_offset, bool use_self)
+{
+ /* Make a uvsphere and a grid.
+ * The sphere is radius 1, has nrings rings and 2 * nrings segs,
+ * and is centered at (0,0,z_offset).
+ * The plane is 4x4, has 2**grid_level subdivisions x and y,
+ * and is centered at the origin. */
+ if (nrings < 2 || grid_level < 1) {
+ return;
+ }
+ BLI_task_scheduler_init(); /* Without this, no parallelism. */
+ double time_start = PIL_check_seconds_timer();
+ IMeshArena arena;
+ int num_sphere_verts;
+ int num_sphere_tris;
+ int nsegs = 2 * nrings;
+ int num_grid_verts;
+ int num_grid_tris;
+ int subdivs = 1 << grid_level;
+ get_sphere_params(nrings, nsegs, true, &num_sphere_verts, &num_sphere_tris);
+ get_grid_params(subdivs, subdivs, true, &num_grid_verts, &num_grid_tris);
+ Array<Face *> tris(num_sphere_tris + num_grid_tris);
+ arena.reserve(num_sphere_verts + num_grid_verts, num_sphere_tris + num_grid_tris);
+ double3 center(0.0, 0.0, z_offset);
+ fill_sphere_data(nrings,
+ nsegs,
+ center,
+ 1.0,
+ true,
+ MutableSpan<Face *>(tris.begin(), num_sphere_tris),
+ 0,
+ 0,
+ &arena);
+ fill_grid_data(subdivs,
+ subdivs,
+ true,
+ 4.0,
+ double3(0, 0, 0),
+ MutableSpan<Face *>(tris.begin() + num_sphere_tris, num_grid_tris),
+ num_sphere_verts,
+ num_sphere_tris,
+ &arena);
+ IMesh mesh(tris);
+ double time_create = PIL_check_seconds_timer();
+ // write_obj_mesh(mesh, "spheregrid_in");
+ IMesh out;
+ if (use_self) {
+ out = trimesh_self_intersect(mesh, &arena);
+ }
+ else {
+ int nf = num_sphere_tris;
+ out = trimesh_nary_intersect(
+ mesh, 2, [nf](int t) { return t < nf ? 0 : 1; }, false, &arena);
+ }
+ double time_intersect = PIL_check_seconds_timer();
+ std::cout << "Create time: " << time_create - time_start << "\n";
+ std::cout << "Intersect time: " << time_intersect - time_create << "\n";
+ std::cout << "Total time: " << time_intersect - time_start << "\n";
+ if (DO_OBJ) {
+ write_obj_mesh(out, "spheregrid");
+ }
+ BLI_task_scheduler_exit();
+}
+
+TEST(mesh_intersect_perf, SphereSphere)
+{
+ spheresphere_test(64, 0.5, false);
+}
+
+TEST(mesh_intersect_perf, SphereGrid)
+{
+ spheregrid_test(64, 4, 0.1, false);
+}
+
+# endif
+
+} // namespace blender::meshintersect::tests
+#endif
diff --git a/source/blender/blenlib/tests/BLI_set_test.cc b/source/blender/blenlib/tests/BLI_set_test.cc
index df3f7ab544c..3ea9a59b3db 100644
--- a/source/blender/blenlib/tests/BLI_set_test.cc
+++ b/source/blender/blenlib/tests/BLI_set_test.cc
@@ -3,6 +3,7 @@
#include <set>
#include <unordered_set>
+#include "BLI_exception_safety_test_utils.hh"
#include "BLI_ghash.h"
#include "BLI_rand.h"
#include "BLI_set.hh"
@@ -462,6 +463,55 @@ TEST(set, StringViewKeys)
EXPECT_TRUE(set.contains("hello"));
}
+TEST(set, SpanConstructorExceptions)
+{
+ std::array<ExceptionThrower, 5> array = {1, 2, 3, 4, 5};
+ array[3].throw_during_copy = true;
+ Span<ExceptionThrower> span = array;
+
+ EXPECT_ANY_THROW({ Set<ExceptionThrower> set(span); });
+}
+
+TEST(set, CopyConstructorExceptions)
+{
+ Set<ExceptionThrower> set = {1, 2, 3, 4, 5};
+ set.lookup_key(3).throw_during_copy = true;
+ EXPECT_ANY_THROW({ Set<ExceptionThrower> set_copy(set); });
+}
+
+TEST(set, MoveConstructorExceptions)
+{
+ using SetType = Set<ExceptionThrower, 4>;
+ SetType set = {1, 2, 3};
+ set.lookup_key(2).throw_during_move = true;
+ EXPECT_ANY_THROW({ SetType set_moved(std::move(set)); });
+ EXPECT_EQ(set.size(), 0);
+ set.add_multiple({3, 6, 7});
+ EXPECT_EQ(set.size(), 3);
+}
+
+TEST(set, AddNewExceptions)
+{
+ Set<ExceptionThrower> set;
+ ExceptionThrower value;
+ value.throw_during_copy = true;
+ EXPECT_ANY_THROW({ set.add_new(value); });
+ EXPECT_EQ(set.size(), 0);
+ EXPECT_ANY_THROW({ set.add_new(value); });
+ EXPECT_EQ(set.size(), 0);
+}
+
+TEST(set, AddExceptions)
+{
+ Set<ExceptionThrower> set;
+ ExceptionThrower value;
+ value.throw_during_copy = true;
+ EXPECT_ANY_THROW({ set.add(value); });
+ EXPECT_EQ(set.size(), 0);
+ EXPECT_ANY_THROW({ set.add(value); });
+ EXPECT_EQ(set.size(), 0);
+}
+
/**
* Set this to 1 to activate the benchmark. It is disabled by default, because it prints a lot.
*/
diff --git a/source/blender/blenlib/tests/BLI_stack_cxx_test.cc b/source/blender/blenlib/tests/BLI_stack_cxx_test.cc
index 3572e751b88..c03893c5596 100644
--- a/source/blender/blenlib/tests/BLI_stack_cxx_test.cc
+++ b/source/blender/blenlib/tests/BLI_stack_cxx_test.cc
@@ -1,5 +1,6 @@
/* Apache License, Version 2.0 */
+#include "BLI_exception_safety_test_utils.hh"
#include "BLI_stack.hh"
#include "BLI_strict_flags.h"
#include "BLI_vector.hh"
@@ -185,4 +186,59 @@ TEST(stack, OveralignedValues)
}
}
+TEST(stack, SpanConstructorExceptions)
+{
+ std::array<ExceptionThrower, 5> values;
+ values[3].throw_during_copy = true;
+ EXPECT_ANY_THROW({ Stack<ExceptionThrower> stack(values); });
+}
+
+TEST(stack, MoveConstructorExceptions)
+{
+ Stack<ExceptionThrower, 4> stack;
+ stack.push({});
+ stack.push({});
+ stack.peek().throw_during_move = true;
+ EXPECT_ANY_THROW({ Stack<ExceptionThrower> moved_stack{std::move(stack)}; });
+}
+
+TEST(stack, PushExceptions)
+{
+ Stack<ExceptionThrower, 2> stack;
+ stack.push({});
+ stack.push({});
+ ExceptionThrower *ptr1 = &stack.peek();
+ ExceptionThrower value;
+ value.throw_during_copy = true;
+ EXPECT_ANY_THROW({ stack.push(value); });
+ EXPECT_EQ(stack.size(), 2);
+ ExceptionThrower *ptr2 = &stack.peek();
+ EXPECT_EQ(ptr1, ptr2);
+ EXPECT_TRUE(stack.is_invariant_maintained());
+}
+
+TEST(stack, PopExceptions)
+{
+ Stack<ExceptionThrower> stack;
+ stack.push({});
+ stack.peek().throw_during_move = true;
+ stack.push({});
+ stack.pop(); /* NOLINT: bugprone-throw-keyword-missing */
+ EXPECT_ANY_THROW({ stack.pop(); }); /* NOLINT: bugprone-throw-keyword-missing */
+ EXPECT_EQ(stack.size(), 1);
+ EXPECT_TRUE(stack.is_invariant_maintained());
+}
+
+TEST(stack, PushMultipleExceptions)
+{
+ Stack<ExceptionThrower> stack;
+ stack.push({});
+ std::array<ExceptionThrower, 100> values;
+ values[6].throw_during_copy = true;
+ EXPECT_ANY_THROW({ stack.push_multiple(values); });
+ EXPECT_TRUE(stack.is_invariant_maintained());
+ EXPECT_ANY_THROW({ stack.push_multiple(values); });
+ EXPECT_TRUE(stack.is_invariant_maintained());
+}
+
} // namespace blender::tests
diff --git a/source/blender/blenlib/tests/BLI_vector_test.cc b/source/blender/blenlib/tests/BLI_vector_test.cc
index 792e120d2c0..e6b2e7c6365 100644
--- a/source/blender/blenlib/tests/BLI_vector_test.cc
+++ b/source/blender/blenlib/tests/BLI_vector_test.cc
@@ -1,5 +1,6 @@
/* Apache License, Version 2.0 */
+#include "BLI_exception_safety_test_utils.hh"
#include "BLI_strict_flags.h"
#include "BLI_vector.hh"
#include "testing/testing.h"
@@ -709,4 +710,119 @@ TEST(vector, ReverseIterator)
EXPECT_EQ_ARRAY(reversed_vec.data(), Span({7, 6, 5, 4}).data(), 4);
}
+TEST(vector, SizeValueConstructorExceptions)
+{
+ ExceptionThrower value;
+ value.throw_during_copy = true;
+ EXPECT_ANY_THROW({ Vector<ExceptionThrower> vec(5, value); });
+}
+
+TEST(vector, SpanConstructorExceptions)
+{
+ std::array<ExceptionThrower, 5> values;
+ values[3].throw_during_copy = true;
+ EXPECT_ANY_THROW({ Vector<ExceptionThrower> vec(values); });
+}
+
+TEST(vector, MoveConstructorExceptions)
+{
+ Vector<ExceptionThrower, 4> vec(3);
+ vec[2].throw_during_move = true;
+ EXPECT_ANY_THROW({ Vector<ExceptionThrower> moved_vector{std::move(vec)}; });
+}
+
+TEST(vector, AppendExceptions)
+{
+ Vector<ExceptionThrower, 4> vec(2);
+ ExceptionThrower *ptr1 = &vec.last();
+ ExceptionThrower value;
+ value.throw_during_copy = true;
+ EXPECT_ANY_THROW({ vec.append(value); });
+ EXPECT_EQ(vec.size(), 2);
+ ExceptionThrower *ptr2 = &vec.last();
+ EXPECT_EQ(ptr1, ptr2);
+}
+
+TEST(vector, ExtendExceptions)
+{
+ Vector<ExceptionThrower> vec(5);
+ std::array<ExceptionThrower, 10> values;
+ values[6].throw_during_copy = true;
+ EXPECT_ANY_THROW({ vec.extend(values); });
+ EXPECT_EQ(vec.size(), 5);
+}
+
+TEST(vector, Insert1Exceptions)
+{
+ Vector<ExceptionThrower> vec(10);
+ std::array<ExceptionThrower, 5> values;
+ values[3].throw_during_copy = true;
+ EXPECT_ANY_THROW({ vec.insert(7, values); });
+}
+
+TEST(vector, Insert2Exceptions)
+{
+ Vector<ExceptionThrower> vec(10);
+ vec.reserve(100);
+ vec[8].throw_during_move = true;
+ std::array<ExceptionThrower, 5> values;
+ EXPECT_ANY_THROW({ vec.insert(3, values); });
+}
+
+TEST(vector, PopLastExceptions)
+{
+ Vector<ExceptionThrower> vec(10);
+ vec.last().throw_during_move = true;
+ EXPECT_ANY_THROW({ vec.pop_last(); }); /* NOLINT: bugprone-throw-keyword-missing */
+ EXPECT_EQ(vec.size(), 10);
+}
+
+TEST(vector, RemoveAndReorderExceptions)
+{
+ Vector<ExceptionThrower> vec(10);
+ vec.last().throw_during_move = true;
+ EXPECT_ANY_THROW({ vec.remove_and_reorder(3); });
+ EXPECT_EQ(vec.size(), 10);
+}
+
+TEST(vector, RemoveExceptions)
+{
+ Vector<ExceptionThrower> vec(10);
+ vec[8].throw_during_move = true;
+ EXPECT_ANY_THROW({ vec.remove(2); });
+ EXPECT_EQ(vec.size(), 10);
+}
+
+TEST(vector, RemoveChunk)
+{
+ Vector<int> vec = {2, 3, 4, 5, 6, 7, 8};
+ EXPECT_EQ(vec.size(), 7);
+ vec.remove(2, 4);
+ EXPECT_EQ(vec.size(), 3);
+ EXPECT_EQ(vec[0], 2);
+ EXPECT_EQ(vec[1], 3);
+ EXPECT_EQ(vec[2], 8);
+ vec.remove(0, 1);
+ EXPECT_EQ(vec.size(), 2);
+ EXPECT_EQ(vec[0], 3);
+ EXPECT_EQ(vec[1], 8);
+ vec.remove(1, 1);
+ EXPECT_EQ(vec.size(), 1);
+ EXPECT_EQ(vec[0], 3);
+ vec.remove(0, 1);
+ EXPECT_EQ(vec.size(), 0);
+ vec.remove(0, 0);
+ EXPECT_EQ(vec.size(), 0);
+}
+
+TEST(vector, RemoveChunkExceptions)
+{
+ Vector<ExceptionThrower> vec(10);
+ vec.remove(1, 3);
+ EXPECT_EQ(vec.size(), 7);
+ vec[5].throw_during_move = true;
+ EXPECT_ANY_THROW({ vec.remove(2, 3); });
+ EXPECT_EQ(vec.size(), 7);
+}
+
} // namespace blender::tests
diff --git a/source/blender/blenloader/BLO_read_write.h b/source/blender/blenloader/BLO_read_write.h
index 024b6a6c5e4..c2f3615725e 100644
--- a/source/blender/blenloader/BLO_read_write.h
+++ b/source/blender/blenloader/BLO_read_write.h
@@ -177,9 +177,12 @@ bool BLO_write_is_undo(BlendWriter *writer);
*/
void *BLO_read_get_new_data_address(BlendDataReader *reader, const void *old_address);
+void *BLO_read_get_new_packed_address(BlendDataReader *reader, const void *old_address);
#define BLO_read_data_address(reader, ptr_p) \
*((void **)ptr_p) = BLO_read_get_new_data_address((reader), *(ptr_p))
+#define BLO_read_packed_address(reader, ptr_p) \
+ *((void **)ptr_p) = BLO_read_get_new_packed_address((reader), *(ptr_p))
typedef void (*BlendReadListFn)(BlendDataReader *reader, void *data);
void BLO_read_list_cb(BlendDataReader *reader, struct ListBase *list, BlendReadListFn callback);
@@ -195,6 +198,7 @@ void BLO_read_pointer_array(BlendDataReader *reader, void **ptr_p);
/* Misc. */
bool BLO_read_requires_endian_switch(BlendDataReader *reader);
+bool BLO_read_data_is_undo(BlendDataReader *reader);
/* Blend Read Lib API
* ===================
@@ -208,6 +212,9 @@ ID *BLO_read_get_new_id_address(BlendLibReader *reader, struct Library *lib, str
#define BLO_read_id_address(reader, lib, id_ptr_p) \
*(id_ptr_p) = (void *)BLO_read_get_new_id_address((reader), (lib), (ID *)*(id_ptr_p))
+/* Misc. */
+bool BLO_read_lib_is_undo(BlendLibReader *reader);
+
/* Blend Expand API
* ===================
*
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index 9003bc4f794..b4b25815d39 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -120,7 +120,9 @@
#include "BKE_constraint.h"
#include "BKE_curve.h"
#include "BKE_curveprofile.h"
+#include "BKE_deform.h"
#include "BKE_effect.h"
+#include "BKE_fcurve.h"
#include "BKE_fcurve_driver.h"
#include "BKE_fluid.h"
#include "BKE_global.h" // for G
@@ -140,6 +142,7 @@
#include "BKE_mesh_runtime.h"
#include "BKE_modifier.h"
#include "BKE_multires.h"
+#include "BKE_nla.h"
#include "BKE_node.h" // for tree type defines
#include "BKE_object.h"
#include "BKE_paint.h"
@@ -264,8 +267,6 @@ static BHead *find_bhead_from_idname(FileData *fd, const char *idname);
#ifdef USE_COLLECTION_COMPAT_28
static void expand_scene_collection(BlendExpander *expander, SceneCollection *sc);
#endif
-static void direct_link_animdata(BlendDataReader *reader, AnimData *adt);
-static void lib_link_animdata(BlendLibReader *reader, ID *id, AnimData *adt);
typedef struct BHeadN {
struct BHeadN *next, *prev;
@@ -1827,9 +1828,7 @@ static void change_link_placeholder_to_real_ID_pointer(ListBase *mainlist,
void *old,
void *new)
{
- Main *mainptr;
-
- for (mainptr = mainlist->first; mainptr; mainptr = mainptr->next) {
+ LISTBASE_FOREACH (Main *, mainptr, mainlist) {
FileData *fd;
if (mainptr->curlib) {
@@ -1853,9 +1852,7 @@ static void change_link_placeholder_to_real_ID_pointer(ListBase *mainlist,
*/
void blo_clear_proxy_pointers_from_lib(Main *oldmain)
{
- Object *ob = oldmain->objects.first;
-
- for (; ob; ob = ob->id.next) {
+ LISTBASE_FOREACH (Object *, ob, &oldmain->objects) {
if (ob->id.lib != NULL && ob->proxy_from != NULL && ob->proxy_from->id.lib == NULL) {
ob->proxy_from = NULL;
}
@@ -1873,47 +1870,39 @@ static void insert_packedmap(FileData *fd, PackedFile *pf)
void blo_make_packed_pointer_map(FileData *fd, Main *oldmain)
{
- Image *ima;
- VFont *vfont;
- bSound *sound;
- Volume *volume;
- Library *lib;
-
fd->packedmap = oldnewmap_new();
- for (ima = oldmain->images.first; ima; ima = ima->id.next) {
- ImagePackedFile *imapf;
-
+ LISTBASE_FOREACH (Image *, ima, &oldmain->images) {
if (ima->packedfile) {
insert_packedmap(fd, ima->packedfile);
}
- for (imapf = ima->packedfiles.first; imapf; imapf = imapf->next) {
+ LISTBASE_FOREACH (ImagePackedFile *, imapf, &ima->packedfiles) {
if (imapf->packedfile) {
insert_packedmap(fd, imapf->packedfile);
}
}
}
- for (vfont = oldmain->fonts.first; vfont; vfont = vfont->id.next) {
+ LISTBASE_FOREACH (VFont *, vfont, &oldmain->fonts) {
if (vfont->packedfile) {
insert_packedmap(fd, vfont->packedfile);
}
}
- for (sound = oldmain->sounds.first; sound; sound = sound->id.next) {
+ LISTBASE_FOREACH (bSound *, sound, &oldmain->sounds) {
if (sound->packedfile) {
insert_packedmap(fd, sound->packedfile);
}
}
- for (volume = oldmain->volumes.first; volume; volume = volume->id.next) {
+ LISTBASE_FOREACH (Volume *, volume, &oldmain->volumes) {
if (volume->packedfile) {
insert_packedmap(fd, volume->packedfile);
}
}
- for (lib = oldmain->libraries.first; lib; lib = lib->id.next) {
+ LISTBASE_FOREACH (Library *, lib, &oldmain->libraries) {
if (lib->packedfile) {
insert_packedmap(fd, lib->packedfile);
}
@@ -1924,44 +1913,36 @@ void blo_make_packed_pointer_map(FileData *fd, Main *oldmain)
/* this works because freeing old main only happens after this call */
void blo_end_packed_pointer_map(FileData *fd, Main *oldmain)
{
- Image *ima;
- VFont *vfont;
- bSound *sound;
- Volume *volume;
- Library *lib;
OldNew *entry = fd->packedmap->entries;
- int i;
/* used entries were restored, so we put them to zero */
- for (i = 0; i < fd->packedmap->nentries; i++, entry++) {
+ for (int i = 0; i < fd->packedmap->nentries; i++, entry++) {
if (entry->nr > 0) {
entry->newp = NULL;
}
}
- for (ima = oldmain->images.first; ima; ima = ima->id.next) {
- ImagePackedFile *imapf;
-
+ LISTBASE_FOREACH (Image *, ima, &oldmain->images) {
ima->packedfile = newpackedadr(fd, ima->packedfile);
- for (imapf = ima->packedfiles.first; imapf; imapf = imapf->next) {
+ LISTBASE_FOREACH (ImagePackedFile *, imapf, &ima->packedfiles) {
imapf->packedfile = newpackedadr(fd, imapf->packedfile);
}
}
- for (vfont = oldmain->fonts.first; vfont; vfont = vfont->id.next) {
+ LISTBASE_FOREACH (VFont *, vfont, &oldmain->fonts) {
vfont->packedfile = newpackedadr(fd, vfont->packedfile);
}
- for (sound = oldmain->sounds.first; sound; sound = sound->id.next) {
+ LISTBASE_FOREACH (bSound *, sound, &oldmain->sounds) {
sound->packedfile = newpackedadr(fd, sound->packedfile);
}
- for (lib = oldmain->libraries.first; lib; lib = lib->id.next) {
+ LISTBASE_FOREACH (Library *, lib, &oldmain->libraries) {
lib->packedfile = newpackedadr(fd, lib->packedfile);
}
- for (volume = oldmain->volumes.first; volume; volume = volume->id.next) {
+ LISTBASE_FOREACH (Volume *, volume, &oldmain->volumes) {
volume->packedfile = newpackedadr(fd, volume->packedfile);
}
}
@@ -1969,14 +1950,12 @@ void blo_end_packed_pointer_map(FileData *fd, Main *oldmain)
/* undo file support: add all library pointers in lookup */
void blo_add_library_pointer_map(ListBase *old_mainlist, FileData *fd)
{
- Main *ptr = old_mainlist->first;
ListBase *lbarray[MAX_LIBARRAY];
- for (ptr = ptr->next; ptr; ptr = ptr->next) {
+ LISTBASE_FOREACH (Main *, ptr, old_mainlist) {
int i = set_listbasepointers(ptr, lbarray);
while (i--) {
- ID *id;
- for (id = lbarray[i]->first; id; id = id->next) {
+ LISTBASE_FOREACH (ID *, id, lbarray[i]) {
oldnewmap_insert(fd->libmap, id, id, GS(id->name));
}
}
@@ -2004,7 +1983,7 @@ typedef struct BLOCacheStorage {
static void blo_cache_storage_entry_register(ID *id,
const IDCacheKey *key,
void **UNUSED(cache_p),
- eIDTypeInfoCacheCallbackFlags UNUSED(flags),
+ uint UNUSED(flags),
void *cache_storage_v)
{
BLI_assert(key->id_session_uuid == id->session_uuid);
@@ -2019,11 +1998,8 @@ static void blo_cache_storage_entry_register(ID *id,
}
/** 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,
- eIDTypeInfoCacheCallbackFlags flags,
- void *cache_storage_v)
+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;
@@ -2050,7 +2026,7 @@ static void blo_cache_storage_entry_restore_in_new(ID *UNUSED(id),
static void blo_cache_storage_entry_clear_in_old(ID *UNUSED(id),
const IDCacheKey *key,
void **cache_p,
- eIDTypeInfoCacheCallbackFlags UNUSED(flags),
+ uint UNUSED(flags),
void *cache_storage_v)
{
BLOCacheStorage *cache_storage = cache_storage_v;
@@ -2270,185 +2246,6 @@ static void link_glob_list(FileData *fd, ListBase *lb) /* for glob data */
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Read ID Properties
- * \{ */
-
-static void IDP_DirectLinkProperty(IDProperty *prop, BlendDataReader *reader);
-static void IDP_LibLinkProperty(IDProperty *prop, BlendLibReader *reader);
-
-static void IDP_DirectLinkIDPArray(IDProperty *prop, BlendDataReader *reader)
-{
- IDProperty *array;
- int i;
-
- /* since we didn't save the extra buffer, set totallen to len */
- prop->totallen = prop->len;
- BLO_read_data_address(reader, &prop->data.pointer);
-
- array = (IDProperty *)prop->data.pointer;
-
- /* note!, idp-arrays didn't exist in 2.4x, so the pointer will be cleared
- * there's not really anything we can do to correct this, at least don't crash */
- if (array == NULL) {
- prop->len = 0;
- prop->totallen = 0;
- }
-
- for (i = 0; i < prop->len; i++) {
- IDP_DirectLinkProperty(&array[i], reader);
- }
-}
-
-static void IDP_DirectLinkArray(IDProperty *prop, BlendDataReader *reader)
-{
- IDProperty **array;
- int i;
-
- /* since we didn't save the extra buffer, set totallen to len */
- prop->totallen = prop->len;
-
- if (prop->subtype == IDP_GROUP) {
- BLO_read_pointer_array(reader, &prop->data.pointer);
- array = prop->data.pointer;
-
- for (i = 0; i < prop->len; i++) {
- IDP_DirectLinkProperty(array[i], reader);
- }
- }
- else if (prop->subtype == IDP_DOUBLE) {
- BLO_read_double_array(reader, prop->len, (double **)&prop->data.pointer);
- }
- else {
- /* also used for floats */
- BLO_read_int32_array(reader, prop->len, (int **)&prop->data.pointer);
- }
-}
-
-static void IDP_DirectLinkString(IDProperty *prop, BlendDataReader *reader)
-{
- /*since we didn't save the extra string buffer, set totallen to len.*/
- prop->totallen = prop->len;
- BLO_read_data_address(reader, &prop->data.pointer);
-}
-
-static void IDP_DirectLinkGroup(IDProperty *prop, BlendDataReader *reader)
-{
- ListBase *lb = &prop->data.group;
- IDProperty *loop;
-
- BLO_read_list(reader, lb);
-
- /*Link child id properties now*/
- for (loop = prop->data.group.first; loop; loop = loop->next) {
- IDP_DirectLinkProperty(loop, reader);
- }
-}
-
-static void IDP_DirectLinkProperty(IDProperty *prop, BlendDataReader *reader)
-{
- switch (prop->type) {
- case IDP_GROUP:
- IDP_DirectLinkGroup(prop, reader);
- break;
- case IDP_STRING:
- IDP_DirectLinkString(prop, reader);
- break;
- case IDP_ARRAY:
- IDP_DirectLinkArray(prop, reader);
- break;
- case IDP_IDPARRAY:
- IDP_DirectLinkIDPArray(prop, reader);
- break;
- case IDP_DOUBLE:
- /* Workaround for doubles.
- * They are stored in the same field as `int val, val2` in the IDPropertyData struct,
- * they have to deal with endianness specifically.
- *
- * In theory, val and val2 would've already been swapped
- * if switch_endian is true, so we have to first unswap
- * them then re-swap them as a single 64-bit entity. */
- if (BLO_read_requires_endian_switch(reader)) {
- BLI_endian_switch_int32(&prop->data.val);
- BLI_endian_switch_int32(&prop->data.val2);
- BLI_endian_switch_int64((int64_t *)&prop->data.val);
- }
- break;
- case IDP_INT:
- case IDP_FLOAT:
- case IDP_ID:
- break; /* Nothing special to do here. */
- default:
- /* Unknown IDP type, nuke it (we cannot handle unknown types everywhere in code,
- * IDP are way too polymorphic to do it safely. */
- printf(
- "%s: found unknown IDProperty type %d, reset to Integer one !\n", __func__, prop->type);
- /* Note: we do not attempt to free unknown prop, we have no way to know how to do that! */
- prop->type = IDP_INT;
- prop->subtype = 0;
- IDP_Int(prop) = 0;
- }
-}
-
-#define IDP_DirectLinkGroup_OrFree(prop, reader) \
- _IDP_DirectLinkGroup_OrFree(prop, reader, __func__)
-
-static void _IDP_DirectLinkGroup_OrFree(IDProperty **prop,
- BlendDataReader *reader,
- const char *caller_func_id)
-{
- if (*prop) {
- if ((*prop)->type == IDP_GROUP) {
- IDP_DirectLinkGroup(*prop, reader);
- }
- else {
- /* corrupt file! */
- printf("%s: found non group data, freeing type %d!\n", caller_func_id, (*prop)->type);
- /* don't risk id, data's likely corrupt. */
- // IDP_FreePropertyContent(*prop);
- *prop = NULL;
- }
- }
-}
-
-static void IDP_LibLinkProperty(IDProperty *prop, BlendLibReader *reader)
-{
- if (!prop) {
- return;
- }
-
- switch (prop->type) {
- case IDP_ID: /* PointerProperty */
- {
- void *newaddr = BLO_read_get_new_id_address(reader, NULL, IDP_Id(prop));
- if (IDP_Id(prop) && !newaddr && G.debug) {
- printf("Error while loading \"%s\". Data not found in file!\n", prop->name);
- }
- prop->data.pointer = newaddr;
- break;
- }
- case IDP_IDPARRAY: /* CollectionProperty */
- {
- IDProperty *idp_array = IDP_IDPArray(prop);
- for (int i = 0; i < prop->len; i++) {
- IDP_LibLinkProperty(&(idp_array[i]), reader);
- }
- break;
- }
- case IDP_GROUP: /* PointerProperty */
- {
- LISTBASE_FOREACH (IDProperty *, loop, &prop->data.group) {
- IDP_LibLinkProperty(loop, reader);
- }
- break;
- }
- default:
- break; /* Nothing to do for other IDProps. */
- }
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
/** \name Read Image Preview
* \{ */
@@ -2457,8 +2254,7 @@ static PreviewImage *direct_link_preview_image(BlendDataReader *reader, PreviewI
PreviewImage *prv = BLO_read_get_new_data_address(reader, old_prv);
if (prv) {
- int i;
- for (i = 0; i < NUM_ICON_SIZES; i++) {
+ for (int i = 0; i < NUM_ICON_SIZES; i++) {
if (prv->rect[i]) {
BLO_read_data_address(reader, &prv->rect[i]);
}
@@ -2504,11 +2300,11 @@ 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_LibLinkProperty(id->properties, reader);
+ IDP_BlendReadLib(reader, id->properties);
AnimData *adt = BKE_animdata_from_id(id);
if (adt != NULL) {
- lib_link_animdata(reader, id, adt);
+ BKE_animdata_blend_read_lib(reader, id, adt);
}
if (id->override_library) {
@@ -2635,7 +2431,7 @@ static int direct_link_id_restore_recalc(const FileData *fd,
static void direct_link_id_common(
BlendDataReader *reader, Library *current_library, ID *id, ID *id_old, const int tag)
{
- if (reader->fd->memfile == NULL) {
+ 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;
@@ -2663,7 +2459,7 @@ static void direct_link_id_common(
if (id->properties) {
BLO_read_data_address(reader, &id->properties);
/* this case means the data was written incorrectly, it should not happen */
- IDP_DirectLinkGroup_OrFree(&id->properties, reader);
+ IDP_BlendDataRead(reader, &id->properties);
}
id->flag &= ~LIB_INDIRECT_WEAK_LINK;
@@ -2676,7 +2472,7 @@ static void direct_link_id_common(
*
* 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 (reader->fd->memfile == NULL) {
+ if (!BLO_read_data_is_undo(reader)) {
id->recalc = 0;
id->recalc_after_undo_push = 0;
}
@@ -2836,12 +2632,12 @@ static void direct_link_paint_curve(BlendDataReader *reader, PaintCurve *pc)
/** \name Read PackedFile
* \{ */
-static PackedFile *direct_link_packedfile(BlendDataReader *reader, PackedFile *oldpf)
+static PackedFile *direct_link_packedfile(BlendDataReader *reader, PackedFile *pf)
{
- PackedFile *pf = newpackedadr(reader->fd, oldpf);
+ BLO_read_packed_address(reader, &pf);
if (pf) {
- pf->data = newpackedadr(reader->fd, pf->data);
+ BLO_read_packed_address(reader, &pf->data);
if (pf->data == NULL) {
/* We cannot allow a PackedFile with a NULL data field,
* the whole code assumes this is not possible. See T70315. */
@@ -2872,11 +2668,9 @@ static void lib_link_ipo(BlendLibReader *reader, Ipo *ipo)
// XXX deprecated - old animation system
static void direct_link_ipo(BlendDataReader *reader, Ipo *ipo)
{
- IpoCurve *icu;
-
BLO_read_list(reader, &(ipo->curve));
- for (icu = ipo->curve.first; icu; icu = icu->next) {
+ LISTBASE_FOREACH (IpoCurve *, icu, &ipo->curve) {
BLO_read_data_address(reader, &icu->bezt);
BLO_read_data_address(reader, &icu->bp);
BLO_read_data_address(reader, &icu->driver);
@@ -2886,14 +2680,11 @@ static void direct_link_ipo(BlendDataReader *reader, Ipo *ipo)
// XXX deprecated - old animation system
static void lib_link_nlastrips(BlendLibReader *reader, ID *id, ListBase *striplist)
{
- bActionStrip *strip;
- bActionModifier *amod;
-
- for (strip = striplist->first; strip; strip = strip->next) {
+ LISTBASE_FOREACH (bActionStrip *, strip, striplist) {
BLO_read_id_address(reader, id->lib, &strip->object);
BLO_read_id_address(reader, id->lib, &strip->act);
BLO_read_id_address(reader, id->lib, &strip->ipo);
- for (amod = strip->modifiers.first; amod; amod = amod->next) {
+ LISTBASE_FOREACH (bActionModifier *, amod, &strip->modifiers) {
BLO_read_id_address(reader, id->lib, &amod->ob);
}
}
@@ -2902,11 +2693,9 @@ static void lib_link_nlastrips(BlendLibReader *reader, ID *id, ListBase *stripli
// XXX deprecated - old animation system
static void direct_link_nlastrips(BlendDataReader *reader, ListBase *strips)
{
- bActionStrip *strip;
-
BLO_read_list(reader, strips);
- for (strip = strips->first; strip; strip = strip->next) {
+ LISTBASE_FOREACH (bActionStrip *, strip, strips) {
BLO_read_list(reader, &strip->modifiers);
}
}
@@ -2914,9 +2703,7 @@ static void direct_link_nlastrips(BlendDataReader *reader, ListBase *strips)
// XXX deprecated - old animation system
static void lib_link_constraint_channels(BlendLibReader *reader, ID *id, ListBase *chanbase)
{
- bConstraintChannel *chan;
-
- for (chan = chanbase->first; chan; chan = chan->next) {
+ LISTBASE_FOREACH (bConstraintChannel *, chan, chanbase) {
BLO_read_id_address(reader, id->lib, &chan->ipo);
}
}
@@ -2927,153 +2714,6 @@ static void lib_link_constraint_channels(BlendLibReader *reader, ID *id, ListBas
/** \name Read ID: Action
* \{ */
-static void lib_link_fmodifiers(BlendLibReader *reader, ID *id, ListBase *list)
-{
- FModifier *fcm;
-
- for (fcm = list->first; fcm; fcm = fcm->next) {
- /* data for specific modifiers */
- switch (fcm->type) {
- case FMODIFIER_TYPE_PYTHON: {
- FMod_Python *data = (FMod_Python *)fcm->data;
- BLO_read_id_address(reader, id->lib, &data->script);
-
- break;
- }
- }
- }
-}
-
-static void lib_link_fcurves(BlendLibReader *reader, ID *id, ListBase *list)
-{
- FCurve *fcu;
-
- if (list == NULL) {
- return;
- }
-
- /* relink ID-block references... */
- for (fcu = list->first; fcu; fcu = fcu->next) {
- /* driver data */
- if (fcu->driver) {
- ChannelDriver *driver = fcu->driver;
- DriverVar *dvar;
-
- for (dvar = driver->variables.first; dvar; dvar = dvar->next) {
- DRIVER_TARGETS_LOOPER_BEGIN (dvar) {
- /* only relink if still used */
- if (tarIndex < dvar->num_targets) {
- BLO_read_id_address(reader, id->lib, &dtar->id);
- }
- else {
- dtar->id = NULL;
- }
- }
- DRIVER_TARGETS_LOOPER_END;
- }
- }
-
- /* modifiers */
- lib_link_fmodifiers(reader, id, &fcu->modifiers);
- }
-}
-
-/* NOTE: this assumes that link_list has already been called on the list */
-static void direct_link_fmodifiers(BlendDataReader *reader, ListBase *list, FCurve *curve)
-{
- FModifier *fcm;
-
- for (fcm = list->first; fcm; fcm = fcm->next) {
- /* relink general data */
- BLO_read_data_address(reader, &fcm->data);
- fcm->curve = curve;
-
- /* do relinking of data for specific types */
- switch (fcm->type) {
- case FMODIFIER_TYPE_GENERATOR: {
- FMod_Generator *data = (FMod_Generator *)fcm->data;
- BLO_read_float_array(reader, data->arraysize, &data->coefficients);
- break;
- }
- case FMODIFIER_TYPE_ENVELOPE: {
- FMod_Envelope *data = (FMod_Envelope *)fcm->data;
-
- BLO_read_data_address(reader, &data->data);
-
- break;
- }
- case FMODIFIER_TYPE_PYTHON: {
- FMod_Python *data = (FMod_Python *)fcm->data;
-
- BLO_read_data_address(reader, &data->prop);
- IDP_DirectLinkGroup_OrFree(&data->prop, reader);
-
- break;
- }
- }
- }
-}
-
-/* NOTE: this assumes that link_list has already been called on the list */
-static void direct_link_fcurves(BlendDataReader *reader, ListBase *list)
-{
- FCurve *fcu;
-
- /* link F-Curve data to F-Curve again (non ID-libs) */
- for (fcu = list->first; fcu; fcu = fcu->next) {
- /* curve data */
- BLO_read_data_address(reader, &fcu->bezt);
- BLO_read_data_address(reader, &fcu->fpt);
-
- /* rna path */
- BLO_read_data_address(reader, &fcu->rna_path);
-
- /* group */
- BLO_read_data_address(reader, &fcu->grp);
-
- /* clear disabled flag - allows disabled drivers to be tried again ([#32155]),
- * but also means that another method for "reviving disabled F-Curves" exists
- */
- fcu->flag &= ~FCURVE_DISABLED;
-
- /* driver */
- BLO_read_data_address(reader, &fcu->driver);
- if (fcu->driver) {
- ChannelDriver *driver = fcu->driver;
- DriverVar *dvar;
-
- /* Compiled expression data will need to be regenerated
- * (old pointer may still be set here). */
- driver->expr_comp = NULL;
- driver->expr_simple = NULL;
-
- /* give the driver a fresh chance - the operating environment may be different now
- * (addons, etc. may be different) so the driver namespace may be sane now [#32155]
- */
- driver->flag &= ~DRIVER_FLAG_INVALID;
-
- /* relink variables, targets and their paths */
- BLO_read_list(reader, &driver->variables);
- for (dvar = driver->variables.first; dvar; dvar = dvar->next) {
- DRIVER_TARGETS_LOOPER_BEGIN (dvar) {
- /* only relink the targets being used */
- if (tarIndex < dvar->num_targets) {
- BLO_read_data_address(reader, &dtar->rna_path);
- }
- else {
- dtar->rna_path = NULL;
- }
- }
- DRIVER_TARGETS_LOOPER_END;
- }
- }
-
- /* modifiers */
- BLO_read_list(reader, &fcu->modifiers);
- direct_link_fmodifiers(reader, &fcu->modifiers, fcu);
- }
-}
-
static void lib_link_action(BlendLibReader *reader, bAction *act)
{
// XXX deprecated - old animation system <<<
@@ -3083,7 +2723,7 @@ static void lib_link_action(BlendLibReader *reader, bAction *act)
}
// >>> XXX deprecated - old animation system
- lib_link_fcurves(reader, &act->id, &act->curves);
+ BKE_fcurve_blend_read_lib(reader, &act->id, &act->curves);
LISTBASE_FOREACH (TimeMarker *, marker, &act->markers) {
if (marker->camera) {
@@ -3094,102 +2734,34 @@ static void lib_link_action(BlendLibReader *reader, bAction *act)
static void direct_link_action(BlendDataReader *reader, bAction *act)
{
- bActionChannel *achan; // XXX deprecated - old animation system
- bActionGroup *agrp;
-
BLO_read_list(reader, &act->curves);
BLO_read_list(reader, &act->chanbase); // XXX deprecated - old animation system
BLO_read_list(reader, &act->groups);
BLO_read_list(reader, &act->markers);
// XXX deprecated - old animation system <<<
- for (achan = act->chanbase.first; achan; achan = achan->next) {
+ LISTBASE_FOREACH (bActionChannel *, achan, &act->chanbase) {
BLO_read_data_address(reader, &achan->grp);
BLO_read_list(reader, &achan->constraintChannels);
}
// >>> XXX deprecated - old animation system
- direct_link_fcurves(reader, &act->curves);
+ BKE_fcurve_blend_read_data(reader, &act->curves);
- for (agrp = act->groups.first; agrp; agrp = agrp->next) {
+ LISTBASE_FOREACH (bActionGroup *, agrp, &act->groups) {
BLO_read_data_address(reader, &agrp->channels.first);
BLO_read_data_address(reader, &agrp->channels.last);
}
}
-static void lib_link_nladata_strips(BlendLibReader *reader, ID *id, ListBase *list)
-{
- NlaStrip *strip;
-
- for (strip = list->first; strip; strip = strip->next) {
- /* check strip's children */
- lib_link_nladata_strips(reader, id, &strip->strips);
-
- /* check strip's F-Curves */
- lib_link_fcurves(reader, id, &strip->fcurves);
-
- /* reassign the counted-reference to action */
- BLO_read_id_address(reader, id->lib, &strip->act);
- }
-}
-
-static void lib_link_nladata(BlendLibReader *reader, ID *id, ListBase *list)
-{
- NlaTrack *nlt;
-
- /* we only care about the NLA strips inside the tracks */
- for (nlt = list->first; nlt; nlt = nlt->next) {
- lib_link_nladata_strips(reader, id, &nlt->strips);
- }
-}
-
-/* This handles Animato NLA-Strips linking
- * NOTE: this assumes that link_list has already been called on the list
- */
-static void direct_link_nladata_strips(BlendDataReader *reader, ListBase *list)
-{
- NlaStrip *strip;
-
- for (strip = list->first; strip; strip = strip->next) {
- /* strip's child strips */
- BLO_read_list(reader, &strip->strips);
- direct_link_nladata_strips(reader, &strip->strips);
-
- /* strip's F-Curves */
- BLO_read_list(reader, &strip->fcurves);
- direct_link_fcurves(reader, &strip->fcurves);
-
- /* strip's F-Modifiers */
- BLO_read_list(reader, &strip->modifiers);
- direct_link_fmodifiers(reader, &strip->modifiers, NULL);
- }
-}
-
-/* NOTE: this assumes that BLO_read_list has already been called on the list */
-static void direct_link_nladata(BlendDataReader *reader, ListBase *list)
-{
- NlaTrack *nlt;
-
- for (nlt = list->first; nlt; nlt = nlt->next) {
- /* relink list of strips */
- BLO_read_list(reader, &nlt->strips);
-
- /* relink strip data */
- direct_link_nladata_strips(reader, &nlt->strips);
- }
-}
-
/* ------- */
static void lib_link_keyingsets(BlendLibReader *reader, ID *id, ListBase *list)
{
- KeyingSet *ks;
- KS_Path *ksp;
-
/* here, we're only interested in the ID pointer stored in some of the paths */
- for (ks = list->first; ks; ks = ks->next) {
- for (ksp = ks->paths.first; ksp; ksp = ksp->next) {
+ LISTBASE_FOREACH (KeyingSet *, ks, list) {
+ LISTBASE_FOREACH (KS_Path *, ksp, &ks->paths) {
BLO_read_id_address(reader, id->lib, &ksp->id);
}
}
@@ -3198,71 +2770,18 @@ static void lib_link_keyingsets(BlendLibReader *reader, ID *id, ListBase *list)
/* NOTE: this assumes that BLO_read_list has already been called on the list */
static void direct_link_keyingsets(BlendDataReader *reader, ListBase *list)
{
- KeyingSet *ks;
- KS_Path *ksp;
-
/* link KeyingSet data to KeyingSet again (non ID-libs) */
- for (ks = list->first; ks; ks = ks->next) {
+ LISTBASE_FOREACH (KeyingSet *, ks, list) {
/* paths */
BLO_read_list(reader, &ks->paths);
- for (ksp = ks->paths.first; ksp; ksp = ksp->next) {
+ LISTBASE_FOREACH (KS_Path *, ksp, &ks->paths) {
/* rna path */
BLO_read_data_address(reader, &ksp->rna_path);
}
}
}
-/* ------- */
-
-static void lib_link_animdata(BlendLibReader *reader, ID *id, AnimData *adt)
-{
- if (adt == NULL) {
- return;
- }
-
- /* link action data */
- BLO_read_id_address(reader, id->lib, &adt->action);
- BLO_read_id_address(reader, id->lib, &adt->tmpact);
-
- /* link drivers */
- lib_link_fcurves(reader, id, &adt->drivers);
-
- /* overrides don't have lib-link for now, so no need to do anything */
-
- /* link NLA-data */
- lib_link_nladata(reader, id, &adt->nla_tracks);
-}
-
-static void direct_link_animdata(BlendDataReader *reader, AnimData *adt)
-{
- /* NOTE: must have called newdataadr already before doing this... */
- if (adt == NULL) {
- return;
- }
-
- /* link drivers */
- BLO_read_list(reader, &adt->drivers);
- direct_link_fcurves(reader, &adt->drivers);
- adt->driver_array = NULL;
-
- /* link overrides */
- // TODO...
-
- /* link NLA-data */
- BLO_read_list(reader, &adt->nla_tracks);
- direct_link_nladata(reader, &adt->nla_tracks);
-
- /* relink active track/strip - even though strictly speaking this should only be used
- * if we're in 'tweaking mode', we need to be able to have this loaded back for
- * undo, but also since users may not exit tweakmode before saving (#24535)
- */
- // TODO: it's not really nice that anyone should be able to save the file in this
- // state, but it's going to be too hard to enforce this single case...
- BLO_read_data_address(reader, &adt->act_track);
- BLO_read_data_address(reader, &adt->actstrip);
-}
-
/** \} */
/* -------------------------------------------------------------------- */
@@ -3282,7 +2801,7 @@ static void direct_link_cachefile(BlendDataReader *reader, CacheFile *cache_file
/* relink animdata */
BLO_read_data_address(reader, &cache_file->adt);
- direct_link_animdata(reader, cache_file->adt);
+ BKE_animdata_blend_read_data(reader, cache_file->adt);
}
/** \} */
@@ -3330,7 +2849,7 @@ static void direct_link_workspace(BlendDataReader *reader, WorkSpace *workspace,
/* Same issue/fix as in direct_link_workspace_link_scene_data: Can't read workspace data
* when reading windows, so have to update windows after/when reading workspaces. */
- for (wmWindowManager *wm = main->wm.first; wm; wm = wm->id.next) {
+ LISTBASE_FOREACH (wmWindowManager *, wm, &main->wm) {
LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
BLO_read_data_address(reader, &win->workspace_hook->act_layout);
}
@@ -3339,7 +2858,7 @@ static void direct_link_workspace(BlendDataReader *reader, WorkSpace *workspace,
LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) {
tref->runtime = NULL;
BLO_read_data_address(reader, &tref->properties);
- IDP_DirectLinkGroup_OrFree(&tref->properties, reader);
+ IDP_BlendDataRead(reader, &tref->properties);
}
workspace->status_text = NULL;
@@ -3364,7 +2883,7 @@ static void lib_link_workspace_instance_hook(BlendLibReader *reader,
static void lib_link_node_socket(BlendLibReader *reader, Library *lib, bNodeSocket *sock)
{
- IDP_LibLinkProperty(sock->prop, reader);
+ IDP_BlendReadLib(reader, sock->prop);
switch ((eNodeSocketDatatype)sock->type) {
case SOCK_OBJECT: {
@@ -3411,7 +2930,7 @@ static void lib_link_ntree(BlendLibReader *reader, Library *lib, bNodeTree *ntre
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
/* Link ID Properties -- and copy this comment EXACTLY for easy finding
* of library blocks that implement this.*/
- IDP_LibLinkProperty(node->prop, reader);
+ IDP_BlendReadLib(reader, node->prop);
BLO_read_id_address(reader, lib, &node->id);
@@ -3430,7 +2949,7 @@ static void lib_link_ntree(BlendLibReader *reader, Library *lib, bNodeTree *ntre
/* For nodes with static socket layout, add/remove sockets as needed
* to match the static layout. */
- if (reader->fd->memfile == NULL) {
+ if (!BLO_read_lib_is_undo(reader)) {
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
node_verify_socket_templates(ntree, node);
}
@@ -3446,7 +2965,7 @@ static void lib_link_nodetree(BlendLibReader *reader, bNodeTree *ntree)
static void direct_link_node_socket(BlendDataReader *reader, bNodeSocket *sock)
{
BLO_read_data_address(reader, &sock->prop);
- IDP_DirectLinkGroup_OrFree(&sock->prop, reader);
+ IDP_BlendDataRead(reader, &sock->prop);
BLO_read_data_address(reader, &sock->link);
sock->typeinfo = NULL;
@@ -3459,9 +2978,6 @@ static void direct_link_node_socket(BlendDataReader *reader, bNodeSocket *sock)
static void direct_link_nodetree(BlendDataReader *reader, bNodeTree *ntree)
{
/* note: writing and reading goes in sync, for speed */
- bNode *node;
- bNodeSocket *sock;
- bNodeLink *link;
ntree->init = 0; /* to set callbacks and force setting types */
ntree->is_updating = false;
@@ -3472,20 +2988,20 @@ static void direct_link_nodetree(BlendDataReader *reader, bNodeTree *ntree)
ntree->execdata = NULL;
BLO_read_data_address(reader, &ntree->adt);
- direct_link_animdata(reader, ntree->adt);
+ BKE_animdata_blend_read_data(reader, ntree->adt);
BLO_read_list(reader, &ntree->nodes);
- for (node = ntree->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
node->typeinfo = NULL;
BLO_read_list(reader, &node->inputs);
BLO_read_list(reader, &node->outputs);
BLO_read_data_address(reader, &node->prop);
- IDP_DirectLinkGroup_OrFree(&node->prop, reader);
+ IDP_BlendDataRead(reader, &node->prop);
BLO_read_list(reader, &node->internal_links);
- for (link = node->internal_links.first; link; link = link->next) {
+ LISTBASE_FOREACH (bNodeLink *, link, &node->internal_links) {
BLO_read_data_address(reader, &link->fromnode);
BLO_read_data_address(reader, &link->fromsock);
BLO_read_data_address(reader, &link->tonode);
@@ -3564,14 +3080,14 @@ static void direct_link_nodetree(BlendDataReader *reader, bNodeTree *ntree)
BLO_read_list(reader, &ntree->links);
/* and we connect the rest */
- for (node = ntree->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
BLO_read_data_address(reader, &node->parent);
node->lasty = 0;
- for (sock = node->inputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
direct_link_node_socket(reader, sock);
}
- for (sock = node->outputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
direct_link_node_socket(reader, sock);
}
}
@@ -3579,14 +3095,14 @@ static void direct_link_nodetree(BlendDataReader *reader, bNodeTree *ntree)
/* interface socket lists */
BLO_read_list(reader, &ntree->inputs);
BLO_read_list(reader, &ntree->outputs);
- for (sock = ntree->inputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) {
direct_link_node_socket(reader, sock);
}
- for (sock = ntree->outputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) {
direct_link_node_socket(reader, sock);
}
- for (link = ntree->links.first; link; link = link->next) {
+ LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
BLO_read_data_address(reader, &link->fromnode);
BLO_read_data_address(reader, &link->tonode);
BLO_read_data_address(reader, &link->fromsock);
@@ -3623,10 +3139,9 @@ static void lib_link_constraint_cb(bConstraint *UNUSED(con),
static void lib_link_constraints(BlendLibReader *reader, ID *id, ListBase *conlist)
{
tConstraintLinkData cld;
- bConstraint *con;
/* legacy fixes */
- for (con = conlist->first; con; con = con->next) {
+ LISTBASE_FOREACH (bConstraint *, con, conlist) {
/* patch for error introduced by changing constraints (dunno how) */
/* if con->data type changes, dna cannot resolve the pointer! (ton) */
if (con->data == NULL) {
@@ -3650,10 +3165,8 @@ static void lib_link_constraints(BlendLibReader *reader, ID *id, ListBase *conli
static void direct_link_constraints(BlendDataReader *reader, ListBase *lb)
{
- bConstraint *con;
-
BLO_read_list(reader, lb);
- for (con = lb->first; con; con = con->next) {
+ LISTBASE_FOREACH (bConstraint *, con, lb) {
BLO_read_data_address(reader, &con->data);
switch (con->type) {
@@ -3663,7 +3176,7 @@ static void direct_link_constraints(BlendDataReader *reader, ListBase *lb)
BLO_read_list(reader, &data->targets);
BLO_read_data_address(reader, &data->prop);
- IDP_DirectLinkGroup_OrFree(&data->prop, reader);
+ IDP_BlendDataRead(reader, &data->prop);
break;
}
case CONSTRAINT_TYPE_ARMATURE: {
@@ -3716,7 +3229,7 @@ static void lib_link_pose(BlendLibReader *reader, Object *ob, bPose *pose)
/* always rebuild to match proxy or lib changes, but on Undo */
bool rebuild = false;
- if (reader->fd->memfile == NULL) {
+ if (!BLO_read_lib_is_undo(reader)) {
if (ob->proxy || ob->id.lib != arm->id.lib) {
rebuild = true;
}
@@ -3742,7 +3255,7 @@ static void lib_link_pose(BlendLibReader *reader, Object *ob, bPose *pose)
pchan->bone = BKE_armature_find_bone_name(arm, pchan->name);
- IDP_LibLinkProperty(pchan->prop, reader);
+ IDP_BlendReadLib(reader, pchan->prop);
BLO_read_id_address(reader, arm->id.lib, &pchan->custom);
if (UNLIKELY(pchan->bone == NULL)) {
@@ -3764,7 +3277,7 @@ static void lib_link_pose(BlendLibReader *reader, Object *ob, bPose *pose)
static void lib_link_bones(BlendLibReader *reader, Bone *bone)
{
- IDP_LibLinkProperty(bone->prop, reader);
+ IDP_BlendReadLib(reader, bone->prop);
LISTBASE_FOREACH (Bone *, curbone, &bone->childbase) {
lib_link_bones(reader, curbone);
@@ -3780,11 +3293,9 @@ static void lib_link_armature(BlendLibReader *reader, bArmature *arm)
static void direct_link_bones(BlendDataReader *reader, Bone *bone)
{
- Bone *child;
-
BLO_read_data_address(reader, &bone->parent);
BLO_read_data_address(reader, &bone->prop);
- IDP_DirectLinkGroup_OrFree(&bone->prop, reader);
+ IDP_BlendDataRead(reader, &bone->prop);
BLO_read_data_address(reader, &bone->bbone_next);
BLO_read_data_address(reader, &bone->bbone_prev);
@@ -3793,15 +3304,13 @@ static void direct_link_bones(BlendDataReader *reader, Bone *bone)
BLO_read_list(reader, &bone->childbase);
- for (child = bone->childbase.first; child; child = child->next) {
+ LISTBASE_FOREACH (Bone *, child, &bone->childbase) {
direct_link_bones(reader, child);
}
}
static void direct_link_armature(BlendDataReader *reader, bArmature *arm)
{
- Bone *bone;
-
BLO_read_list(reader, &arm->bonebase);
arm->bonehash = NULL;
arm->edbo = NULL;
@@ -3809,9 +3318,9 @@ static void direct_link_armature(BlendDataReader *reader, bArmature *arm)
arm->needs_flush_to_id = 0;
BLO_read_data_address(reader, &arm->adt);
- direct_link_animdata(reader, arm->adt);
+ BKE_animdata_blend_read_data(reader, arm->adt);
- for (bone = arm->bonebase.first; bone; bone = bone->next) {
+ LISTBASE_FOREACH (Bone *, bone, &arm->bonebase) {
direct_link_bones(reader, bone);
}
@@ -3843,7 +3352,7 @@ static void lib_link_camera(BlendLibReader *reader, Camera *ca)
static void direct_link_camera(BlendDataReader *reader, Camera *ca)
{
BLO_read_data_address(reader, &ca->adt);
- direct_link_animdata(reader, ca->adt);
+ BKE_animdata_blend_read_data(reader, ca->adt);
BLO_read_list(reader, &ca->bg_images);
@@ -3867,7 +3376,7 @@ static void lib_link_light(BlendLibReader *reader, Light *la)
static void direct_link_light(BlendDataReader *reader, Light *la)
{
BLO_read_data_address(reader, &la->adt);
- direct_link_animdata(reader, la->adt);
+ BKE_animdata_blend_read_data(reader, la->adt);
BLO_read_data_address(reader, &la->curfalloff);
if (la->curfalloff) {
@@ -3885,10 +3394,8 @@ static void direct_link_light(BlendDataReader *reader, Light *la)
void blo_do_versions_key_uidgen(Key *key)
{
- KeyBlock *block;
-
key->uidgen = 1;
- for (block = key->block.first; block; block = block->next) {
+ LISTBASE_FOREACH (KeyBlock *, block, &key->block) {
block->uid = key->uidgen++;
}
}
@@ -3903,13 +3410,10 @@ static void lib_link_key(BlendLibReader *reader, Key *key)
static void switch_endian_keyblock(Key *key, KeyBlock *kb)
{
- int elemsize, a, b;
- char *data;
+ int elemsize = key->elemsize;
+ char *data = kb->data;
- elemsize = key->elemsize;
- data = kb->data;
-
- for (a = 0; a < kb->totelem; a++) {
+ for (int a = 0; a < kb->totelem; a++) {
const char *cp = key->elemstr;
char *poin = data;
@@ -3917,11 +3421,12 @@ static void switch_endian_keyblock(Key *key, KeyBlock *kb)
switch (cp[1]) { /* cp[1] = type */
case IPO_FLOAT:
case IPO_BPOINT:
- case IPO_BEZTRIPLE:
- b = cp[0];
+ case IPO_BEZTRIPLE: {
+ int b = cp[0];
BLI_endian_switch_float_array((float *)poin, b);
poin += sizeof(float) * b;
break;
+ }
}
cp += 2;
@@ -3932,16 +3437,14 @@ static void switch_endian_keyblock(Key *key, KeyBlock *kb)
static void direct_link_key(BlendDataReader *reader, Key *key)
{
- KeyBlock *kb;
-
BLO_read_list(reader, &(key->block));
BLO_read_data_address(reader, &key->adt);
- direct_link_animdata(reader, key->adt);
+ BKE_animdata_blend_read_data(reader, key->adt);
BLO_read_data_address(reader, &key->refkey);
- for (kb = key->block.first; kb; kb = kb->next) {
+ LISTBASE_FOREACH (KeyBlock *, kb, &key->block) {
BLO_read_data_address(reader, &kb->data);
if (BLO_read_requires_endian_switch(reader)) {
@@ -3968,7 +3471,7 @@ static void lib_link_mball(BlendLibReader *reader, MetaBall *mb)
static void direct_link_mball(BlendDataReader *reader, MetaBall *mb)
{
BLO_read_data_address(reader, &mb->adt);
- direct_link_animdata(reader, mb->adt);
+ BKE_animdata_blend_read_data(reader, mb->adt);
BLO_read_pointer_array(reader, (void **)&mb->mat);
@@ -3997,7 +3500,7 @@ static void lib_link_world(BlendLibReader *reader, World *wrld)
static void direct_link_world(BlendDataReader *reader, World *wrld)
{
BLO_read_data_address(reader, &wrld->adt);
- direct_link_animdata(reader, wrld->adt);
+ BKE_animdata_blend_read_data(reader, wrld->adt);
wrld->preview = direct_link_preview_image(reader, wrld->preview);
BLI_listbase_clear(&wrld->gpumaterial);
@@ -4032,8 +3535,6 @@ static void lib_link_text(BlendLibReader *UNUSED(reader), Text *UNUSED(text))
static void direct_link_text(BlendDataReader *reader, Text *text)
{
- TextLine *ln;
-
BLO_read_data_address(reader, &text->filepath);
text->compiled = NULL;
@@ -4050,7 +3551,7 @@ static void direct_link_text(BlendDataReader *reader, Text *text)
BLO_read_data_address(reader, &text->curl);
BLO_read_data_address(reader, &text->sell);
- for (ln = text->lines.first; ln; ln = ln->next) {
+ LISTBASE_FOREACH (TextLine *, ln, &text->lines) {
BLO_read_data_address(reader, &ln->line);
ln->format = NULL;
@@ -4084,12 +3585,10 @@ static void lib_link_image(BlendLibReader *UNUSED(reader), Image *ima)
static void direct_link_image(BlendDataReader *reader, Image *ima)
{
- ImagePackedFile *imapf;
-
BLO_read_list(reader, &ima->tiles);
BLO_read_list(reader, &(ima->renderslots));
- if (reader->fd->memfile == NULL) {
+ if (!BLO_read_data_is_undo(reader)) {
/* We reset this last render slot index only when actually reading a file, not for undo. */
ima->last_render_slot = ima->render_slot;
}
@@ -4098,7 +3597,7 @@ static void direct_link_image(BlendDataReader *reader, Image *ima)
BLO_read_list(reader, &(ima->packedfiles));
if (ima->packedfiles.first) {
- for (imapf = ima->packedfiles.first; imapf; imapf = imapf->next) {
+ LISTBASE_FOREACH (ImagePackedFile *, imapf, &ima->packedfiles) {
imapf->packedfile = direct_link_packedfile(reader, imapf->packedfile);
}
ima->packedfile = NULL;
@@ -4151,11 +3650,8 @@ static void switch_endian_knots(Nurb *nu)
static void direct_link_curve(BlendDataReader *reader, Curve *cu)
{
- Nurb *nu;
- TextBox *tb;
-
BLO_read_data_address(reader, &cu->adt);
- direct_link_animdata(reader, cu->adt);
+ BKE_animdata_blend_read_data(reader, cu->adt);
/* Protect against integer overflow vulnerability. */
CLAMP(cu->len_char32, 0, INT_MAX - 4);
@@ -4172,7 +3668,7 @@ static void direct_link_curve(BlendDataReader *reader, Curve *cu)
else {
cu->nurb.first = cu->nurb.last = NULL;
- tb = MEM_calloc_arrayN(MAXTEXTBOX, sizeof(TextBox), "TextBoxread");
+ TextBox *tb = MEM_calloc_arrayN(MAXTEXTBOX, sizeof(TextBox), "TextBoxread");
if (cu->tb) {
memcpy(tb, cu->tb, cu->totbox * sizeof(TextBox));
MEM_freeN(cu->tb);
@@ -4193,7 +3689,7 @@ static void direct_link_curve(BlendDataReader *reader, Curve *cu)
cu->editfont = NULL;
cu->batch_cache = NULL;
- for (nu = cu->nurb.first; nu; nu = nu->next) {
+ LISTBASE_FOREACH (Nurb *, nu, &cu->nurb) {
BLO_read_data_address(reader, &nu->bezt);
BLO_read_data_address(reader, &nu->bp);
BLO_read_data_address(reader, &nu->knotsu);
@@ -4224,7 +3720,7 @@ static void lib_link_texture(BlendLibReader *reader, Tex *tex)
static void direct_link_texture(BlendDataReader *reader, Tex *tex)
{
BLO_read_data_address(reader, &tex->adt);
- direct_link_animdata(reader, tex->adt);
+ BKE_animdata_blend_read_data(reader, tex->adt);
BLO_read_data_address(reader, &tex->coba);
@@ -4259,7 +3755,7 @@ static void lib_link_material(BlendLibReader *reader, Material *ma)
static void direct_link_material(BlendDataReader *reader, Material *ma)
{
BLO_read_data_address(reader, &ma->adt);
- direct_link_animdata(reader, ma->adt);
+ BKE_animdata_blend_read_data(reader, ma->adt);
ma->texpaintslot = NULL;
@@ -4290,9 +3786,7 @@ static const char *ptcache_data_struct[] = {
static void direct_link_pointcache_cb(BlendDataReader *reader, void *data)
{
PTCacheMem *pm = data;
- PTCacheExtra *extra;
- int i;
- for (i = 0; i < BPHYS_TOT_DATA; i++) {
+ for (int i = 0; i < BPHYS_TOT_DATA; i++) {
BLO_read_data_address(reader, &pm->data[i]);
/* the cache saves non-struct data without DNA */
@@ -4309,7 +3803,7 @@ static void direct_link_pointcache_cb(BlendDataReader *reader, void *data)
BLO_read_list(reader, &pm->extradata);
- for (extra = pm->extradata.first; extra; extra = extra->next) {
+ LISTBASE_FOREACH (PTCacheExtra *, extra, &pm->extradata) {
BLO_read_data_address(reader, &extra->data);
}
}
@@ -4337,9 +3831,8 @@ static void direct_link_pointcache_list(BlendDataReader *reader,
int force_disk)
{
if (ptcaches->first) {
- PointCache *cache = NULL;
BLO_read_list(reader, ptcaches);
- for (cache = ptcaches->first; cache; cache = cache->next) {
+ LISTBASE_FOREACH (PointCache *, cache, ptcaches) {
direct_link_pointcache(reader, cache);
if (force_disk) {
cache->flag |= PTCACHE_DISK_CACHE;
@@ -4402,11 +3895,8 @@ static void lib_link_particlesettings(BlendLibReader *reader, ParticleSettings *
}
if (part->boids) {
- BoidState *state = part->boids->states.first;
- BoidRule *rule;
- for (; state; state = state->next) {
- rule = state->rules.first;
- for (; rule; rule = rule->next) {
+ LISTBASE_FOREACH (BoidState *, state, &part->boids->states) {
+ LISTBASE_FOREACH (BoidRule *, rule, &state->rules) {
switch (rule->type) {
case eBoidRuleType_Goal:
case eBoidRuleType_Avoid: {
@@ -4442,13 +3932,11 @@ static void direct_link_partdeflect(PartDeflect *pd)
static void direct_link_particlesettings(BlendDataReader *reader, ParticleSettings *part)
{
- int a;
-
BLO_read_data_address(reader, &part->adt);
BLO_read_data_address(reader, &part->pd);
BLO_read_data_address(reader, &part->pd2);
- direct_link_animdata(reader, part->adt);
+ BKE_animdata_blend_read_data(reader, part->adt);
direct_link_partdeflect(part->pd);
direct_link_partdeflect(part->pd2);
@@ -4476,16 +3964,15 @@ static void direct_link_particlesettings(BlendDataReader *reader, ParticleSettin
BLO_read_data_address(reader, &part->fluid);
if (part->boids) {
- BoidState *state;
BLO_read_list(reader, &part->boids->states);
- for (state = part->boids->states.first; state; state = state->next) {
+ LISTBASE_FOREACH (BoidState *, state, &part->boids->states) {
BLO_read_list(reader, &state->rules);
BLO_read_list(reader, &state->conditions);
BLO_read_list(reader, &state->actions);
}
}
- for (a = 0; a < MAX_MTEX; a++) {
+ for (int a = 0; a < MAX_MTEX; a++) {
BLO_read_data_address(reader, &part->mtex[a]);
}
@@ -4498,16 +3985,11 @@ static void lib_link_particlesystems(BlendLibReader *reader,
ID *id,
ListBase *particles)
{
- ParticleSystem *psys, *psysnext;
-
- for (psys = particles->first; psys; psys = psysnext) {
- psysnext = psys->next;
+ LISTBASE_FOREACH_MUTABLE (ParticleSystem *, psys, particles) {
BLO_read_id_address(reader, id->lib, &psys->part);
if (psys->part) {
- ParticleTarget *pt = psys->targets.first;
-
- for (; pt; pt = pt->next) {
+ LISTBASE_FOREACH (ParticleTarget *, pt, &psys->targets) {
BLO_read_id_address(reader, id->lib, &pt->ob);
}
@@ -4536,11 +4018,10 @@ static void lib_link_particlesystems(BlendLibReader *reader,
}
static void direct_link_particlesystems(BlendDataReader *reader, ListBase *particles)
{
- ParticleSystem *psys;
ParticleData *pa;
int a;
- for (psys = particles->first; psys; psys = psys->next) {
+ LISTBASE_FOREACH (ParticleSystem *, psys, particles) {
BLO_read_data_address(reader, &psys->particles);
if (psys->particles && psys->particles->hair) {
@@ -4629,255 +4110,6 @@ static void direct_link_particlesystems(BlendDataReader *reader, ListBase *parti
/** \name Read ID: Mesh
* \{ */
-static void lib_link_mesh(BlendLibReader *reader, Mesh *me)
-{
- /* this check added for python created meshes */
- if (me->mat) {
- for (int i = 0; i < me->totcol; i++) {
- BLO_read_id_address(reader, me->id.lib, &me->mat[i]);
- }
- }
- else {
- me->totcol = 0;
- }
-
- BLO_read_id_address(reader, me->id.lib, &me->ipo); // XXX: deprecated: old anim sys
- BLO_read_id_address(reader, me->id.lib, &me->key);
- BLO_read_id_address(reader, me->id.lib, &me->texcomesh);
-}
-
-static void direct_link_dverts(BlendDataReader *reader, int count, MDeformVert *mdverts)
-{
- int i;
-
- if (mdverts == NULL) {
- return;
- }
-
- for (i = count; i > 0; i--, mdverts++) {
- /*convert to vgroup allocation system*/
- MDeformWeight *dw;
- if (mdverts->dw && (dw = BLO_read_get_new_data_address(reader, mdverts->dw))) {
- const ssize_t dw_len = mdverts->totweight * sizeof(MDeformWeight);
- void *dw_tmp = MEM_mallocN(dw_len, "direct_link_dverts");
- memcpy(dw_tmp, dw, dw_len);
- mdverts->dw = dw_tmp;
- MEM_freeN(dw);
- }
- else {
- mdverts->dw = NULL;
- mdverts->totweight = 0;
- }
- }
-}
-
-static void direct_link_mdisps(BlendDataReader *reader, int count, MDisps *mdisps, int external)
-{
- if (mdisps) {
- int i;
-
- for (i = 0; i < count; i++) {
- BLO_read_data_address(reader, &mdisps[i].disps);
- BLO_read_data_address(reader, &mdisps[i].hidden);
-
- if (mdisps[i].totdisp && !mdisps[i].level) {
- /* this calculation is only correct for loop mdisps;
- * if loading pre-BMesh face mdisps this will be
- * overwritten with the correct value in
- * bm_corners_to_loops() */
- float gridsize = sqrtf(mdisps[i].totdisp);
- mdisps[i].level = (int)(logf(gridsize - 1.0f) / (float)M_LN2) + 1;
- }
-
- if (BLO_read_requires_endian_switch(reader) && (mdisps[i].disps)) {
- /* DNA_struct_switch_endian doesn't do endian swap for (*disps)[] */
- /* this does swap for data written at write_mdisps() - readfile.c */
- BLI_endian_switch_float_array(*mdisps[i].disps, mdisps[i].totdisp * 3);
- }
- if (!external && !mdisps[i].disps) {
- mdisps[i].totdisp = 0;
- }
- }
- }
-}
-
-static void direct_link_grid_paint_mask(BlendDataReader *reader,
- int count,
- GridPaintMask *grid_paint_mask)
-{
- if (grid_paint_mask) {
- int i;
-
- for (i = 0; i < count; i++) {
- GridPaintMask *gpm = &grid_paint_mask[i];
- if (gpm->data) {
- BLO_read_data_address(reader, &gpm->data);
- }
- }
- }
-}
-
-/*this isn't really a public api function, so prototyped here*/
-static void direct_link_customdata(BlendDataReader *reader, CustomData *data, int count)
-{
- int i = 0;
-
- BLO_read_data_address(reader, &data->layers);
-
- /* annoying workaround for bug [#31079] loading legacy files with
- * no polygons _but_ have stale customdata */
- if (UNLIKELY(count == 0 && data->layers == NULL && data->totlayer != 0)) {
- CustomData_reset(data);
- return;
- }
-
- BLO_read_data_address(reader, &data->external);
-
- while (i < data->totlayer) {
- CustomDataLayer *layer = &data->layers[i];
-
- if (layer->flag & CD_FLAG_EXTERNAL) {
- layer->flag &= ~CD_FLAG_IN_MEMORY;
- }
-
- layer->flag &= ~CD_FLAG_NOFREE;
-
- if (CustomData_verify_versions(data, i)) {
- BLO_read_data_address(reader, &layer->data);
- if (layer->type == CD_MDISPS) {
- direct_link_mdisps(reader, count, layer->data, layer->flag & CD_FLAG_EXTERNAL);
- }
- else if (layer->type == CD_GRID_PAINT_MASK) {
- direct_link_grid_paint_mask(reader, count, layer->data);
- }
- i++;
- }
- }
-
- CustomData_update_typemap(data);
-}
-
-static void direct_link_mesh(BlendDataReader *reader, Mesh *mesh)
-{
- BLO_read_pointer_array(reader, (void **)&mesh->mat);
-
- 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->mselect);
-
- /* animdata */
- BLO_read_data_address(reader, &mesh->adt);
- direct_link_animdata(reader, mesh->adt);
-
- /* Normally direct_link_dverts should be called in direct_link_customdata,
- * but for backwards compatibility in do_versions to work we do it here. */
- direct_link_dverts(reader, mesh->totvert, mesh->dvert);
-
- direct_link_customdata(reader, &mesh->vdata, mesh->totvert);
- direct_link_customdata(reader, &mesh->edata, mesh->totedge);
- direct_link_customdata(reader, &mesh->fdata, mesh->totface);
- direct_link_customdata(reader, &mesh->ldata, mesh->totloop);
- direct_link_customdata(reader, &mesh->pdata, mesh->totpoly);
-
- mesh->texflag &= ~ME_AUTOSPACE_EVALUATED;
- mesh->edit_mesh = NULL;
- BKE_mesh_runtime_reset(mesh);
-
- /* happens with old files */
- if (mesh->mselect == NULL) {
- mesh->totselect = 0;
- }
-
- /* Multires data */
- BLO_read_data_address(reader, &mesh->mr);
- if (mesh->mr) {
- MultiresLevel *lvl;
-
- BLO_read_list(reader, &mesh->mr->levels);
- lvl = mesh->mr->levels.first;
-
- direct_link_customdata(reader, &mesh->mr->vdata, lvl->totvert);
- direct_link_dverts(reader, lvl->totvert, CustomData_get(&mesh->mr->vdata, 0, CD_MDEFORMVERT));
- direct_link_customdata(reader, &mesh->mr->fdata, lvl->totface);
-
- BLO_read_data_address(reader, &mesh->mr->edge_flags);
- BLO_read_data_address(reader, &mesh->mr->edge_creases);
-
- BLO_read_data_address(reader, &mesh->mr->verts);
-
- /* If mesh has the same number of vertices as the
- * highest multires level, load the current mesh verts
- * into multires and discard the old data. Needed
- * because some saved files either do not have a verts
- * array, or the verts array contains out-of-date
- * data. */
- if (mesh->totvert == ((MultiresLevel *)mesh->mr->levels.last)->totvert) {
- if (mesh->mr->verts) {
- MEM_freeN(mesh->mr->verts);
- }
- mesh->mr->verts = MEM_dupallocN(mesh->mvert);
- }
-
- for (; lvl; lvl = lvl->next) {
- BLO_read_data_address(reader, &lvl->verts);
- BLO_read_data_address(reader, &lvl->faces);
- BLO_read_data_address(reader, &lvl->edges);
- BLO_read_data_address(reader, &lvl->colfaces);
- }
- }
-
- /* if multires is present but has no valid vertex data,
- * there's no way to recover it; silently remove multires */
- if (mesh->mr && !mesh->mr->verts) {
- multires_free(mesh->mr);
- mesh->mr = NULL;
- }
-
- if ((BLO_read_requires_endian_switch(reader)) && mesh->tface) {
- TFace *tf = mesh->tface;
- int i;
-
- for (i = 0; i < mesh->totface; i++, tf++) {
- BLI_endian_switch_uint32_array(tf->col, 4);
- }
- }
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Read ID: Lattice
- * \{ */
-
-static void lib_link_latt(BlendLibReader *reader, Lattice *lt)
-{
- BLO_read_id_address(reader, lt->id.lib, &lt->ipo); // XXX deprecated - old animation system
- BLO_read_id_address(reader, lt->id.lib, &lt->key);
-}
-
-static void direct_link_latt(BlendDataReader *reader, Lattice *lt)
-{
- BLO_read_data_address(reader, &lt->def);
-
- BLO_read_data_address(reader, &lt->dvert);
- direct_link_dverts(reader, lt->pntsu * lt->pntsv * lt->pntsw, lt->dvert);
-
- lt->editlatt = NULL;
- lt->batch_cache = NULL;
-
- BLO_read_data_address(reader, &lt->adt);
- direct_link_animdata(reader, lt->adt);
-}
-
/** \} */
/* -------------------------------------------------------------------- */
@@ -4900,7 +4132,7 @@ static void lib_link_modifiers(BlendLibReader *reader, Object *ob)
/* If linking from a library, clear 'local' library override flag. */
if (ob->id.lib != NULL) {
- for (ModifierData *mod = ob->modifiers.first; mod != NULL; mod = mod->next) {
+ LISTBASE_FOREACH (ModifierData *, mod, &ob->modifiers) {
mod->flag &= ~eModifierFlag_OverrideLibrary_Local;
}
}
@@ -4912,8 +4144,7 @@ static void lib_link_gpencil_modifiers(BlendLibReader *reader, Object *ob)
/* If linking from a library, clear 'local' library override flag. */
if (ob->id.lib != NULL) {
- for (GpencilModifierData *mod = ob->greasepencil_modifiers.first; mod != NULL;
- mod = mod->next) {
+ LISTBASE_FOREACH (GpencilModifierData *, mod, &ob->greasepencil_modifiers) {
mod->flag &= ~eGpencilModifierFlag_OverrideLibrary_Local;
}
}
@@ -4925,7 +4156,7 @@ static void lib_link_shaderfxs(BlendLibReader *reader, Object *ob)
/* If linking from a library, clear 'local' library override flag. */
if (ob->id.lib != NULL) {
- for (ShaderFxData *fx = ob->shader_fx.first; fx != NULL; fx = fx->next) {
+ LISTBASE_FOREACH (ShaderFxData *, fx, &ob->shader_fx) {
fx->flag &= ~eShaderFxFlag_OverrideLibrary_Local;
}
}
@@ -4934,7 +4165,6 @@ static void lib_link_shaderfxs(BlendLibReader *reader, Object *ob)
static void lib_link_object(BlendLibReader *reader, Object *ob)
{
bool warn = false;
- int a;
// XXX deprecated - old animation system <<<
BLO_read_id_address(reader, ob->id.lib, &ob->ipo);
@@ -5013,7 +4243,7 @@ static void lib_link_object(BlendLibReader *reader, Object *ob)
ob->mode &= ~OB_MODE_POSE;
}
}
- for (a = 0; a < ob->totcol; a++) {
+ for (int a = 0; a < ob->totcol; a++) {
BLO_read_id_address(reader, ob->id.lib, &ob->mat[a]);
}
@@ -5094,17 +4324,6 @@ static void lib_link_object(BlendLibReader *reader, Object *ob)
BLO_read_id_address(reader, ob->id.lib, &ob->rigidbody_constraint->ob2);
}
- {
- LodLevel *level;
- for (level = ob->lodlevels.first; level; level = level->next) {
- BLO_read_id_address(reader, ob->id.lib, &level->source);
-
- if (!level->source && level == ob->lodlevels.first) {
- level->source = ob;
- }
- }
- }
-
if (warn) {
BKE_report(reader->fd->reports, RPT_WARNING, "Warning in console");
}
@@ -5128,8 +4347,6 @@ static void direct_link_motionpath(BlendDataReader *reader, bMotionPath *mpath)
static void direct_link_pose(BlendDataReader *reader, bPose *pose)
{
- bPoseChannel *pchan;
-
if (!pose) {
return;
}
@@ -5140,7 +4357,7 @@ static void direct_link_pose(BlendDataReader *reader, bPose *pose)
pose->chanhash = NULL;
pose->chan_array = NULL;
- for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) {
+ LISTBASE_FOREACH (bPoseChannel *, pchan, &pose->chanbase) {
BKE_pose_channel_runtime_reset(&pchan->runtime);
BKE_pose_channel_session_uuid_generate(pchan);
@@ -5155,7 +4372,7 @@ static void direct_link_pose(BlendDataReader *reader, bPose *pose)
direct_link_constraints(reader, &pchan->constraints);
BLO_read_data_address(reader, &pchan->prop);
- IDP_DirectLinkGroup_OrFree(&pchan->prop, reader);
+ IDP_BlendDataRead(reader, &pchan->prop);
BLO_read_data_address(reader, &pchan->mpath);
if (pchan->mpath) {
@@ -5292,11 +4509,9 @@ static ModifierData *modifier_replace_with_fluid(FileData *fd,
static void direct_link_modifiers(BlendDataReader *reader, ListBase *lb, Object *ob)
{
- ModifierData *md;
-
BLO_read_list(reader, lb);
- for (md = lb->first; md; md = md->next) {
+ LISTBASE_FOREACH (ModifierData *, md, lb) {
BKE_modifier_session_uuid_generate(md);
md->error = NULL;
@@ -5453,10 +4668,9 @@ static void direct_link_modifiers(BlendDataReader *reader, ListBase *lb, Object
pmd->canvas->flags &= ~MOD_DPAINT_BAKING; /* just in case */
if (pmd->canvas->surfaces.first) {
- DynamicPaintSurface *surface;
BLO_read_list(reader, &pmd->canvas->surfaces);
- for (surface = pmd->canvas->surfaces.first; surface; surface = surface->next) {
+ LISTBASE_FOREACH (DynamicPaintSurface *, surface, &pmd->canvas->surfaces) {
surface->canvas = pmd->canvas;
surface->data = NULL;
direct_link_pointcache_list(reader, &(surface->ptcaches), &(surface->pointcache), 1);
@@ -5485,11 +4699,9 @@ static void direct_link_modifiers(BlendDataReader *reader, ListBase *lb, Object
static void direct_link_gpencil_modifiers(BlendDataReader *reader, ListBase *lb)
{
- GpencilModifierData *md;
-
BLO_read_list(reader, lb);
- for (md = lb->first; md; md = md->next) {
+ LISTBASE_FOREACH (GpencilModifierData *, md, lb) {
md->error = NULL;
/* if modifiers disappear, or for upward compatibility */
@@ -5566,11 +4778,9 @@ static void direct_link_gpencil_modifiers(BlendDataReader *reader, ListBase *lb)
static void direct_link_shaderfxs(BlendDataReader *reader, ListBase *lb)
{
- ShaderFxData *fx;
-
BLO_read_list(reader, lb);
- for (fx = lb->first; fx; fx = fx->next) {
+ LISTBASE_FOREACH (ShaderFxData *, fx, lb) {
fx->error = NULL;
/* if shader disappear, or for upward compatibility */
@@ -5594,15 +4804,16 @@ static void direct_link_object(BlendDataReader *reader, Object *ob)
* Also when linking in a file don't allow edit and pose modes.
* See [#34776, #42780] for more information.
*/
- if (reader->fd->memfile || (ob->id.tag & (LIB_TAG_EXTERN | LIB_TAG_INDIRECT))) {
+ const bool is_undo = BLO_read_data_is_undo(reader);
+ if (is_undo || (ob->id.tag & (LIB_TAG_EXTERN | LIB_TAG_INDIRECT))) {
ob->mode &= ~(OB_MODE_EDIT | OB_MODE_PARTICLE_EDIT);
- if (!reader->fd->memfile) {
+ if (!is_undo) {
ob->mode &= ~OB_MODE_POSE;
}
}
BLO_read_data_address(reader, &ob->adt);
- direct_link_animdata(reader, ob->adt);
+ BKE_animdata_blend_read_data(reader, ob->adt);
BLO_read_data_address(reader, &ob->pose);
direct_link_pose(reader, ob->pose);
@@ -5684,20 +4895,16 @@ static void direct_link_object(BlendDataReader *reader, Object *ob)
if (ob->soft) {
SoftBody *sb = ob->soft;
- sb->bpoint = NULL; // init pointers so it gets rebuilt nicely
+ /* init pointers so it gets rebuilt nicely */
+ sb->bpoint = NULL;
sb->bspring = NULL;
sb->scratch = NULL;
- sb->admmpd = NULL;
-
- /* Re-alloc or read custom data structs */
- sbCustomRead(ob);
/* although not used anymore */
/* still have to be loaded to be compatible with old files */
BLO_read_pointer_array(reader, (void **)&sb->keys);
if (sb->keys) {
- int a;
- for (a = 0; a < sb->totkey; a++) {
+ for (int a = 0; a < sb->totkey; a++) {
BLO_read_data_address(reader, &sb->keys[a]);
}
}
@@ -5779,14 +4986,11 @@ static void direct_link_object(BlendDataReader *reader, Object *ob)
if (ob->sculpt) {
ob->sculpt = NULL;
/* Only create data on undo, otherwise rely on editor mode switching. */
- if (reader->fd->memfile && (ob->mode & OB_MODE_ALL_SCULPT)) {
+ if (BLO_read_data_is_undo(reader) && (ob->mode & OB_MODE_ALL_SCULPT)) {
BKE_object_sculpt_data_create(ob);
}
}
- BLO_read_list(reader, &ob->lodlevels);
- ob->currentlod = ob->lodlevels.first;
-
ob->preview = direct_link_preview_image(reader, ob->preview);
}
@@ -5833,7 +5037,7 @@ static void direct_link_view_layer(BlendDataReader *reader, ViewLayer *view_laye
BLO_read_data_address(reader, &view_layer->active_collection);
BLO_read_data_address(reader, &view_layer->id_properties);
- IDP_DirectLinkGroup_OrFree(&view_layer->id_properties, reader);
+ IDP_BlendDataRead(reader, &view_layer->id_properties);
BLO_read_list(reader, &(view_layer->freestyle_config.modules));
BLO_read_list(reader, &(view_layer->freestyle_config.linesets));
@@ -5853,9 +5057,8 @@ static void lib_link_layer_collection(BlendLibReader *reader,
BLO_read_id_address(reader, lib, &layer_collection->collection);
}
- for (LayerCollection *layer_collection_nested = layer_collection->layer_collections.first;
- layer_collection_nested != NULL;
- layer_collection_nested = layer_collection_nested->next) {
+ LISTBASE_FOREACH (
+ LayerCollection *, layer_collection_nested, &layer_collection->layer_collections) {
lib_link_layer_collection(reader, lib, layer_collection_nested, false);
}
}
@@ -5886,15 +5089,13 @@ static void lib_link_view_layer(BlendLibReader *reader, Library *lib, ViewLayer
}
}
- for (LayerCollection *layer_collection = view_layer->layer_collections.first;
- layer_collection != NULL;
- layer_collection = layer_collection->next) {
+ LISTBASE_FOREACH (LayerCollection *, layer_collection, &view_layer->layer_collections) {
lib_link_layer_collection(reader, lib, layer_collection, true);
}
BLO_read_id_address(reader, lib, &view_layer->mat_override);
- IDP_LibLinkProperty(view_layer->id_properties, reader);
+ IDP_BlendReadLib(reader, view_layer->id_properties);
}
/** \} */
@@ -5955,8 +5156,7 @@ static void direct_link_collection(BlendDataReader *reader, Collection *collecti
static void lib_link_collection_data(BlendLibReader *reader, Library *lib, Collection *collection)
{
- for (CollectionObject *cob = collection->gobject.first, *cob_next = NULL; cob; cob = cob_next) {
- cob_next = cob->next;
+ LISTBASE_FOREACH_MUTABLE (CollectionObject *, cob, &collection->gobject) {
BLO_read_id_address(reader, lib, &cob->ob);
if (cob->ob == NULL) {
@@ -5964,7 +5164,7 @@ static void lib_link_collection_data(BlendLibReader *reader, Library *lib, Colle
}
}
- for (CollectionChild *child = collection->children.first; child != NULL; child = child->next) {
+ LISTBASE_FOREACH (CollectionChild *, child, &collection->children) {
BLO_read_id_address(reader, lib, &child->collection);
}
}
@@ -5993,9 +5193,8 @@ static void lib_link_collection(BlendLibReader *reader, Collection *collection)
/* patch for missing scene IDs, can't be in do-versions */
static void composite_patch(bNodeTree *ntree, Scene *scene)
{
- bNode *node;
- for (node = ntree->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->id == NULL && node->type == CMP_NODE_R_LAYERS) {
node->id = &scene->id;
}
@@ -6020,9 +5219,7 @@ static void link_paint(BlendLibReader *reader, Scene *sce, Paint *p)
static void lib_link_sequence_modifiers(BlendLibReader *reader, Scene *scene, ListBase *lb)
{
- SequenceModifierData *smd;
-
- for (smd = lb->first; smd; smd = smd->next) {
+ LISTBASE_FOREACH (SequenceModifierData *, smd, lb) {
if (smd->mask_id) {
BLO_read_id_address(reader, scene->id.lib, &smd->mask_id);
}
@@ -6074,7 +5271,7 @@ static void direct_link_view3dshading(BlendDataReader *reader, View3DShading *sh
{
if (shading->prop) {
BLO_read_data_address(reader, &shading->prop);
- IDP_DirectLinkGroup_OrFree(&shading->prop, reader);
+ IDP_BlendDataRead(reader, &shading->prop);
}
}
@@ -6173,10 +5370,7 @@ static void lib_link_scene(BlendLibReader *reader, Scene *sce)
BLO_read_id_address(reader, sce->id.lib, &sce->toolsettings->gp_sculpt.guide.reference_object);
- for (Base *base_legacy_next, *base_legacy = sce->base.first; base_legacy;
- base_legacy = base_legacy_next) {
- base_legacy_next = base_legacy->next;
-
+ LISTBASE_FOREACH_MUTABLE (Base *, base_legacy, &sce->base) {
BLO_read_id_address(reader, sce->id.lib, &base_legacy->object);
if (base_legacy->object == NULL) {
@@ -6193,8 +5387,8 @@ static void lib_link_scene(BlendLibReader *reader, Scene *sce)
}
Sequence *seq;
- SEQ_BEGIN (sce->ed, seq) {
- IDP_LibLinkProperty(seq->prop, reader);
+ SEQ_ALL_BEGIN (sce->ed, seq) {
+ IDP_BlendReadLib(reader, seq->prop);
if (seq->ipo) {
BLO_read_id_address(
@@ -6235,7 +5429,7 @@ static void lib_link_scene(BlendLibReader *reader, Scene *sce)
lib_link_sequence_modifiers(reader, sce, &seq->modifiers);
}
- SEQ_END;
+ SEQ_ALL_END;
LISTBASE_FOREACH (TimeMarker *, marker, &sce->markers) {
if (marker->camera) {
@@ -6299,7 +5493,7 @@ static void lib_link_scenes_check_set(Main *bmain)
{
#ifdef USE_SETSCENE_CHECK
const int totscene = BLI_listbase_count(&bmain->scenes);
- for (Scene *sce = bmain->scenes.first; sce; sce = sce->id.next) {
+ 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)) {
@@ -6316,11 +5510,9 @@ static void lib_link_scenes_check_set(Main *bmain)
static void link_recurs_seq(BlendDataReader *reader, ListBase *lb)
{
- Sequence *seq;
-
BLO_read_list(reader, lb);
- for (seq = lb->first; seq; seq = seq->next) {
+ LISTBASE_FOREACH (Sequence *, seq, lb) {
if (seq->seqbase.first) {
link_recurs_seq(reader, &seq->seqbase);
}
@@ -6365,11 +5557,9 @@ static void direct_link_paint_helper(BlendDataReader *reader, const Scene *scene
static void direct_link_sequence_modifiers(BlendDataReader *reader, ListBase *lb)
{
- SequenceModifierData *smd;
-
BLO_read_list(reader, lb);
- for (smd = lb->first; smd; smd = smd->next) {
+ LISTBASE_FOREACH (SequenceModifierData *, smd, lb) {
if (smd->mask_sequence) {
BLO_read_data_address(reader, &smd->mask_sequence);
}
@@ -6389,13 +5579,6 @@ static void direct_link_sequence_modifiers(BlendDataReader *reader, ListBase *lb
static void direct_link_scene(BlendDataReader *reader, Scene *sce)
{
- Editing *ed;
- Sequence *seq;
- MetaStack *ms;
- RigidBodyWorld *rbw;
- ViewLayer *view_layer;
- SceneRenderLayer *srl;
-
sce->depsgraph_hash = NULL;
sce->fps_info = NULL;
@@ -6410,7 +5593,7 @@ static void direct_link_scene(BlendDataReader *reader, Scene *sce)
BLO_read_list(reader, &(sce->base));
BLO_read_data_address(reader, &sce->adt);
- direct_link_animdata(reader, sce->adt);
+ BKE_animdata_blend_read_data(reader, sce->adt);
BLO_read_list(reader, &sce->keyingsets);
direct_link_keyingsets(reader, &sce->keyingsets);
@@ -6469,7 +5652,7 @@ static void direct_link_scene(BlendDataReader *reader, Scene *sce)
ListBase *old_seqbasep = &sce->ed->seqbase;
BLO_read_data_address(reader, &sce->ed);
- ed = sce->ed;
+ Editing *ed = sce->ed;
BLO_read_data_address(reader, &ed->act_seq);
ed->cache = NULL;
@@ -6478,7 +5661,8 @@ static void direct_link_scene(BlendDataReader *reader, Scene *sce)
/* recursive link sequences, lb will be correctly initialized */
link_recurs_seq(reader, &ed->seqbase);
- SEQ_BEGIN (ed, seq) {
+ Sequence *seq;
+ SEQ_ALL_BEGIN (ed, seq) {
/* Do as early as possible, so that other parts of reading can rely on valid session UUID. */
BKE_sequence_session_uuid_generate(seq);
@@ -6509,7 +5693,7 @@ static void direct_link_scene(BlendDataReader *reader, Scene *sce)
}
BLO_read_data_address(reader, &seq->prop);
- IDP_DirectLinkGroup_OrFree(&seq->prop, reader);
+ IDP_BlendDataRead(reader, &seq->prop);
BLO_read_data_address(reader, &seq->strip);
if (seq->strip && seq->strip->done == 0) {
@@ -6541,7 +5725,7 @@ static void direct_link_scene(BlendDataReader *reader, Scene *sce)
direct_link_sequence_modifiers(reader, &seq->modifiers);
}
- SEQ_END;
+ SEQ_ALL_END;
/* link metastack, slight abuse of structs here,
* have to restore pointer to internal part in struct */
@@ -6571,7 +5755,7 @@ static void direct_link_scene(BlendDataReader *reader, Scene *sce)
/* stack */
BLO_read_list(reader, &(ed->metastack));
- for (ms = ed->metastack.first; ms; ms = ms->next) {
+ LISTBASE_FOREACH (MetaStack *, ms, &ed->metastack) {
BLO_read_data_address(reader, &ms->parseq);
if (ms->oldbasep == old_seqbasep) {
@@ -6603,7 +5787,7 @@ static void direct_link_scene(BlendDataReader *reader, Scene *sce)
}
if (sce->r.ffcodecdata.properties) {
BLO_read_data_address(reader, &sce->r.ffcodecdata.properties);
- IDP_DirectLinkGroup_OrFree(&sce->r.ffcodecdata.properties, reader);
+ IDP_BlendDataRead(reader, &sce->r.ffcodecdata.properties);
}
BLO_read_list(reader, &(sce->markers));
@@ -6611,9 +5795,9 @@ static void direct_link_scene(BlendDataReader *reader, Scene *sce)
BLO_read_list(reader, &(sce->r.layers));
BLO_read_list(reader, &(sce->r.views));
- for (srl = sce->r.layers.first; srl; srl = srl->next) {
+ LISTBASE_FOREACH (SceneRenderLayer *, srl, &sce->r.layers) {
BLO_read_data_address(reader, &srl->prop);
- IDP_DirectLinkGroup_OrFree(&srl->prop, reader);
+ IDP_BlendDataRead(reader, &srl->prop);
BLO_read_list(reader, &(srl->freestyleConfig.modules));
BLO_read_list(reader, &(srl->freestyleConfig.linesets));
}
@@ -6621,7 +5805,7 @@ static void direct_link_scene(BlendDataReader *reader, Scene *sce)
direct_link_view_settings(reader, &sce->view_settings);
BLO_read_data_address(reader, &sce->rigidbody_world);
- rbw = sce->rigidbody_world;
+ RigidBodyWorld *rbw = sce->rigidbody_world;
if (rbw) {
BLO_read_data_address(reader, &rbw->shared);
@@ -6675,11 +5859,11 @@ static void direct_link_scene(BlendDataReader *reader, Scene *sce)
/* insert into global old-new map for reading without UI (link_global accesses it again) */
link_glob_list(reader->fd, &sce->view_layers);
- for (view_layer = sce->view_layers.first; view_layer; view_layer = view_layer->next) {
+ LISTBASE_FOREACH (ViewLayer *, view_layer, &sce->view_layers) {
direct_link_view_layer(reader, view_layer);
}
- if (reader->fd->memfile) {
+ if (BLO_read_data_is_undo(reader)) {
/* If it's undo do nothing here, caches are handled by higher-level generic calling code. */
}
else {
@@ -6694,7 +5878,7 @@ static void direct_link_scene(BlendDataReader *reader, Scene *sce)
direct_link_view3dshading(reader, &sce->display.shading);
BLO_read_data_address(reader, &sce->layer_properties);
- IDP_DirectLinkGroup_OrFree(&sce->layer_properties, reader);
+ IDP_BlendDataRead(reader, &sce->layer_properties);
}
/** \} */
@@ -6722,8 +5906,6 @@ static void lib_link_gpencil(BlendLibReader *reader, bGPdata *gpd)
/* relinks grease-pencil data - used for direct_link and old file linkage */
static void direct_link_gpencil(BlendDataReader *reader, bGPdata *gpd)
{
- bGPDpalette *palette;
-
/* we must firstly have some grease-pencil data to link! */
if (gpd == NULL) {
return;
@@ -6731,7 +5913,7 @@ static void direct_link_gpencil(BlendDataReader *reader, bGPdata *gpd)
/* relink animdata */
BLO_read_data_address(reader, &gpd->adt);
- direct_link_animdata(reader, gpd->adt);
+ BKE_animdata_blend_read_data(reader, gpd->adt);
/* Ensure full objectmode for linked grease pencil. */
if (gpd->id.lib != NULL) {
@@ -6751,7 +5933,7 @@ static void direct_link_gpencil(BlendDataReader *reader, bGPdata *gpd)
/* relink palettes (old palettes deprecated, only to convert old files) */
BLO_read_list(reader, &gpd->palettes);
if (gpd->palettes.first != NULL) {
- for (palette = gpd->palettes.first; palette; palette = palette->next) {
+ LISTBASE_FOREACH (Palette *, palette, &gpd->palettes) {
BLO_read_list(reader, &palette->colors);
}
}
@@ -6786,7 +5968,7 @@ static void direct_link_gpencil(BlendDataReader *reader, bGPdata *gpd)
/* relink weight data */
if (gps->dvert) {
BLO_read_data_address(reader, &gps->dvert);
- direct_link_dverts(reader, gps->totpoints, gps->dvert);
+ BKE_defvert_blend_read(reader, gps->totpoints, gps->dvert);
}
}
}
@@ -6814,19 +5996,17 @@ static void direct_link_panel_list(BlendDataReader *reader, ListBase *lb)
static void direct_link_region(BlendDataReader *reader, ARegion *region, int spacetype)
{
- uiList *ui_list;
-
direct_link_panel_list(reader, &region->panels);
BLO_read_list(reader, &region->panels_category_active);
BLO_read_list(reader, &region->ui_lists);
- for (ui_list = region->ui_lists.first; ui_list; ui_list = ui_list->next) {
+ LISTBASE_FOREACH (uiList *, ui_list, &region->ui_lists) {
ui_list->type = NULL;
ui_list->dyn_data = NULL;
BLO_read_data_address(reader, &ui_list->properties);
- IDP_DirectLinkGroup_OrFree(&ui_list->properties, reader);
+ IDP_BlendDataRead(reader, &ui_list->properties);
}
BLO_read_list(reader, &region->ui_previews);
@@ -6879,9 +6059,6 @@ static void direct_link_region(BlendDataReader *reader, ARegion *region, int spa
static void direct_link_area(BlendDataReader *reader, ScrArea *area)
{
- SpaceLink *sl;
- ARegion *region;
-
BLO_read_list(reader, &(area->spacedata));
BLO_read_list(reader, &(area->regionbase));
@@ -6906,7 +6083,7 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area)
area->spacetype = SPACE_EMPTY;
}
- for (region = area->regionbase.first; region; region = region->next) {
+ LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
direct_link_region(reader, region, area->spacetype);
}
@@ -6922,7 +6099,7 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area)
blo_do_versions_view3d_split_250(area->spacedata.first, &area->regionbase);
}
- for (sl = area->spacedata.first; sl; sl = sl->next) {
+ LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
BLO_read_list(reader, &(sl->regionbase));
/* if we do not have the spacetype registered we cannot
@@ -6931,7 +6108,7 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area)
sl->spacetype = SPACE_EMPTY;
}
- for (region = sl->regionbase.first; region; region = region->next) {
+ LISTBASE_FOREACH (ARegion *, region, &sl->regionbase) {
direct_link_region(reader, region, sl->spacetype);
}
@@ -6986,8 +6163,7 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area)
space_outliner->treestore = BLI_mempool_create(
sizeof(TreeStoreElem), ts->usedelem, 512, BLI_MEMPOOL_ALLOW_ITER);
if (ts->usedelem && elems) {
- int i;
- for (i = 0; i < ts->usedelem; i++) {
+ for (int i = 0; i < ts->usedelem; i++) {
TreeStoreElem *new_elem = BLI_mempool_alloc(space_outliner->treestore);
*new_elem = elems[i];
}
@@ -7069,7 +6245,6 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area)
}
else if (sl->spacetype == SPACE_CONSOLE) {
SpaceConsole *sconsole = (SpaceConsole *)sl;
- ConsoleLine *cl, *cl_next;
BLO_read_list(reader, &sconsole->scrollback);
BLO_read_list(reader, &sconsole->history);
@@ -7080,8 +6255,7 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area)
/* comma expressions, (e.g. expr1, expr2, expr3) evaluate each expression,
* from left to right. the right-most expression sets the result of the comma
* expression as a whole*/
- for (cl = sconsole->history.first; cl; cl = cl_next) {
- cl_next = cl->next;
+ 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 */
@@ -7243,7 +6417,6 @@ static void lib_link_area(BlendLibReader *reader, ID *parent_id, ScrArea *area)
}
case SPACE_NODE: {
SpaceNode *snode = (SpaceNode *)sl;
- bNodeTreePath *path, *path_next;
/* node tree can be stored locally in id too, link this first */
BLO_read_id_address(reader, parent_id->lib, &snode->id);
@@ -7257,6 +6430,7 @@ static void lib_link_area(BlendLibReader *reader, ID *parent_id, ScrArea *area)
BLO_read_id_address(reader, parent_id->lib, &snode->nodetree);
}
+ bNodeTreePath *path;
for (path = snode->treepath.first; path; path = path->next) {
if (path == snode->treepath.first) {
/* first nodetree in path is same as snode->nodetree */
@@ -7272,6 +6446,7 @@ static void lib_link_area(BlendLibReader *reader, ID *parent_id, ScrArea *area)
}
/* remaining path entries are invalid, remove */
+ bNodeTreePath *path_next;
for (; path; path = path_next) {
path_next = path->next;
@@ -7354,12 +6529,10 @@ static void lib_link_wm_xr_data(BlendLibReader *reader, ID *parent_id, wmXrData
static void direct_link_windowmanager(BlendDataReader *reader, wmWindowManager *wm)
{
- wmWindow *win;
-
id_us_ensure_real(&wm->id);
BLO_read_list(reader, &wm->windows);
- for (win = wm->windows.first; win; win = win->next) {
+ LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
BLO_read_data_address(reader, &win->parent);
WorkSpaceInstanceHook *hook = win->workspace_hook;
@@ -7894,8 +7067,7 @@ void blo_lib_link_restore(Main *oldmain,
struct IDNameLib_Map *id_map = BKE_main_idmap_create(
newmain, true, oldmain, MAIN_IDMAP_TYPE_NAME);
- for (WorkSpace *workspace = newmain->workspaces.first; workspace;
- workspace = workspace->id.next) {
+ LISTBASE_FOREACH (WorkSpace *, workspace, &newmain->workspaces) {
LISTBASE_FOREACH (WorkSpaceLayout *, layout, &workspace->layouts) {
lib_link_workspace_layout_restore(id_map, newmain, layout);
}
@@ -7950,9 +7122,7 @@ void blo_lib_link_restore(Main *oldmain,
/* and as patch for 2.48 and older */
void blo_do_versions_view3d_split_250(View3D *v3d, ListBase *regions)
{
- ARegion *region;
-
- for (region = regions->first; region; region = region->next) {
+ LISTBASE_FOREACH (ARegion *, region, regions) {
if (region->regiontype == RGN_TYPE_WINDOW && region->regiondata == NULL) {
RegionView3D *rv3d;
@@ -8057,10 +7227,9 @@ static void lib_link_library(BlendLibReader *UNUSED(reader), Library *UNUSED(lib
* in relation to the blend file. */
static void fix_relpaths_library(const char *basepath, Main *main)
{
- Library *lib;
/* BLO_read_from_memory uses a blank filename */
if (basepath == NULL || basepath[0] == '\0') {
- for (lib = main->libraries.first; lib; lib = lib->id.next) {
+ 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
@@ -8072,7 +7241,7 @@ static void fix_relpaths_library(const char *basepath, Main *main)
}
}
else {
- for (lib = main->libraries.first; lib; lib = lib->id.next) {
+ 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. */
@@ -8098,7 +7267,7 @@ static void lib_link_lightprobe(BlendLibReader *reader, LightProbe *prb)
static void direct_link_lightprobe(BlendDataReader *reader, LightProbe *prb)
{
BLO_read_data_address(reader, &prb->adt);
- direct_link_animdata(reader, prb->adt);
+ BKE_animdata_blend_read_data(reader, prb->adt);
}
/** \} */
@@ -8115,7 +7284,7 @@ static void lib_link_speaker(BlendLibReader *reader, Speaker *spk)
static void direct_link_speaker(BlendDataReader *reader, Speaker *spk)
{
BLO_read_data_address(reader, &spk->adt);
- direct_link_animdata(reader, spk->adt);
+ BKE_animdata_blend_read_data(reader, spk->adt);
#if 0
spk->sound = newdataadr(fd, spk->sound);
@@ -8141,7 +7310,7 @@ static void direct_link_sound(BlendDataReader *reader, bSound *sound)
sound->cache = NULL;
}
- if (reader->fd->memfile != NULL) {
+ if (BLO_read_data_is_undo(reader)) {
sound->tags |= SOUND_TAGS_WAVEFORM_NO_RELOAD;
}
@@ -8175,26 +7344,20 @@ static void direct_link_movieReconstruction(BlendDataReader *reader,
static void direct_link_movieTracks(BlendDataReader *reader, ListBase *tracksbase)
{
- MovieTrackingTrack *track;
-
BLO_read_list(reader, tracksbase);
- for (track = tracksbase->first; track; track = track->next) {
+ LISTBASE_FOREACH (MovieTrackingTrack *, track, tracksbase) {
BLO_read_data_address(reader, &track->markers);
}
}
static void direct_link_moviePlaneTracks(BlendDataReader *reader, ListBase *plane_tracks_base)
{
- MovieTrackingPlaneTrack *plane_track;
-
BLO_read_list(reader, plane_tracks_base);
- for (plane_track = plane_tracks_base->first; plane_track; plane_track = plane_track->next) {
- int i;
-
+ LISTBASE_FOREACH (MovieTrackingPlaneTrack *, plane_track, plane_tracks_base) {
BLO_read_pointer_array(reader, (void **)&plane_track->point_tracks);
- for (i = 0; i < plane_track->point_tracksnr; i++) {
+ for (int i = 0; i < plane_track->point_tracksnr; i++) {
BLO_read_data_address(reader, &plane_track->point_tracks[i]);
}
@@ -8205,7 +7368,6 @@ static void direct_link_moviePlaneTracks(BlendDataReader *reader, ListBase *plan
static void direct_link_movieclip(BlendDataReader *reader, MovieClip *clip)
{
MovieTracking *tracking = &clip->tracking;
- MovieTrackingObject *object;
BLO_read_data_address(reader, &clip->adt);
@@ -8233,7 +7395,7 @@ static void direct_link_movieclip(BlendDataReader *reader, MovieClip *clip)
BLO_read_list(reader, &tracking->objects);
- for (object = tracking->objects.first; object; object = object->next) {
+ LISTBASE_FOREACH (MovieTrackingObject *, object, &tracking->objects) {
direct_link_movieTracks(reader, &object->tracks);
direct_link_moviePlaneTracks(reader, &object->plane_tracks);
direct_link_movieReconstruction(reader, &object->reconstruction);
@@ -8242,9 +7404,7 @@ static void direct_link_movieclip(BlendDataReader *reader, MovieClip *clip)
static void lib_link_movieTracks(BlendLibReader *reader, MovieClip *clip, ListBase *tracksbase)
{
- MovieTrackingTrack *track;
-
- for (track = tracksbase->first; track; track = track->next) {
+ LISTBASE_FOREACH (MovieTrackingTrack *, track, tracksbase) {
BLO_read_id_address(reader, clip->id.lib, &track->gpd);
}
}
@@ -8253,9 +7413,7 @@ static void lib_link_moviePlaneTracks(BlendLibReader *reader,
MovieClip *clip,
ListBase *tracksbase)
{
- MovieTrackingPlaneTrack *plane_track;
-
- for (plane_track = tracksbase->first; plane_track; plane_track = plane_track->next) {
+ LISTBASE_FOREACH (MovieTrackingPlaneTrack *, plane_track, tracksbase) {
BLO_read_id_address(reader, clip->id.lib, &plane_track->image);
}
}
@@ -8283,28 +7441,22 @@ static void lib_link_movieclip(BlendLibReader *reader, MovieClip *clip)
static void direct_link_mask(BlendDataReader *reader, Mask *mask)
{
- MaskLayer *masklay;
-
BLO_read_data_address(reader, &mask->adt);
BLO_read_list(reader, &mask->masklayers);
- for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
- MaskSpline *spline;
- MaskLayerShape *masklay_shape;
-
+ LISTBASE_FOREACH (MaskLayer *, masklay, &mask->masklayers) {
/* can't use newdataadr since it's a pointer within an array */
MaskSplinePoint *act_point_search = NULL;
BLO_read_list(reader, &masklay->splines);
- for (spline = masklay->splines.first; spline; spline = spline->next) {
+ LISTBASE_FOREACH (MaskSpline *, spline, &masklay->splines) {
MaskSplinePoint *points_old = spline->points;
- int i;
BLO_read_data_address(reader, &spline->points);
- for (i = 0; i < spline->tot_point; i++) {
+ for (int i = 0; i < spline->tot_point; i++) {
MaskSplinePoint *point = &spline->points[i];
if (point->tot_uw) {
@@ -8321,8 +7473,7 @@ static void direct_link_mask(BlendDataReader *reader, Mask *mask)
BLO_read_list(reader, &masklay->splines_shapes);
- for (masklay_shape = masklay->splines_shapes.first; masklay_shape;
- masklay_shape = masklay_shape->next) {
+ LISTBASE_FOREACH (MaskLayerShape *, masklay_shape, &masklay->splines_shapes) {
BLO_read_data_address(reader, &masklay_shape->data);
if (masklay_shape->tot_vert) {
@@ -8351,9 +7502,7 @@ static void lib_link_mask(BlendLibReader *reader, Mask *mask)
spline = masklay->splines.first;
while (spline) {
- int i;
-
- for (i = 0; i < spline->tot_point; i++) {
+ for (int i = 0; i < spline->tot_point; i++) {
MaskSplinePoint *point = &spline->points[i];
lib_link_mask_parent(reader, mask, &point->parent);
@@ -8374,9 +7523,7 @@ static void lib_link_mask(BlendLibReader *reader, Mask *mask)
static void lib_link_linestyle(BlendLibReader *reader, FreestyleLineStyle *linestyle)
{
- LineStyleModifier *m;
-
- for (m = linestyle->color_modifiers.first; m; m = m->next) {
+ LISTBASE_FOREACH (LineStyleModifier *, m, &linestyle->color_modifiers) {
switch (m->type) {
case LS_MODIFIER_DISTANCE_FROM_OBJECT: {
LineStyleColorModifier_DistanceFromObject *cm =
@@ -8386,7 +7533,7 @@ static void lib_link_linestyle(BlendLibReader *reader, FreestyleLineStyle *lines
}
}
}
- for (m = linestyle->alpha_modifiers.first; m; m = m->next) {
+ LISTBASE_FOREACH (LineStyleModifier *, m, &linestyle->alpha_modifiers) {
switch (m->type) {
case LS_MODIFIER_DISTANCE_FROM_OBJECT: {
LineStyleAlphaModifier_DistanceFromObject *am =
@@ -8396,7 +7543,7 @@ static void lib_link_linestyle(BlendLibReader *reader, FreestyleLineStyle *lines
}
}
}
- for (m = linestyle->thickness_modifiers.first; m; m = m->next) {
+ LISTBASE_FOREACH (LineStyleModifier *, m, &linestyle->thickness_modifiers) {
switch (m->type) {
case LS_MODIFIER_DISTANCE_FROM_OBJECT: {
LineStyleThicknessModifier_DistanceFromObject *tm =
@@ -8582,28 +7729,25 @@ static void direct_link_linestyle_geometry_modifier(BlendDataReader *UNUSED(read
static void direct_link_linestyle(BlendDataReader *reader, FreestyleLineStyle *linestyle)
{
- int a;
- LineStyleModifier *modifier;
-
BLO_read_data_address(reader, &linestyle->adt);
- direct_link_animdata(reader, linestyle->adt);
+ BKE_animdata_blend_read_data(reader, linestyle->adt);
BLO_read_list(reader, &linestyle->color_modifiers);
- for (modifier = linestyle->color_modifiers.first; modifier; modifier = modifier->next) {
+ LISTBASE_FOREACH (LineStyleModifier *, modifier, &linestyle->color_modifiers) {
direct_link_linestyle_color_modifier(reader, modifier);
}
BLO_read_list(reader, &linestyle->alpha_modifiers);
- for (modifier = linestyle->alpha_modifiers.first; modifier; modifier = modifier->next) {
+ LISTBASE_FOREACH (LineStyleModifier *, modifier, &linestyle->alpha_modifiers) {
direct_link_linestyle_alpha_modifier(reader, modifier);
}
BLO_read_list(reader, &linestyle->thickness_modifiers);
- for (modifier = linestyle->thickness_modifiers.first; modifier; modifier = modifier->next) {
+ LISTBASE_FOREACH (LineStyleModifier *, modifier, &linestyle->thickness_modifiers) {
direct_link_linestyle_thickness_modifier(reader, modifier);
}
BLO_read_list(reader, &linestyle->geometry_modifiers);
- for (modifier = linestyle->geometry_modifiers.first; modifier; modifier = modifier->next) {
+ LISTBASE_FOREACH (LineStyleModifier *, modifier, &linestyle->geometry_modifiers) {
direct_link_linestyle_geometry_modifier(reader, modifier);
}
- for (a = 0; a < MAX_MTEX; a++) {
+ for (int a = 0; a < MAX_MTEX; a++) {
BLO_read_data_address(reader, &linestyle->mtex[a]);
}
}
@@ -8624,11 +7768,11 @@ static void lib_link_hair(BlendLibReader *reader, Hair *hair)
static void direct_link_hair(BlendDataReader *reader, Hair *hair)
{
BLO_read_data_address(reader, &hair->adt);
- direct_link_animdata(reader, hair->adt);
+ BKE_animdata_blend_read_data(reader, hair->adt);
/* Geometry */
- direct_link_customdata(reader, &hair->pdata, hair->totpoint);
- direct_link_customdata(reader, &hair->cdata, hair->totcurve);
+ CustomData_blend_read(reader, &hair->pdata, hair->totpoint);
+ CustomData_blend_read(reader, &hair->cdata, hair->totcurve);
BKE_hair_update_customdata_pointers(hair);
/* Materials */
@@ -8651,10 +7795,10 @@ static void lib_link_pointcloud(BlendLibReader *reader, PointCloud *pointcloud)
static void direct_link_pointcloud(BlendDataReader *reader, PointCloud *pointcloud)
{
BLO_read_data_address(reader, &pointcloud->adt);
- direct_link_animdata(reader, pointcloud->adt);
+ BKE_animdata_blend_read_data(reader, pointcloud->adt);
/* Geometry */
- direct_link_customdata(reader, &pointcloud->pdata, pointcloud->totpoint);
+ CustomData_blend_read(reader, &pointcloud->pdata, pointcloud->totpoint);
BKE_pointcloud_update_customdata_pointers(pointcloud);
/* Materials */
@@ -8682,7 +7826,7 @@ static void lib_link_volume(BlendLibReader *reader, Volume *volume)
static void direct_link_volume(BlendDataReader *reader, Volume *volume)
{
BLO_read_data_address(reader, &volume->adt);
- direct_link_animdata(reader, volume->adt);
+ BKE_animdata_blend_read_data(reader, volume->adt);
volume->packedfile = direct_link_packedfile(reader, volume->packedfile);
volume->runtime.frame = 0;
@@ -8707,7 +7851,7 @@ static void lib_link_simulation(BlendLibReader *reader, Simulation *simulation)
static void direct_link_simulation(BlendDataReader *reader, Simulation *simulation)
{
BLO_read_data_address(reader, &simulation->adt);
- direct_link_animdata(reader, simulation->adt);
+ BKE_animdata_blend_read_data(reader, simulation->adt);
BLO_read_list(reader, &simulation->states);
LISTBASE_FOREACH (SimulationState *, state, &simulation->states) {
@@ -8715,7 +7859,7 @@ static void direct_link_simulation(BlendDataReader *reader, Simulation *simulati
BLO_read_data_address(reader, &state->type);
if (STREQ(state->type, SIM_TYPE_NAME_PARTICLE_SIMULATION)) {
ParticleSimulationState *particle_state = (ParticleSimulationState *)state;
- direct_link_customdata(reader, &particle_state->attributes, particle_state->tot_particles);
+ CustomData_blend_read(reader, &particle_state->attributes, particle_state->tot_particles);
}
}
@@ -8753,7 +7897,7 @@ 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?). */
- for (Object *ob = bmain->objects.first; ob != NULL; ob = ob->id.next) {
+ 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);
@@ -8862,6 +8006,9 @@ static bool direct_link_id(FileData *fd, Main *main, const int tag, ID *id, ID *
}
const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id);
+ if (id_type->blend_read_data != NULL) {
+ 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. */
@@ -8880,9 +8027,6 @@ static bool direct_link_id(FileData *fd, Main *main, const int tag, ID *id, ID *
case ID_OB:
direct_link_object(&reader, (Object *)id);
break;
- case ID_ME:
- direct_link_mesh(&reader, (Mesh *)id);
- break;
case ID_CU:
direct_link_curve(&reader, (Curve *)id);
break;
@@ -8913,9 +8057,6 @@ static bool direct_link_id(FileData *fd, Main *main, const int tag, ID *id, ID *
case ID_KE:
direct_link_key(&reader, (Key *)id);
break;
- case ID_LT:
- direct_link_latt(&reader, (Lattice *)id);
- break;
case ID_WO:
direct_link_world(&reader, (World *)id);
break;
@@ -8988,6 +8129,10 @@ static bool direct_link_id(FileData *fd, Main *main, const int tag, ID *id, ID *
case ID_SIM:
direct_link_simulation(&reader, (Simulation *)id);
break;
+ case ID_ME:
+ case ID_LT:
+ /* Do nothing. Handled by IDTypeInfo callback. */
+ break;
}
/* try to restore (when undoing) or clear ID's cache pointers. */
@@ -9464,11 +8609,9 @@ static void do_versions_userdef(FileData *fd, BlendFileData *bfd)
}
if (MAIN_VERSION_OLDER(bmain, 266, 4)) {
- bTheme *btheme;
-
/* Themes for Node and Sequence editor were not using grid color,
* but back. we copy this over then. */
- for (btheme = user->themes.first; btheme; btheme = btheme->next) {
+ LISTBASE_FOREACH (bTheme *, btheme, &user->themes) {
copy_v4_v4_uchar(btheme->space_node.grid, btheme->space_node.back);
copy_v4_v4_uchar(btheme->space_sequencer.grid, btheme->space_sequencer.back);
}
@@ -9583,6 +8726,11 @@ static void lib_link_all(FileData *fd, Main *bmain)
lib_link_id(&reader, id);
+ const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id);
+ if (id_type->blend_read_lib != NULL) {
+ id_type->blend_read_lib(&reader, id);
+ }
+
/* Note: ID types are processed in reverse order as defined by INDEX_ID_XXX enums in DNA_ID.h.
* This ensures handling of most dependencies in proper order, as elsewhere in code.
* Please keep order of entries in that switch matching that order, it's easier to quickly see
@@ -9648,18 +8796,12 @@ static void lib_link_all(FileData *fd, Main *bmain)
case ID_LA:
lib_link_light(&reader, (Light *)id);
break;
- case ID_LT:
- lib_link_latt(&reader, (Lattice *)id);
- break;
case ID_MB:
lib_link_mball(&reader, (MetaBall *)id);
break;
case ID_CU:
lib_link_curve(&reader, (Curve *)id);
break;
- case ID_ME:
- lib_link_mesh(&reader, (Mesh *)id);
- break;
case ID_CF:
lib_link_cachefiles(&reader, (CacheFile *)id);
break;
@@ -9713,6 +8855,10 @@ static void lib_link_all(FileData *fd, Main *bmain)
case ID_LI:
lib_link_library(&reader, (Library *)id); /* Only init users. */
break;
+ case ID_ME:
+ case ID_LT:
+ /* Do nothing. Handled by IDTypeInfo callback. */
+ break;
}
id->tag &= ~LIB_TAG_NEED_LINK;
@@ -9749,7 +8895,7 @@ static void lib_link_all(FileData *fd, Main *bmain)
static void direct_link_keymapitem(BlendDataReader *reader, wmKeyMapItem *kmi)
{
BLO_read_data_address(reader, &kmi->properties);
- IDP_DirectLinkGroup_OrFree(&kmi->properties, reader);
+ IDP_BlendDataRead(reader, &kmi->properties);
kmi->ptr = NULL;
kmi->flag &= ~KMI_UPDATE;
}
@@ -9757,11 +8903,6 @@ static void direct_link_keymapitem(BlendDataReader *reader, wmKeyMapItem *kmi)
static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead)
{
UserDef *user;
- wmKeyMap *keymap;
- wmKeyMapItem *kmi;
- wmKeyMapDiffItem *kmdi;
- bAddon *addon;
-
bfd->user = user = read_struct(fd, bhead, "user def");
/* User struct has separate do-version handling */
@@ -9781,7 +8922,7 @@ static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead)
BLO_read_list(reader, &user->addons);
BLO_read_list(reader, &user->autoexec_paths);
- for (keymap = user->user_keymaps.first; keymap; keymap = keymap->next) {
+ LISTBASE_FOREACH (wmKeyMap *, keymap, &user->user_keymaps) {
keymap->modal_items = NULL;
keymap->poll = NULL;
keymap->flag &= ~KEYMAP_UPDATE;
@@ -9789,7 +8930,7 @@ static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead)
BLO_read_list(reader, &keymap->diff_items);
BLO_read_list(reader, &keymap->items);
- for (kmdi = keymap->diff_items.first; kmdi; kmdi = kmdi->next) {
+ LISTBASE_FOREACH (wmKeyMapDiffItem *, kmdi, &keymap->diff_items) {
BLO_read_data_address(reader, &kmdi->remove_item);
BLO_read_data_address(reader, &kmdi->add_item);
@@ -9801,14 +8942,14 @@ static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead)
}
}
- for (kmi = keymap->items.first; kmi; kmi = kmi->next) {
+ 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_DirectLinkGroup_OrFree(&kpt->prop, reader);
+ IDP_BlendDataRead(reader, &kpt->prop);
}
LISTBASE_FOREACH (bUserMenu *, um, &user->user_menus) {
@@ -9817,14 +8958,14 @@ static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead)
if (umi->type == USER_MENU_TYPE_OPERATOR) {
bUserMenuItem_Op *umi_op = (bUserMenuItem_Op *)umi;
BLO_read_data_address(reader, &umi_op->prop);
- IDP_DirectLinkGroup_OrFree(&umi_op->prop, reader);
+ IDP_BlendDataRead(reader, &umi_op->prop);
}
}
}
- for (addon = user->addons.first; addon; addon = addon->next) {
+ LISTBASE_FOREACH (bAddon *, addon, &user->addons) {
BLO_read_data_address(reader, &addon->prop);
- IDP_DirectLinkGroup_OrFree(&addon->prop, reader);
+ IDP_BlendDataRead(reader, &addon->prop);
}
// XXX
@@ -10283,8 +9424,7 @@ static BLOExpandDoitCallback expand_doit;
// XXX deprecated - old animation system
static void expand_ipo(BlendExpander *expander, Ipo *ipo)
{
- IpoCurve *icu;
- for (icu = ipo->curve.first; icu; icu = icu->next) {
+ LISTBASE_FOREACH (IpoCurve *, icu, &ipo->curve) {
if (icu->driver) {
BLO_expand(expander, icu->driver->ob);
}
@@ -10294,115 +9434,11 @@ static void expand_ipo(BlendExpander *expander, Ipo *ipo)
// XXX deprecated - old animation system
static void expand_constraint_channels(BlendExpander *expander, ListBase *chanbase)
{
- bConstraintChannel *chan;
- for (chan = chanbase->first; chan; chan = chan->next) {
+ LISTBASE_FOREACH (bConstraintChannel *, chan, chanbase) {
BLO_expand(expander, chan->ipo);
}
}
-static void expand_fmodifiers(BlendExpander *expander, ListBase *list)
-{
- FModifier *fcm;
-
- for (fcm = list->first; fcm; fcm = fcm->next) {
- /* library data for specific F-Modifier types */
- switch (fcm->type) {
- case FMODIFIER_TYPE_PYTHON: {
- FMod_Python *data = (FMod_Python *)fcm->data;
-
- BLO_expand(expander, data->script);
-
- break;
- }
- }
- }
-}
-
-static void expand_fcurves(BlendExpander *expander, ListBase *list)
-{
- FCurve *fcu;
-
- for (fcu = list->first; fcu; fcu = fcu->next) {
- /* Driver targets if there is a driver */
- if (fcu->driver) {
- ChannelDriver *driver = fcu->driver;
- DriverVar *dvar;
-
- for (dvar = driver->variables.first; dvar; dvar = dvar->next) {
- DRIVER_TARGETS_LOOPER_BEGIN (dvar) {
- // TODO: only expand those that are going to get used?
- BLO_expand(expander, dtar->id);
- }
- DRIVER_TARGETS_LOOPER_END;
- }
- }
-
- /* F-Curve Modifiers */
- expand_fmodifiers(expander, &fcu->modifiers);
- }
-}
-
-static void expand_animdata_nlastrips(BlendExpander *expander, ListBase *list)
-{
- NlaStrip *strip;
-
- for (strip = list->first; strip; strip = strip->next) {
- /* check child strips */
- expand_animdata_nlastrips(expander, &strip->strips);
-
- /* check F-Curves */
- expand_fcurves(expander, &strip->fcurves);
-
- /* check F-Modifiers */
- expand_fmodifiers(expander, &strip->modifiers);
-
- /* relink referenced action */
- BLO_expand(expander, strip->act);
- }
-}
-
-static void expand_animdata(BlendExpander *expander, AnimData *adt)
-{
- NlaTrack *nlt;
-
- /* own action */
- BLO_expand(expander, adt->action);
- BLO_expand(expander, adt->tmpact);
-
- /* drivers - assume that these F-Curves have driver data to be in this list... */
- expand_fcurves(expander, &adt->drivers);
-
- /* nla-data - referenced actions */
- for (nlt = adt->nla_tracks.first; nlt; nlt = nlt->next) {
- expand_animdata_nlastrips(expander, &nlt->strips);
- }
-}
-
-static void expand_idprops(BlendExpander *expander, IDProperty *prop)
-{
- if (!prop) {
- return;
- }
-
- switch (prop->type) {
- case IDP_ID:
- BLO_expand(expander, IDP_Id(prop));
- break;
- case IDP_IDPARRAY: {
- IDProperty *idp_array = IDP_IDPArray(prop);
- for (int i = 0; i < prop->len; i++) {
- expand_idprops(expander, &idp_array[i]);
- }
- break;
- }
- case IDP_GROUP:
- LISTBASE_FOREACH (IDProperty *, loop, &prop->data.group) {
- expand_idprops(expander, loop);
- }
- break;
- }
-}
-
static void expand_id(BlendExpander *expander, ID *id);
static void expand_nodetree(BlendExpander *expander, bNodeTree *ntree);
static void expand_collection(BlendExpander *expander, Collection *collection);
@@ -10427,7 +9463,7 @@ static void expand_id_embedded_id(BlendExpander *expander, ID *id)
static void expand_id(BlendExpander *expander, ID *id)
{
- expand_idprops(expander, id->properties);
+ IDP_BlendReadExpand(expander, id->properties);
if (id->override_library) {
BLO_expand(expander, id->override_library->reference);
@@ -10436,7 +9472,7 @@ static void expand_id(BlendExpander *expander, ID *id)
AnimData *adt = BKE_animdata_from_id(id);
if (adt != NULL) {
- expand_animdata(expander, adt);
+ BKE_animdata_blend_read_expand(expander, adt);
}
expand_id_embedded_id(expander, id);
@@ -10444,17 +9480,15 @@ static void expand_id(BlendExpander *expander, ID *id)
static void expand_action(BlendExpander *expander, bAction *act)
{
- bActionChannel *chan;
-
// XXX deprecated - old animation system --------------
- for (chan = act->chanbase.first; chan; chan = chan->next) {
+ LISTBASE_FOREACH (bActionChannel *, chan, &act->chanbase) {
BLO_expand(expander, chan->ipo);
expand_constraint_channels(expander, &chan->constraintChannels);
}
// ---------------------------------------------------
/* F-Curves in Action */
- expand_fcurves(expander, &act->curves);
+ BKE_fcurve_blend_read_expand(expander, &act->curves);
LISTBASE_FOREACH (TimeMarker *, marker, &act->markers) {
if (marker->camera) {
@@ -10465,12 +9499,9 @@ static void expand_action(BlendExpander *expander, bAction *act)
static void expand_keyingsets(BlendExpander *expander, ListBase *list)
{
- KeyingSet *ks;
- KS_Path *ksp;
-
/* expand the ID-pointers in KeyingSets's paths */
- for (ks = list->first; ks; ks = ks->next) {
- for (ksp = ks->paths.first; ksp; ksp = ksp->next) {
+ LISTBASE_FOREACH (KeyingSet *, ks, list) {
+ LISTBASE_FOREACH (KS_Path *, ksp, &ks->paths) {
BLO_expand(expander, ksp->id);
}
}
@@ -10478,15 +9509,13 @@ static void expand_keyingsets(BlendExpander *expander, ListBase *list)
static void expand_particlesettings(BlendExpander *expander, ParticleSettings *part)
{
- int a;
-
BLO_expand(expander, part->instance_object);
BLO_expand(expander, part->instance_collection);
BLO_expand(expander, part->force_group);
BLO_expand(expander, part->bb_ob);
BLO_expand(expander, part->collision_group);
- for (a = 0; a < MAX_MTEX; a++) {
+ for (int a = 0; a < MAX_MTEX; a++) {
if (part->mtex[a]) {
BLO_expand(expander, part->mtex[a]->tex);
BLO_expand(expander, part->mtex[a]->object);
@@ -10507,11 +9536,8 @@ static void expand_particlesettings(BlendExpander *expander, ParticleSettings *p
}
if (part->boids) {
- BoidState *state;
- BoidRule *rule;
-
- for (state = part->boids->states.first; state; state = state->next) {
- for (rule = state->rules.first; rule; rule = rule->next) {
+ LISTBASE_FOREACH (BoidState *, state, &part->boids->states) {
+ LISTBASE_FOREACH (BoidRule *, rule, &state->rules) {
if (rule->type == eBoidRuleType_Avoid) {
BoidRuleGoalAvoid *gabr = (BoidRuleGoalAvoid *)rule;
BLO_expand(expander, gabr->ob);
@@ -10553,7 +9579,7 @@ static void expand_key(BlendExpander *expander, Key *key)
static void expand_node_socket(BlendExpander *expander, bNodeSocket *sock)
{
- expand_idprops(expander, sock->prop);
+ IDP_BlendReadExpand(expander, sock->prop);
if (sock->default_value != NULL) {
@@ -10595,18 +9621,16 @@ static void expand_node_sockets(BlendExpander *expander, ListBase *sockets)
static void expand_nodetree(BlendExpander *expander, bNodeTree *ntree)
{
- bNode *node;
-
if (ntree->gpd) {
BLO_expand(expander, ntree->gpd);
}
- for (node = ntree->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->id && node->type != CMP_NODE_R_LAYERS) {
BLO_expand(expander, node->id);
}
- expand_idprops(expander, node->prop);
+ IDP_BlendReadExpand(expander, node->prop);
expand_node_sockets(expander, &node->inputs);
expand_node_sockets(expander, &node->outputs);
@@ -10649,12 +9673,6 @@ static void expand_light(BlendExpander *expander, Light *la)
BLO_expand(expander, la->ipo); // XXX deprecated - old animation system
}
-static void expand_lattice(BlendExpander *expander, Lattice *lt)
-{
- BLO_expand(expander, lt->ipo); // XXX deprecated - old animation system
- BLO_expand(expander, lt->key);
-}
-
static void expand_world(BlendExpander *expander, World *wrld)
{
BLO_expand(expander, wrld->ipo); // XXX deprecated - old animation system
@@ -10662,18 +9680,14 @@ static void expand_world(BlendExpander *expander, World *wrld)
static void expand_mball(BlendExpander *expander, MetaBall *mb)
{
- int a;
-
- for (a = 0; a < mb->totcol; a++) {
+ for (int a = 0; a < mb->totcol; a++) {
BLO_expand(expander, mb->mat[a]);
}
}
static void expand_curve(BlendExpander *expander, Curve *cu)
{
- int a;
-
- for (a = 0; a < cu->totcol; a++) {
+ for (int a = 0; a < cu->totcol; a++) {
BLO_expand(expander, cu->mat[a]);
}
@@ -10688,18 +9702,6 @@ static void expand_curve(BlendExpander *expander, Curve *cu)
BLO_expand(expander, cu->textoncurve);
}
-static void expand_mesh(BlendExpander *expander, Mesh *me)
-{
- int a;
-
- for (a = 0; a < me->totcol; a++) {
- BLO_expand(expander, me->mat[a]);
- }
-
- BLO_expand(expander, me->key);
- BLO_expand(expander, me->texcomesh);
-}
-
/* callback function used to expand constraint ID-links */
static void expand_constraint_cb(bConstraint *UNUSED(con),
ID **idpoin,
@@ -10712,12 +9714,10 @@ static void expand_constraint_cb(bConstraint *UNUSED(con),
static void expand_constraints(BlendExpander *expander, ListBase *lb)
{
- bConstraint *curcon;
-
BKE_constraints_id_loop(lb, expand_constraint_cb, expander);
/* deprecated manual expansion stuff */
- for (curcon = lb->first; curcon; curcon = curcon->next) {
+ LISTBASE_FOREACH (bConstraint *, curcon, lb) {
if (curcon->ipo) {
BLO_expand(expander, curcon->ipo); // XXX deprecated - old animation system
}
@@ -10726,22 +9726,20 @@ static void expand_constraints(BlendExpander *expander, ListBase *lb)
static void expand_pose(BlendExpander *expander, bPose *pose)
{
- bPoseChannel *chan;
-
if (!pose) {
return;
}
- for (chan = pose->chanbase.first; chan; chan = chan->next) {
+ LISTBASE_FOREACH (bPoseChannel *, chan, &pose->chanbase) {
expand_constraints(expander, &chan->constraints);
- expand_idprops(expander, chan->prop);
+ IDP_BlendReadExpand(expander, chan->prop);
BLO_expand(expander, chan->custom);
}
}
static void expand_bones(BlendExpander *expander, Bone *bone)
{
- expand_idprops(expander, bone->prop);
+ IDP_BlendReadExpand(expander, bone->prop);
LISTBASE_FOREACH (Bone *, curBone, &bone->childbase) {
expand_bones(expander, curBone);
@@ -10766,11 +9764,6 @@ static void expand_object_expandModifiers(void *userData,
static void expand_object(BlendExpander *expander, Object *ob)
{
- ParticleSystem *psys;
- bActionStrip *strip;
- PartEff *paf;
- int a;
-
BLO_expand(expander, ob->data);
/* expand_object_expandModifier() */
@@ -10800,18 +9793,18 @@ static void expand_object(BlendExpander *expander, Object *ob)
expand_constraint_channels(expander, &ob->constraintChannels);
- for (strip = ob->nlastrips.first; strip; strip = strip->next) {
+ LISTBASE_FOREACH (bActionStrip *, strip, &ob->nlastrips) {
BLO_expand(expander, strip->object);
BLO_expand(expander, strip->act);
BLO_expand(expander, strip->ipo);
}
// XXX deprecated - old animation system (for version patching only)
- for (a = 0; a < ob->totcol; a++) {
+ for (int a = 0; a < ob->totcol; a++) {
BLO_expand(expander, ob->mat[a]);
}
- paf = blo_do_version_give_parteff_245(ob);
+ PartEff *paf = blo_do_version_give_parteff_245(ob);
if (paf && paf->group) {
BLO_expand(expander, paf->group);
}
@@ -10827,7 +9820,7 @@ static void expand_object(BlendExpander *expander, Object *ob)
BLO_expand(expander, ob->proxy_group);
}
- for (psys = ob->particlesystem.first; psys; psys = psys->next) {
+ LISTBASE_FOREACH (ParticleSystem *, psys, &ob->particlesystem) {
BLO_expand(expander, psys->part);
}
@@ -10848,13 +9841,6 @@ static void expand_object(BlendExpander *expander, Object *ob)
BLO_expand(expander, ob->rigidbody_constraint->ob1);
BLO_expand(expander, ob->rigidbody_constraint->ob2);
}
-
- if (ob->currentlod) {
- LodLevel *level;
- for (level = ob->lodlevels.first; level; level = level->next) {
- BLO_expand(expander, level->source);
- }
- }
}
#ifdef USE_COLLECTION_COMPAT_28
@@ -10872,10 +9858,6 @@ static void expand_scene_collection(BlendExpander *expander, SceneCollection *sc
static void expand_scene(BlendExpander *expander, Scene *sce)
{
- SceneRenderLayer *srl;
- FreestyleModuleConfig *module;
- FreestyleLineSet *lineset;
-
LISTBASE_FOREACH (Base *, base_legacy, &sce->base) {
BLO_expand(expander, base_legacy->object);
}
@@ -10888,14 +9870,14 @@ static void expand_scene(BlendExpander *expander, Scene *sce)
BLO_expand(expander, sce->set);
}
- for (srl = sce->r.layers.first; srl; srl = srl->next) {
+ LISTBASE_FOREACH (SceneRenderLayer *, srl, &sce->r.layers) {
BLO_expand(expander, srl->mat_override);
- for (module = srl->freestyleConfig.modules.first; module; module = module->next) {
+ LISTBASE_FOREACH (FreestyleModuleConfig *, module, &srl->freestyleConfig.modules) {
if (module->script) {
BLO_expand(expander, module->script);
}
}
- for (lineset = srl->freestyleConfig.linesets.first; lineset; lineset = lineset->next) {
+ LISTBASE_FOREACH (FreestyleLineSet *, lineset, &srl->freestyleConfig.linesets) {
if (lineset->group) {
BLO_expand(expander, lineset->group);
}
@@ -10904,15 +9886,15 @@ static void expand_scene(BlendExpander *expander, Scene *sce)
}
LISTBASE_FOREACH (ViewLayer *, view_layer, &sce->view_layers) {
- expand_idprops(expander, view_layer->id_properties);
+ IDP_BlendReadExpand(expander, view_layer->id_properties);
- for (module = view_layer->freestyle_config.modules.first; module; module = module->next) {
+ LISTBASE_FOREACH (FreestyleModuleConfig *, module, &view_layer->freestyle_config.modules) {
if (module->script) {
BLO_expand(expander, module->script);
}
}
- for (lineset = view_layer->freestyle_config.linesets.first; lineset; lineset = lineset->next) {
+ LISTBASE_FOREACH (FreestyleLineSet *, lineset, &view_layer->freestyle_config.linesets) {
if (lineset->group) {
BLO_expand(expander, lineset->group);
}
@@ -10927,8 +9909,8 @@ static void expand_scene(BlendExpander *expander, Scene *sce)
if (sce->ed) {
Sequence *seq;
- SEQ_BEGIN (sce->ed, seq) {
- expand_idprops(expander, seq->prop);
+ SEQ_ALL_BEGIN (sce->ed, seq) {
+ IDP_BlendReadExpand(expander, seq->prop);
if (seq->scene) {
BLO_expand(expander, seq->scene);
@@ -10951,7 +9933,7 @@ static void expand_scene(BlendExpander *expander, Scene *sce)
BLO_expand(expander, data->text_font);
}
}
- SEQ_END;
+ SEQ_ALL_END;
}
if (sce->rigidbody_world) {
@@ -11023,17 +10005,10 @@ static void expand_mask_parent(BlendExpander *expander, MaskParent *parent)
static void expand_mask(BlendExpander *expander, Mask *mask)
{
- MaskLayer *mask_layer;
-
- for (mask_layer = mask->masklayers.first; mask_layer; mask_layer = mask_layer->next) {
- MaskSpline *spline;
-
- for (spline = mask_layer->splines.first; spline; spline = spline->next) {
- int i;
-
- for (i = 0; i < spline->tot_point; i++) {
+ LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) {
+ LISTBASE_FOREACH (MaskSpline *, spline, &mask_layer->splines) {
+ for (int i = 0; i < spline->tot_point; i++) {
MaskSplinePoint *point = &spline->points[i];
-
expand_mask_parent(expander, &point->parent);
}
@@ -11044,27 +10019,24 @@ static void expand_mask(BlendExpander *expander, Mask *mask)
static void expand_linestyle(BlendExpander *expander, FreestyleLineStyle *linestyle)
{
- int a;
- LineStyleModifier *m;
-
- for (a = 0; a < MAX_MTEX; a++) {
+ for (int a = 0; a < MAX_MTEX; a++) {
if (linestyle->mtex[a]) {
BLO_expand(expander, linestyle->mtex[a]->tex);
BLO_expand(expander, linestyle->mtex[a]->object);
}
}
- for (m = linestyle->color_modifiers.first; m; m = m->next) {
+ LISTBASE_FOREACH (LineStyleModifier *, m, &linestyle->color_modifiers) {
if (m->type == LS_MODIFIER_DISTANCE_FROM_OBJECT) {
BLO_expand(expander, ((LineStyleColorModifier_DistanceFromObject *)m)->target);
}
}
- for (m = linestyle->alpha_modifiers.first; m; m = m->next) {
+ LISTBASE_FOREACH (LineStyleModifier *, m, &linestyle->alpha_modifiers) {
if (m->type == LS_MODIFIER_DISTANCE_FROM_OBJECT) {
BLO_expand(expander, ((LineStyleAlphaModifier_DistanceFromObject *)m)->target);
}
}
- for (m = linestyle->thickness_modifiers.first; m; m = m->next) {
+ LISTBASE_FOREACH (LineStyleModifier *, m, &linestyle->thickness_modifiers) {
if (m->type == LS_MODIFIER_DISTANCE_FROM_OBJECT) {
BLO_expand(expander, ((LineStyleThicknessModifier_DistanceFromObject *)m)->target);
}
@@ -11094,10 +10066,6 @@ static void expand_hair(BlendExpander *expander, Hair *hair)
for (int a = 0; a < hair->totcol; a++) {
BLO_expand(expander, hair->mat[a]);
}
-
- if (hair->adt) {
- expand_animdata(expander, hair->adt);
- }
}
static void expand_pointcloud(BlendExpander *expander, PointCloud *pointcloud)
@@ -11105,10 +10073,6 @@ static void expand_pointcloud(BlendExpander *expander, PointCloud *pointcloud)
for (int a = 0; a < pointcloud->totcol; a++) {
BLO_expand(expander, pointcloud->mat[a]);
}
-
- if (pointcloud->adt) {
- expand_animdata(expander, pointcloud->adt);
- }
}
static void expand_volume(BlendExpander *expander, Volume *volume)
@@ -11116,17 +10080,10 @@ static void expand_volume(BlendExpander *expander, Volume *volume)
for (int a = 0; a < volume->totcol; a++) {
BLO_expand(expander, volume->mat[a]);
}
-
- if (volume->adt) {
- expand_animdata(expander, volume->adt);
- }
}
static void expand_simulation(BlendExpander *expander, Simulation *simulation)
{
- if (simulation->adt) {
- expand_animdata(expander, simulation->adt);
- }
LISTBASE_FOREACH (SimulationDependency *, dependency, &simulation->dependencies) {
BLO_expand(expander, dependency->id);
}
@@ -11169,13 +10126,15 @@ void BLO_expand_main(void *fdhandle, Main *mainvar)
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);
+ }
+
switch (GS(id->name)) {
case ID_OB:
expand_object(&expander, (Object *)id);
break;
- case ID_ME:
- expand_mesh(&expander, (Mesh *)id);
- break;
case ID_CU:
expand_curve(&expander, (Curve *)id);
break;
@@ -11194,9 +10153,6 @@ void BLO_expand_main(void *fdhandle, Main *mainvar)
case ID_WO:
expand_world(&expander, (World *)id);
break;
- case ID_LT:
- expand_lattice(&expander, (Lattice *)id);
- break;
case ID_LA:
expand_light(&expander, (Light *)id);
break;
@@ -11287,9 +10243,7 @@ void BLO_expand_main(void *fdhandle, Main *mainvar)
static bool object_in_any_scene(Main *bmain, Object *ob)
{
- Scene *sce;
-
- for (sce = bmain->scenes.first; sce; sce = sce->id.next) {
+ LISTBASE_FOREACH (Scene *, sce, &bmain->scenes) {
if (BKE_scene_object_find(sce, ob)) {
return true;
}
@@ -11300,9 +10254,7 @@ static bool object_in_any_scene(Main *bmain, Object *ob)
static bool object_in_any_collection(Main *bmain, Object *ob)
{
- Collection *collection;
-
- for (collection = bmain->collections.first; collection; collection = collection->id.next) {
+ LISTBASE_FOREACH (Collection *, collection, &bmain->collections) {
if (BKE_collection_has_object(collection, ob)) {
return true;
}
@@ -11326,7 +10278,7 @@ static void add_loose_objects_to_scene(Main *mainvar,
/* Give all objects which are LIB_TAG_INDIRECT a base,
* or for a collection when *lib has been set. */
- for (Object *ob = mainvar->objects.first; ob; ob = ob->id.next) {
+ LISTBASE_FOREACH (Object *, ob, &mainvar->objects) {
bool do_it = (ob->id.tag & LIB_TAG_DOIT) != 0;
if (do_it || ((ob->id.tag & LIB_TAG_INDIRECT) && (ob->id.tag & LIB_TAG_PRE_EXISTING) == 0)) {
if (do_append) {
@@ -11393,8 +10345,7 @@ static void add_collections_to_scene(Main *mainvar,
}
/* Give all objects which are tagged a base. */
- for (Collection *collection = mainvar->collections.first; collection;
- collection = collection->id.next) {
+ LISTBASE_FOREACH (Collection *, collection, &mainvar->collections) {
if ((flag & FILE_GROUP_INSTANCE) && (collection->id.tag & LIB_TAG_DOIT)) {
/* Any indirect collection should not have been tagged. */
BLI_assert((collection->id.tag & LIB_TAG_INDIRECT) == 0);
@@ -11437,8 +10388,7 @@ static void add_collections_to_scene(Main *mainvar,
* Note that we only check object directly into that collection,
* not recursively into its children.
*/
- for (CollectionObject *coll_ob = collection->gobject.first; coll_ob != NULL;
- coll_ob = coll_ob->next) {
+ LISTBASE_FOREACH (CollectionObject *, coll_ob, &collection->gobject) {
Object *ob = coll_ob->ob;
if ((ob->id.tag & (LIB_TAG_PRE_EXISTING | LIB_TAG_DOIT | LIB_TAG_INDIRECT)) == 0 &&
(ob->id.lib == lib) && (object_in_any_scene(bmain, ob) == 0)) {
@@ -11452,8 +10402,7 @@ static void add_collections_to_scene(Main *mainvar,
BKE_collection_child_add(bmain, active_collection, collection);
if (flag & FILE_AUTOSELECT) {
- for (CollectionObject *coll_ob = collection->gobject.first; coll_ob != NULL;
- coll_ob = coll_ob->next) {
+ LISTBASE_FOREACH (CollectionObject *, coll_ob, &collection->gobject) {
Object *ob = coll_ob->ob;
Base *base = BKE_view_layer_base_find(view_layer, ob);
if (base) {
@@ -11679,9 +10628,7 @@ static void split_main_newid(Main *mainptr, Main *main_newid)
while (i--) {
BLI_listbase_clear(lbarray_newid[i]);
- for (ID *id = lbarray[i]->first, *idnext; id; id = idnext) {
- idnext = id->next;
-
+ LISTBASE_FOREACH_MUTABLE (ID *, id, lbarray[i]) {
if (id->tag & LIB_TAG_NEW) {
BLI_remlink(lbarray[i], id);
BLI_addtail(lbarray_newid[i], id);
@@ -12127,8 +11074,8 @@ static void read_libraries(FileData *basefd, ListBase *mainlist)
}
/* 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
+ * 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. */
@@ -12145,6 +11092,11 @@ void *BLO_read_get_new_data_address(BlendDataReader *reader, const void *old_add
return newdataadr(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);
@@ -12300,6 +11252,16 @@ void BLO_read_pointer_array(BlendDataReader *reader, void **ptr_p)
*ptr_p = final_array;
}
+bool BLO_read_data_is_undo(BlendDataReader *reader)
+{
+ return reader->fd->memfile != NULL;
+}
+
+bool BLO_read_lib_is_undo(BlendLibReader *reader)
+{
+ return reader->fd->memfile != NULL;
+}
+
void BLO_expand_id(BlendExpander *expander, ID *id)
{
expand_doit(expander->fd, expander->main, id);
diff --git a/source/blender/blenloader/intern/versioning_250.c b/source/blender/blenloader/intern/versioning_250.c
index 0e753c84476..dad86f80813 100644
--- a/source/blender/blenloader/intern/versioning_250.c
+++ b/source/blender/blenloader/intern/versioning_250.c
@@ -666,7 +666,7 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain)
for (scene = bmain->scenes.first; scene; scene = scene->id.next) {
if (scene->ed && scene->ed->seqbasep) {
- SEQ_BEGIN (scene->ed, seq) {
+ SEQ_ALL_BEGIN (scene->ed, seq) {
if (seq->type == SEQ_TYPE_SOUND_HD) {
char str[FILE_MAX];
BLI_join_dirfile(str, sizeof(str), seq->strip->dir, seq->strip->stripdata->name);
@@ -682,7 +682,7 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain)
#undef SEQ_USE_PROXY_CUSTOM_DIR
#undef SEQ_USE_PROXY_CUSTOM_FILE
}
- SEQ_END;
+ SEQ_ALL_END;
}
}
@@ -1409,10 +1409,10 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain)
sce->r.ffcodecdata.audio_codec = 0x0; // CODEC_ID_NONE
}
- SEQ_BEGIN (sce->ed, seq) {
+ SEQ_ALL_BEGIN (sce->ed, seq) {
seq->volume = 1.0f;
}
- SEQ_END;
+ SEQ_ALL_END;
}
/* particle brush strength factor was changed from int to float */
@@ -1681,12 +1681,12 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain)
for (scene = bmain->scenes.first; scene; scene = scene->id.next) {
Sequence *seq;
- SEQ_BEGIN (scene->ed, seq) {
+ SEQ_ALL_BEGIN (scene->ed, seq) {
if (seq->sat == 0.0f) {
seq->sat = 1.0f;
}
}
- SEQ_END;
+ SEQ_ALL_END;
}
/* GSOC 2010 Sculpt - New settings for Brush */
@@ -2166,10 +2166,10 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain)
for (scene = bmain->scenes.first; scene; scene = scene->id.next) {
scene->r.ffcodecdata.audio_channels = 2;
scene->audio.volume = 1.0f;
- SEQ_BEGIN (scene->ed, seq) {
+ SEQ_ALL_BEGIN (scene->ed, seq) {
seq->pitch = 1.0f;
}
- SEQ_END;
+ SEQ_ALL_END;
}
}
diff --git a/source/blender/blenloader/intern/versioning_260.c b/source/blender/blenloader/intern/versioning_260.c
index 1ac90398c0a..dec5c8d495a 100644
--- a/source/blender/blenloader/intern/versioning_260.c
+++ b/source/blender/blenloader/intern/versioning_260.c
@@ -1489,7 +1489,7 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain)
if (scene->ed) {
Sequence *seq;
- SEQ_BEGIN (scene->ed, seq) {
+ SEQ_ALL_BEGIN (scene->ed, seq) {
Strip *strip = seq->strip;
if (strip && strip->color_balance) {
@@ -1512,7 +1512,7 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain)
strip->color_balance = NULL;
}
}
- SEQ_END;
+ SEQ_ALL_END;
}
}
}
@@ -1804,7 +1804,7 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain)
for (scene = bmain->scenes.first; scene; scene = scene->id.next) {
Sequence *seq;
- SEQ_BEGIN (scene->ed, seq) {
+ SEQ_ALL_BEGIN (scene->ed, seq) {
enum { SEQ_MAKE_PREMUL = (1 << 6) };
if (seq->flag & SEQ_MAKE_PREMUL) {
seq->alpha_mode = SEQ_ALPHA_STRAIGHT;
@@ -1813,7 +1813,7 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain)
BKE_sequence_alpha_mode_from_extension(seq);
}
}
- SEQ_END;
+ SEQ_ALL_END;
if (scene->r.bake_samples == 0) {
scene->r.bake_samples = 256;
@@ -2447,13 +2447,13 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain)
for (scene = bmain->scenes.first; scene; scene = scene->id.next) {
Sequence *seq;
- SEQ_BEGIN (scene->ed, seq) {
+ SEQ_ALL_BEGIN (scene->ed, seq) {
if (seq->type == SEQ_TYPE_WIPE) {
WipeVars *wv = seq->effectdata;
wv->angle = DEG2RADF(wv->angle);
}
}
- SEQ_END;
+ SEQ_ALL_END;
}
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c
index b19c6221391..2452ac43bd4 100644
--- a/source/blender/blenloader/intern/versioning_270.c
+++ b/source/blender/blenloader/intern/versioning_270.c
@@ -891,17 +891,6 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
}
-
- /* hysteresis set to 10% but not activated */
- if (!DNA_struct_elem_find(fd->filesdna, "LodLevel", "int", "obhysteresis")) {
- Object *ob;
- for (ob = bmain->objects.first; ob; ob = ob->id.next) {
- LodLevel *level;
- for (level = ob->lodlevels.first; level; level = level->next) {
- level->obhysteresis = 10;
- }
- }
- }
}
if (!MAIN_VERSION_ATLEAST(bmain, 274, 4)) {
@@ -924,7 +913,7 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *bmain)
srv = scene->r.views.last;
BLI_strncpy(srv->suffix, STEREO_RIGHT_SUFFIX, sizeof(srv->suffix));
- SEQ_BEGIN (scene->ed, seq) {
+ SEQ_ALL_BEGIN (scene->ed, seq) {
seq->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Stereo Display 3d Format");
#define SEQ_USE_PROXY_CUSTOM_DIR (1 << 19)
@@ -940,7 +929,7 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *bmain)
#undef SEQ_USE_PROXY_CUSTOM_DIR
#undef SEQ_USE_PROXY_CUSTOM_FILE
}
- SEQ_END;
+ SEQ_ALL_END;
}
for (screen = bmain->screens.first; screen; screen = screen->id.next) {
@@ -1225,7 +1214,7 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *bmain)
for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) {
Sequence *seq;
- SEQ_BEGIN (scene->ed, seq) {
+ SEQ_ALL_BEGIN (scene->ed, seq) {
if (seq->type != SEQ_TYPE_TEXT) {
continue;
}
@@ -1241,7 +1230,7 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *bmain)
data->shadow_color[3] = 1.0f;
}
}
- SEQ_END;
+ SEQ_ALL_END;
}
/* Adding "Properties" region to DopeSheet */
diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c
index 0d8f2eac99e..2434d9e9f74 100644
--- a/source/blender/blenloader/intern/versioning_280.c
+++ b/source/blender/blenloader/intern/versioning_280.c
@@ -3463,7 +3463,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
if (scene->ed) {
Sequence *seq;
- SEQ_BEGIN (scene->ed, seq) {
+ SEQ_ALL_BEGIN (scene->ed, seq) {
seq->flag &= ~(SEQ_FLAG_UNUSED_6 | SEQ_FLAG_UNUSED_18 | SEQ_FLAG_UNUSED_19 |
SEQ_FLAG_UNUSED_21);
if (seq->type == SEQ_TYPE_SPEED) {
@@ -3471,7 +3471,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
s->flags &= ~(SEQ_SPEED_UNUSED_1);
}
}
- SEQ_END;
+ SEQ_ALL_END;
}
}
diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c
index bc13e3b3a39..aa222616e4c 100644
--- a/source/blender/blenloader/intern/versioning_290.c
+++ b/source/blender/blenloader/intern/versioning_290.c
@@ -42,6 +42,7 @@
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_node.h"
+#include "BKE_softbody.h"
#include "BLO_readfile.h"
#include "readfile.h"
@@ -208,6 +209,17 @@ void do_versions_after_linking_290(Main *bmain, ReportList *UNUSED(reports))
}
}
+ if (!MAIN_VERSION_ATLEAST(bmain, 291, 1)) {
+ LISTBASE_FOREACH (Collection *, collection, &bmain->collections) {
+ if (BKE_collection_cycles_fix(bmain, collection)) {
+ printf(
+ "WARNING: Cycle detected in collection '%s', fixed as best as possible.\n"
+ "You may have to reconstruct your View Layers...\n",
+ collection->id.name);
+ }
+ }
+ }
+
/**
* Versioning code until next subversion bump goes here.
*
@@ -219,18 +231,26 @@ void do_versions_after_linking_290(Main *bmain, ReportList *UNUSED(reports))
* \note Keep this message at the bottom of the function.
*/
{
- LISTBASE_FOREACH (Collection *, collection, &bmain->collections) {
- if (BKE_collection_cycles_fix(bmain, collection)) {
- printf(
- "WARNING: Cycle detected in collection '%s', fixed as best as possible.\n"
- "You may have to reconstruct your View Layers...\n",
- collection->id.name);
- }
- }
+
/* Keep this block, even when empty. */
}
}
+static void panels_remove_x_closed_flag_recursive(Panel *panel)
+{
+ const bool was_closed_x = panel->flag & PNL_UNUSED_1;
+ const bool was_closed_y = panel->flag & PNL_CLOSED; /* That value was the Y closed flag. */
+
+ SET_FLAG_FROM_TEST(panel->flag, was_closed_x || was_closed_y, PNL_CLOSED);
+
+ /* Clear the old PNL_CLOSEDX flag. */
+ panel->flag &= ~PNL_UNUSED_1;
+
+ LISTBASE_FOREACH (Panel *, child_panel, &panel->children) {
+ panels_remove_x_closed_flag_recursive(child_panel);
+ }
+}
+
void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
{
UNUSED_VARS(fd);
@@ -409,17 +429,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
- /**
- * Versioning code until next subversion bump goes here.
- *
- * \note Be sure to check when bumping the version:
- * - "versioning_userdef.c", #BLO_version_defaults_userpref_blend
- * - "versioning_userdef.c", #do_versions_theme
- *
- * \note Keep this message at the bottom of the function.
- */
- {
- /* Keep this block, even when empty. */
+ if (!MAIN_VERSION_ATLEAST(bmain, 291, 1)) {
/* Initialize additional parameter of the Nishita sky model and change altitude unit. */
if (!DNA_struct_elem_find(fd->filesdna, "NodeTexSky", "float", "sun_intensity")) {
@@ -484,5 +494,49 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
}
+
+ /* Remove panel X axis collapsing, a remnant of horizontal panel alignment. */
+ LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
+ LISTBASE_FOREACH (Panel *, panel, &region->panels) {
+ panels_remove_x_closed_flag_recursive(panel);
+ }
+ }
+ }
+ }
+
+ /* Initialize solver for Boolean. */
+ if (!DNA_struct_elem_find(fd->filesdna, "BooleanModifierData", "enum", "solver")) {
+ for (Object *object = bmain->objects.first; object != NULL; object = object->id.next) {
+ LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
+ if (md->type == eModifierType_Boolean) {
+ BooleanModifierData *bmd = (BooleanModifierData *)md;
+ bmd->solver = eBooleanModifierSolver_Fast;
+ }
+ }
+ }
+ }
+ }
+
+ if (!DNA_struct_elem_find(fd->filesdna, "SoftBody", "int", "solver_mode")) {
+ for (Object *ob = bmain->objects.first; ob != NULL; ob = ob->id.next) {
+ if (ob->soft != NULL) {
+ sbExternalSetDefault(ob->soft);
+ }
+ }
+ }
+
+ /**
+ * Versioning code until next subversion bump goes here.
+ *
+ * \note Be sure to check when bumping the version:
+ * - "versioning_userdef.c", #BLO_version_defaults_userpref_blend
+ * - "versioning_userdef.c", #do_versions_theme
+ *
+ * \note Keep this message at the bottom of the function.
+ */
+ {
+ /* Keep this block, even when empty. */
}
}
diff --git a/source/blender/blenloader/intern/versioning_legacy.c b/source/blender/blenloader/intern/versioning_legacy.c
index 88ccb551e16..58d57f12b8f 100644
--- a/source/blender/blenloader/intern/versioning_legacy.c
+++ b/source/blender/blenloader/intern/versioning_legacy.c
@@ -1251,12 +1251,12 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain)
while (sce) {
ed = sce->ed;
if (ed) {
- SEQ_BEGIN (sce->ed, seq) {
+ SEQ_ALL_BEGIN (sce->ed, seq) {
if (seq->type == SEQ_TYPE_IMAGE || seq->type == SEQ_TYPE_MOVIE) {
seq->alpha_mode = SEQ_ALPHA_STRAIGHT;
}
}
- SEQ_END;
+ SEQ_ALL_END;
}
sce = sce->id.next;
@@ -2442,12 +2442,12 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain)
Sequence *seq;
for (sce = bmain->scenes.first; sce; sce = sce->id.next) {
- SEQ_BEGIN (sce->ed, seq) {
+ SEQ_ALL_BEGIN (sce->ed, seq) {
if (seq->blend_mode == 0) {
seq->blend_opacity = 100.0f;
}
}
- SEQ_END;
+ SEQ_ALL_END;
}
}
@@ -2595,12 +2595,12 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain)
while (sce) {
ed = sce->ed;
if (ed) {
- SEQP_BEGIN (ed, seq) {
+ SEQ_CURRENT_BEGIN (ed, seq) {
if (seq->strip && seq->strip->proxy) {
seq->strip->proxy->quality = 90;
}
}
- SEQ_END;
+ SEQ_CURRENT_END;
}
sce = sce->id.next;
diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c
index 0b116804481..d04907872b7 100644
--- a/source/blender/blenloader/intern/versioning_userdef.c
+++ b/source/blender/blenloader/intern/versioning_userdef.c
@@ -228,6 +228,11 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme)
*/
{
/* Keep this block, even when empty. */
+
+ /* The new defaults for the file browser theme are the same as
+ * the outliner's, and it's less disruptive to just copy them. */
+ copy_v4_v4_uchar(btheme->space_file.back, btheme->space_outliner.back);
+ copy_v4_v4_uchar(btheme->space_file.row_alternate, btheme->space_outliner.row_alternate);
}
#undef FROM_DEFAULT_V4_UCHAR
@@ -758,6 +763,12 @@ void BLO_version_defaults_userpref_blend(Main *bmain, UserDef *userdef)
userdef->statusbar_flag = STATUSBAR_SHOW_VERSION;
}
+ if (!USER_VERSION_ATLEAST(291, 1)) {
+ if (userdef->collection_instance_empty_size == 0) {
+ userdef->collection_instance_empty_size = 1.0f;
+ }
+ }
+
/**
* Versioning code until next subversion bump goes here.
*
@@ -769,10 +780,6 @@ void BLO_version_defaults_userpref_blend(Main *bmain, UserDef *userdef)
*/
{
/* Keep this block, even when empty. */
-
- if (userdef->collection_instance_empty_size == 0) {
- userdef->collection_instance_empty_size = 1.0f;
- }
}
if (userdef->pixelsize == 0.0f) {
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c
index b6f0d5bd70b..3f968b513bb 100644
--- a/source/blender/blenloader/intern/writefile.c
+++ b/source/blender/blenloader/intern/writefile.c
@@ -152,6 +152,7 @@
#include "MEM_guardedalloc.h" // MEM_freeN
#include "BKE_action.h"
+#include "BKE_anim_data.h"
#include "BKE_armature.h"
#include "BKE_blender_version.h"
#include "BKE_bpath.h"
@@ -160,10 +161,12 @@
#include "BKE_constraint.h"
#include "BKE_curve.h"
#include "BKE_curveprofile.h"
+#include "BKE_deform.h"
#include "BKE_fcurve.h"
#include "BKE_fcurve_driver.h"
#include "BKE_global.h" // for G
#include "BKE_gpencil_modifier.h"
+#include "BKE_idprop.h"
#include "BKE_idtype.h"
#include "BKE_layer.h"
#include "BKE_lib_id.h"
@@ -656,108 +659,6 @@ static void writelist_id(WriteData *wd, int filecode, const char *structname, co
* These functions are used by blender's .blend system for file saving/loading.
* \{ */
-void IDP_WriteProperty_OnlyData(const IDProperty *prop, BlendWriter *writer);
-void IDP_WriteProperty(const IDProperty *prop, BlendWriter *writer);
-
-static void IDP_WriteArray(const IDProperty *prop, BlendWriter *writer)
-{
- /*REMEMBER to set totalen to len in the linking code!!*/
- if (prop->data.pointer) {
- BLO_write_raw(writer, MEM_allocN_len(prop->data.pointer), prop->data.pointer);
-
- if (prop->subtype == IDP_GROUP) {
- IDProperty **array = prop->data.pointer;
- int a;
-
- for (a = 0; a < prop->len; a++) {
- IDP_WriteProperty(array[a], writer);
- }
- }
- }
-}
-
-static void IDP_WriteIDPArray(const IDProperty *prop, BlendWriter *writer)
-{
- /*REMEMBER to set totalen to len in the linking code!!*/
- if (prop->data.pointer) {
- const IDProperty *array = prop->data.pointer;
- int a;
-
- BLO_write_struct_array(writer, IDProperty, prop->len, array);
-
- for (a = 0; a < prop->len; a++) {
- IDP_WriteProperty_OnlyData(&array[a], writer);
- }
- }
-}
-
-static void IDP_WriteString(const IDProperty *prop, BlendWriter *writer)
-{
- /*REMEMBER to set totalen to len in the linking code!!*/
- BLO_write_raw(writer, prop->len, prop->data.pointer);
-}
-
-static void IDP_WriteGroup(const IDProperty *prop, BlendWriter *writer)
-{
- IDProperty *loop;
-
- for (loop = prop->data.group.first; loop; loop = loop->next) {
- IDP_WriteProperty(loop, writer);
- }
-}
-
-/* Functions to read/write ID Properties */
-void IDP_WriteProperty_OnlyData(const IDProperty *prop, BlendWriter *writer)
-{
- switch (prop->type) {
- case IDP_GROUP:
- IDP_WriteGroup(prop, writer);
- break;
- case IDP_STRING:
- IDP_WriteString(prop, writer);
- break;
- case IDP_ARRAY:
- IDP_WriteArray(prop, writer);
- break;
- case IDP_IDPARRAY:
- IDP_WriteIDPArray(prop, writer);
- break;
- }
-}
-
-void IDP_WriteProperty(const IDProperty *prop, BlendWriter *writer)
-{
- BLO_write_struct(writer, IDProperty, prop);
- IDP_WriteProperty_OnlyData(prop, writer);
-}
-
-static void write_iddata(BlendWriter *writer, ID *id)
-{
- /* ID_WM's id->properties are considered runtime only, and never written in .blend file. */
- if (id->properties && !ELEM(GS(id->name), ID_WM)) {
- IDP_WriteProperty(id->properties, writer);
- }
-
- if (id->override_library) {
- BLO_write_struct(writer, IDOverrideLibrary, id->override_library);
-
- BLO_write_struct_list(writer, IDOverrideLibraryProperty, &id->override_library->properties);
- LISTBASE_FOREACH (IDOverrideLibraryProperty *, op, &id->override_library->properties) {
- BLO_write_string(writer, op->rna_path);
-
- BLO_write_struct_list(writer, IDOverrideLibraryPropertyOperation, &op->operations);
- LISTBASE_FOREACH (IDOverrideLibraryPropertyOperation *, opop, &op->operations) {
- if (opop->subitem_reference_name) {
- BLO_write_string(writer, opop->subitem_reference_name);
- }
- if (opop->subitem_local_name) {
- BLO_write_string(writer, opop->subitem_local_name);
- }
- }
- }
- }
-}
-
static void write_previews(BlendWriter *writer, const PreviewImage *prv_orig)
{
/* Note we write previews also for undo steps. It takes up some memory,
@@ -782,107 +683,13 @@ static void write_previews(BlendWriter *writer, const PreviewImage *prv_orig)
}
}
-static void write_fmodifiers(BlendWriter *writer, ListBase *fmodifiers)
-{
- FModifier *fcm;
-
- /* Write all modifiers first (for faster reloading) */
- BLO_write_struct_list(writer, FModifier, fmodifiers);
-
- /* Modifiers */
- for (fcm = fmodifiers->first; fcm; fcm = fcm->next) {
- const FModifierTypeInfo *fmi = fmodifier_get_typeinfo(fcm);
-
- /* Write the specific data */
- if (fmi && fcm->data) {
- /* firstly, just write the plain fmi->data struct */
- BLO_write_struct_by_name(writer, fmi->structName, fcm->data);
-
- /* do any modifier specific stuff */
- switch (fcm->type) {
- case FMODIFIER_TYPE_GENERATOR: {
- FMod_Generator *data = fcm->data;
-
- /* write coefficients array */
- if (data->coefficients) {
- BLO_write_float_array(writer, data->arraysize, data->coefficients);
- }
-
- break;
- }
- case FMODIFIER_TYPE_ENVELOPE: {
- FMod_Envelope *data = fcm->data;
-
- /* write envelope data */
- if (data->data) {
- BLO_write_struct_array(writer, FCM_EnvelopeData, data->totvert, data->data);
- }
-
- break;
- }
- case FMODIFIER_TYPE_PYTHON: {
- FMod_Python *data = fcm->data;
-
- /* Write ID Properties -- and copy this comment EXACTLY for easy finding
- * of library blocks that implement this.*/
- IDP_WriteProperty(data->prop, writer);
-
- break;
- }
- }
- }
- }
-}
-
-static void write_fcurves(BlendWriter *writer, ListBase *fcurves)
-{
- FCurve *fcu;
-
- BLO_write_struct_list(writer, FCurve, fcurves);
- for (fcu = fcurves->first; fcu; fcu = fcu->next) {
- /* curve data */
- if (fcu->bezt) {
- BLO_write_struct_array(writer, BezTriple, fcu->totvert, fcu->bezt);
- }
- if (fcu->fpt) {
- BLO_write_struct_array(writer, FPoint, fcu->totvert, fcu->fpt);
- }
-
- if (fcu->rna_path) {
- BLO_write_string(writer, fcu->rna_path);
- }
-
- /* driver data */
- if (fcu->driver) {
- ChannelDriver *driver = fcu->driver;
- DriverVar *dvar;
-
- BLO_write_struct(writer, ChannelDriver, driver);
-
- /* variables */
- BLO_write_struct_list(writer, DriverVar, &driver->variables);
- for (dvar = driver->variables.first; dvar; dvar = dvar->next) {
- DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) {
- if (dtar->rna_path) {
- BLO_write_string(writer, dtar->rna_path);
- }
- }
- DRIVER_TARGETS_LOOPER_END;
- }
- }
-
- /* write F-Modifiers */
- write_fmodifiers(writer, &fcu->modifiers);
- }
-}
-
static void write_action(BlendWriter *writer, bAction *act, const void *id_address)
{
if (act->id.us > 0 || BLO_write_is_undo(writer)) {
BLO_write_id_struct(writer, bAction, id_address, &act->id);
- write_iddata(writer, &act->id);
+ BKE_id_blend_write(writer, &act->id);
- write_fcurves(writer, &act->curves);
+ BKE_fcurve_blend_write(writer, &act->curves);
LISTBASE_FOREACH (bActionGroup *, grp, &act->groups) {
BLO_write_struct(writer, bActionGroup, grp);
@@ -896,15 +703,12 @@ static void write_action(BlendWriter *writer, bAction *act, const void *id_addre
static void write_keyingsets(BlendWriter *writer, ListBase *list)
{
- KeyingSet *ks;
- KS_Path *ksp;
-
- for (ks = list->first; ks; ks = ks->next) {
+ LISTBASE_FOREACH (KeyingSet *, ks, list) {
/* KeyingSet */
BLO_write_struct(writer, KeyingSet, ks);
/* Paths */
- for (ksp = ks->paths.first; ksp; ksp = ksp->next) {
+ LISTBASE_FOREACH (KS_Path *, ksp, &ks->paths) {
/* Path */
BLO_write_struct(writer, KS_Path, ksp);
@@ -915,59 +719,6 @@ static void write_keyingsets(BlendWriter *writer, ListBase *list)
}
}
-static void write_nlastrips(BlendWriter *writer, ListBase *strips)
-{
- NlaStrip *strip;
-
- BLO_write_struct_list(writer, NlaStrip, strips);
- for (strip = strips->first; strip; strip = strip->next) {
- /* write the strip's F-Curves and modifiers */
- write_fcurves(writer, &strip->fcurves);
- write_fmodifiers(writer, &strip->modifiers);
-
- /* write the strip's children */
- write_nlastrips(writer, &strip->strips);
- }
-}
-
-static void write_nladata(BlendWriter *writer, ListBase *nlabase)
-{
- NlaTrack *nlt;
-
- /* write all the tracks */
- for (nlt = nlabase->first; nlt; nlt = nlt->next) {
- /* write the track first */
- BLO_write_struct(writer, NlaTrack, nlt);
-
- /* write the track's strips */
- write_nlastrips(writer, &nlt->strips);
- }
-}
-
-static void write_animdata(BlendWriter *writer, AnimData *adt)
-{
- AnimOverride *aor;
-
- /* firstly, just write the AnimData block */
- BLO_write_struct(writer, AnimData, adt);
-
- /* write drivers */
- write_fcurves(writer, &adt->drivers);
-
- /* write overrides */
- // FIXME: are these needed?
- for (aor = adt->overrides.first; aor; aor = aor->next) {
- /* overrides consist of base data + rna_path */
- BLO_write_struct(writer, AnimOverride, aor);
- BLO_write_string(writer, aor->rna_path);
- }
-
- // TODO write the remaps (if they are needed)
-
- /* write NLA data */
- write_nladata(writer, &adt->nla_tracks);
-}
-
static void write_node_socket_default_value(BlendWriter *writer, bNodeSocket *sock)
{
if (sock->default_value == NULL) {
@@ -1017,7 +768,7 @@ static void write_node_socket(BlendWriter *writer, bNodeSocket *sock)
BLO_write_struct(writer, bNodeSocket, sock);
if (sock->prop) {
- IDP_WriteProperty(sock->prop, writer);
+ IDP_BlendWrite(writer, sock->prop);
}
write_node_socket_default_value(writer, sock);
@@ -1028,7 +779,7 @@ static void write_node_socket_interface(BlendWriter *writer, bNodeSocket *sock)
BLO_write_struct(writer, bNodeSocket, sock);
if (sock->prop) {
- IDP_WriteProperty(sock->prop, writer);
+ IDP_BlendWrite(writer, sock->prop);
}
write_node_socket_default_value(writer, sock);
@@ -1036,31 +787,27 @@ static void write_node_socket_interface(BlendWriter *writer, bNodeSocket *sock)
/* this is only direct data, tree itself should have been written */
static void write_nodetree_nolib(BlendWriter *writer, bNodeTree *ntree)
{
- bNode *node;
- bNodeSocket *sock;
- bNodeLink *link;
-
/* for link_list() speed, we write per list */
if (ntree->adt) {
- write_animdata(writer, ntree->adt);
+ BKE_animdata_blend_write(writer, ntree->adt);
}
- for (node = ntree->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
BLO_write_struct(writer, bNode, node);
if (node->prop) {
- IDP_WriteProperty(node->prop, writer);
+ IDP_BlendWrite(writer, node->prop);
}
- for (sock = node->inputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
write_node_socket(writer, sock);
}
- for (sock = node->outputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
write_node_socket(writer, sock);
}
- for (link = node->internal_links.first; link; link = link->next) {
+ LISTBASE_FOREACH (bNodeLink *, link, &node->internal_links) {
BLO_write_struct(writer, bNodeLink, link);
}
@@ -1124,26 +871,26 @@ static void write_nodetree_nolib(BlendWriter *writer, bNodeTree *ntree)
if (node->type == CMP_NODE_OUTPUT_FILE) {
/* inputs have own storage data */
- for (sock = node->inputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
BLO_write_struct(writer, NodeImageMultiFileSocket, sock->storage);
}
}
if (ELEM(node->type, CMP_NODE_IMAGE, CMP_NODE_R_LAYERS)) {
/* write extra socket info */
- for (sock = node->outputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
BLO_write_struct(writer, NodeImageLayer, sock->storage);
}
}
}
- for (link = ntree->links.first; link; link = link->next) {
+ LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
BLO_write_struct(writer, bNodeLink, link);
}
- for (sock = ntree->inputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) {
write_node_socket_interface(writer, sock);
}
- for (sock = ntree->outputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) {
write_node_socket_interface(writer, sock);
}
}
@@ -1205,15 +952,15 @@ typedef struct RenderInfo {
static void write_renderinfo(WriteData *wd, Main *mainvar)
{
bScreen *curscreen;
- Scene *sce, *curscene = NULL;
+ Scene *curscene = NULL;
ViewLayer *view_layer;
- RenderInfo data;
/* XXX in future, handle multiple windows with multiple screens? */
current_screen_compat(mainvar, false, &curscreen, &curscene, &view_layer);
- for (sce = mainvar->scenes.first; sce; sce = sce->id.next) {
+ LISTBASE_FOREACH (Scene *, sce, &mainvar->scenes) {
if (sce->id.lib == NULL && (sce == curscene || (sce->r.scemode & R_BG_RENDER))) {
+ RenderInfo data;
data.sfra = sce->r.sfra;
data.efra = sce->r.efra;
memset(data.scene_name, 0, sizeof(data.scene_name));
@@ -1229,7 +976,7 @@ static void write_keymapitem(BlendWriter *writer, const wmKeyMapItem *kmi)
{
BLO_write_struct(writer, wmKeyMapItem, kmi);
if (kmi->properties) {
- IDP_WriteProperty(kmi->properties, writer);
+ IDP_BlendWrite(writer, kmi->properties);
}
}
@@ -1262,7 +1009,7 @@ static void write_userdef(BlendWriter *writer, const UserDef *userdef)
LISTBASE_FOREACH (const wmKeyConfigPref *, kpt, &userdef->user_keyconfig_prefs) {
BLO_write_struct(writer, wmKeyConfigPref, kpt);
if (kpt->prop) {
- IDP_WriteProperty(kpt->prop, writer);
+ IDP_BlendWrite(writer, kpt->prop);
}
}
@@ -1273,7 +1020,7 @@ static void write_userdef(BlendWriter *writer, const UserDef *userdef)
const bUserMenuItem_Op *umi_op = (const bUserMenuItem_Op *)umi;
BLO_write_struct(writer, bUserMenuItem_Op, umi_op);
if (umi_op->prop) {
- IDP_WriteProperty(umi_op->prop, writer);
+ IDP_BlendWrite(writer, umi_op->prop);
}
}
else if (umi->type == USER_MENU_TYPE_MENU) {
@@ -1293,7 +1040,7 @@ static void write_userdef(BlendWriter *writer, const UserDef *userdef)
LISTBASE_FOREACH (const bAddon *, bext, &userdef->addons) {
BLO_write_struct(writer, bAddon, bext);
if (bext->prop) {
- IDP_WriteProperty(bext->prop, writer);
+ IDP_BlendWrite(writer, bext->prop);
}
}
@@ -1308,11 +1055,9 @@ static void write_userdef(BlendWriter *writer, const UserDef *userdef)
static void write_boid_state(BlendWriter *writer, BoidState *state)
{
- BoidRule *rule = state->rules.first;
-
BLO_write_struct(writer, BoidState, state);
- for (; rule; rule = rule->next) {
+ LISTBASE_FOREACH (BoidRule *, rule, &state->rules) {
switch (rule->type) {
case eBoidRuleType_Goal:
case eBoidRuleType_Avoid:
@@ -1361,21 +1106,14 @@ static const char *ptcache_extra_struct[] = {
};
static void write_pointcaches(BlendWriter *writer, ListBase *ptcaches)
{
- PointCache *cache = ptcaches->first;
- int i;
-
- for (; cache; cache = cache->next) {
+ LISTBASE_FOREACH (PointCache *, cache, ptcaches) {
BLO_write_struct(writer, PointCache, cache);
if ((cache->flag & PTCACHE_DISK_CACHE) == 0) {
- PTCacheMem *pm = cache->mem_cache.first;
-
- for (; pm; pm = pm->next) {
- PTCacheExtra *extra = pm->extradata.first;
-
+ LISTBASE_FOREACH (PTCacheMem *, pm, &cache->mem_cache) {
BLO_write_struct(writer, PTCacheMem, pm);
- for (i = 0; i < BPHYS_TOT_DATA; i++) {
+ for (int i = 0; i < BPHYS_TOT_DATA; i++) {
if (pm->data[i] && pm->data_types & (1 << i)) {
if (ptcache_data_struct[i][0] == '\0') {
BLO_write_raw(writer, MEM_allocN_len(pm->data[i]), pm->data[i]);
@@ -1387,7 +1125,7 @@ static void write_pointcaches(BlendWriter *writer, ListBase *ptcaches)
}
}
- for (; extra; extra = extra->next) {
+ LISTBASE_FOREACH (PTCacheExtra *, extra, &pm->extradata) {
if (ptcache_extra_struct[extra->type][0] == '\0') {
continue;
}
@@ -1407,10 +1145,10 @@ static void write_particlesettings(BlendWriter *writer,
if (part->id.us > 0 || BLO_write_is_undo(writer)) {
/* write LibData */
BLO_write_id_struct(writer, ParticleSettings, id_address, &part->id);
- write_iddata(writer, &part->id);
+ BKE_id_blend_write(writer, &part->id);
if (part->adt) {
- write_animdata(writer, part->adt);
+ BKE_animdata_blend_write(writer, part->adt);
}
BLO_write_struct(writer, PartDeflect, part->pd);
BLO_write_struct(writer, PartDeflect, part->pd2);
@@ -1464,11 +1202,7 @@ static void write_particlesettings(BlendWriter *writer,
static void write_particlesystems(BlendWriter *writer, ListBase *particles)
{
- ParticleSystem *psys = particles->first;
- ParticleTarget *pt;
- int a;
-
- for (; psys; psys = psys->next) {
+ LISTBASE_FOREACH (ParticleSystem *, psys, particles) {
BLO_write_struct(writer, ParticleSystem, psys);
if (psys->particles) {
@@ -1477,7 +1211,7 @@ static void write_particlesystems(BlendWriter *writer, ListBase *particles)
if (psys->particles->hair) {
ParticleData *pa = psys->particles;
- for (a = 0; a < psys->totpart; a++, pa++) {
+ for (int a = 0; a < psys->totpart; a++, pa++) {
BLO_write_struct_array(writer, HairKey, pa->totkey, pa->hair);
}
}
@@ -1492,8 +1226,7 @@ static void write_particlesystems(BlendWriter *writer, ListBase *particles)
writer, ParticleSpring, psys->tot_fluidsprings, psys->fluid_springs);
}
}
- pt = psys->targets.first;
- for (; pt; pt = pt->next) {
+ LISTBASE_FOREACH (ParticleTarget *, pt, &psys->targets) {
BLO_write_struct(writer, ParticleTarget, pt);
}
@@ -1527,9 +1260,7 @@ static void write_motionpath(BlendWriter *writer, bMotionPath *mpath)
static void write_constraints(BlendWriter *writer, ListBase *conlist)
{
- bConstraint *con;
-
- for (con = conlist->first; con; con = con->next) {
+ LISTBASE_FOREACH (bConstraint *, con, conlist) {
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
/* Write the specific data */
@@ -1541,25 +1272,23 @@ static void write_constraints(BlendWriter *writer, ListBase *conlist)
switch (con->type) {
case CONSTRAINT_TYPE_PYTHON: {
bPythonConstraint *data = con->data;
- bConstraintTarget *ct;
/* write targets */
- for (ct = data->targets.first; ct; ct = ct->next) {
+ LISTBASE_FOREACH (bConstraintTarget *, ct, &data->targets) {
BLO_write_struct(writer, bConstraintTarget, ct);
}
/* Write ID Properties -- and copy this comment EXACTLY for easy finding
* of library blocks that implement this.*/
- IDP_WriteProperty(data->prop, writer);
+ IDP_BlendWrite(writer, data->prop);
break;
}
case CONSTRAINT_TYPE_ARMATURE: {
bArmatureConstraint *data = con->data;
- bConstraintTarget *ct;
/* write targets */
- for (ct = data->targets.first; ct; ct = ct->next) {
+ LISTBASE_FOREACH (bConstraintTarget *, ct, &data->targets) {
BLO_write_struct(writer, bConstraintTarget, ct);
}
@@ -1583,9 +1312,6 @@ static void write_constraints(BlendWriter *writer, ListBase *conlist)
static void write_pose(BlendWriter *writer, bPose *pose, bArmature *arm)
{
- bPoseChannel *chan;
- bActionGroup *grp;
-
/* Write each channel */
if (pose == NULL) {
return;
@@ -1594,11 +1320,11 @@ static void write_pose(BlendWriter *writer, bPose *pose, bArmature *arm)
BLI_assert(arm != NULL);
/* Write channels */
- for (chan = pose->chanbase.first; chan; chan = chan->next) {
+ LISTBASE_FOREACH (bPoseChannel *, chan, &pose->chanbase) {
/* Write ID Properties -- and copy this comment EXACTLY for easy finding
* of library blocks that implement this.*/
if (chan->prop) {
- IDP_WriteProperty(chan->prop, writer);
+ IDP_BlendWrite(writer, chan->prop);
}
write_constraints(writer, &chan->constraints);
@@ -1620,7 +1346,7 @@ static void write_pose(BlendWriter *writer, bPose *pose, bArmature *arm)
}
/* Write groups */
- for (grp = pose->agroups.first; grp; grp = grp->next) {
+ LISTBASE_FOREACH (bActionGroup *, grp, &pose->agroups) {
BLO_write_struct(writer, bActionGroup, grp);
}
@@ -1652,13 +1378,11 @@ static void write_fmaps(BlendWriter *writer, ListBase *fbase)
static void write_modifiers(BlendWriter *writer, ListBase *modbase)
{
- ModifierData *md;
-
if (modbase == NULL) {
return;
}
- for (md = modbase->first; md; md = md->next) {
+ LISTBASE_FOREACH (ModifierData *, md, modbase) {
const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
if (mti == NULL) {
return;
@@ -1717,15 +1441,14 @@ static void write_modifiers(BlendWriter *writer, ListBase *modbase)
DynamicPaintModifierData *pmd = (DynamicPaintModifierData *)md;
if (pmd->canvas) {
- DynamicPaintSurface *surface;
BLO_write_struct(writer, DynamicPaintCanvasSettings, pmd->canvas);
/* write surfaces */
- for (surface = pmd->canvas->surfaces.first; surface; surface = surface->next) {
+ LISTBASE_FOREACH (DynamicPaintSurface *, surface, &pmd->canvas->surfaces) {
BLO_write_struct(writer, DynamicPaintSurface, surface);
}
/* write caches and effector weights */
- for (surface = pmd->canvas->surfaces.first; surface; surface = surface->next) {
+ LISTBASE_FOREACH (DynamicPaintSurface *, surface, &pmd->canvas->surfaces) {
write_pointcaches(writer, &(surface->ptcaches));
BLO_write_struct(writer, EffectorWeights, surface->effector_weights);
@@ -1757,13 +1480,11 @@ static void write_modifiers(BlendWriter *writer, ListBase *modbase)
static void write_gpencil_modifiers(BlendWriter *writer, ListBase *modbase)
{
- GpencilModifierData *md;
-
if (modbase == NULL) {
return;
}
- for (md = modbase->first; md; md = md->next) {
+ LISTBASE_FOREACH (GpencilModifierData *, md, modbase) {
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(md->type);
if (mti == NULL) {
return;
@@ -1824,13 +1545,11 @@ static void write_gpencil_modifiers(BlendWriter *writer, ListBase *modbase)
static void write_shaderfxs(BlendWriter *writer, ListBase *fxbase)
{
- ShaderFxData *fx;
-
if (fxbase == NULL) {
return;
}
- for (fx = fxbase->first; fx; fx = fx->next) {
+ LISTBASE_FOREACH (ShaderFxData *, fx, fxbase) {
const ShaderFxTypeInfo *fxi = BKE_shaderfx_get_info(fx->type);
if (fxi == NULL) {
return;
@@ -1848,10 +1567,10 @@ static void write_object(BlendWriter *writer, Object *ob, const void *id_address
/* write LibData */
BLO_write_id_struct(writer, Object, id_address, &ob->id);
- write_iddata(writer, &ob->id);
+ BKE_id_blend_write(writer, &ob->id);
if (ob->adt) {
- write_animdata(writer, ob->adt);
+ BKE_animdata_blend_write(writer, ob->adt);
}
/* direct data */
@@ -1875,9 +1594,9 @@ static void write_object(BlendWriter *writer, Object *ob, const void *id_address
BLO_write_struct(writer, PartDeflect, ob->pd);
if (ob->soft) {
- /* Don't write ADMM-PD data. */
- ADMMPDInterfaceData *admmpd = ob->soft->admmpd;
- ob->soft->admmpd = NULL;
+ /* Don't write ADMM-PD data. Just recompute when needed. */
+ ListBase *admmpd_list = ob->soft->shared->admmpd_list;
+ ob->soft->shared->admmpd_list = NULL;
/* Set deprecated pointers to prevent crashes of older Blenders */
ob->soft->pointcache = ob->soft->shared->pointcache;
ob->soft->ptcaches = ob->soft->shared->ptcaches;
@@ -1885,8 +1604,8 @@ static void write_object(BlendWriter *writer, Object *ob, const void *id_address
BLO_write_struct(writer, SoftBody_Shared, ob->soft->shared);
write_pointcaches(writer, &(ob->soft->shared->ptcaches));
BLO_write_struct(writer, EffectorWeights, ob->soft->effector_weights);
- /* Reset the ADMM-PD data pointer */
- ob->soft->admmpd = admmpd;
+ /* Reset the ADMM-PD data pointer. */
+ ob->soft->shared->admmpd_list = admmpd_list;
}
if (ob->rigidbody_object) {
@@ -1907,7 +1626,6 @@ static void write_object(BlendWriter *writer, Object *ob, const void *id_address
write_shaderfxs(writer, &ob->shader_fx);
BLO_write_struct_list(writer, LinkData, &ob->pc_ids);
- BLO_write_struct_list(writer, LodLevel, &ob->lodlevels);
write_previews(writer, ob->preview);
}
@@ -1922,7 +1640,7 @@ static void write_vfont(BlendWriter *writer, VFont *vf, const void *id_address)
/* write LibData */
BLO_write_id_struct(writer, VFont, id_address, &vf->id);
- write_iddata(writer, &vf->id);
+ BKE_id_blend_write(writer, &vf->id);
/* direct data */
if (vf->packedfile) {
@@ -1938,10 +1656,10 @@ static void write_key(BlendWriter *writer, Key *key, const void *id_address)
if (key->id.us > 0 || BLO_write_is_undo(writer)) {
/* write LibData */
BLO_write_id_struct(writer, Key, id_address, &key->id);
- write_iddata(writer, &key->id);
+ BKE_id_blend_write(writer, &key->id);
if (key->adt) {
- write_animdata(writer, key->adt);
+ BKE_animdata_blend_write(writer, key->adt);
}
/* direct data */
@@ -1959,10 +1677,10 @@ static void write_camera(BlendWriter *writer, Camera *cam, const void *id_addres
if (cam->id.us > 0 || BLO_write_is_undo(writer)) {
/* write LibData */
BLO_write_id_struct(writer, Camera, id_address, &cam->id);
- write_iddata(writer, &cam->id);
+ BKE_id_blend_write(writer, &cam->id);
if (cam->adt) {
- write_animdata(writer, cam->adt);
+ BKE_animdata_blend_write(writer, cam->adt);
}
LISTBASE_FOREACH (CameraBGImage *, bgpic, &cam->bg_images) {
@@ -1984,12 +1702,12 @@ static void write_mball(BlendWriter *writer, MetaBall *mb, const void *id_addres
/* write LibData */
BLO_write_id_struct(writer, MetaBall, id_address, &mb->id);
- write_iddata(writer, &mb->id);
+ BKE_id_blend_write(writer, &mb->id);
/* direct data */
BLO_write_pointer_array(writer, mb->totcol, mb->mat);
if (mb->adt) {
- write_animdata(writer, mb->adt);
+ BKE_animdata_blend_write(writer, mb->adt);
}
LISTBASE_FOREACH (MetaElem *, ml, &mb->elems) {
@@ -2008,12 +1726,12 @@ static void write_curve(BlendWriter *writer, Curve *cu, const void *id_address)
/* write LibData */
BLO_write_id_struct(writer, Curve, id_address, &cu->id);
- write_iddata(writer, &cu->id);
+ BKE_id_blend_write(writer, &cu->id);
/* direct data */
BLO_write_pointer_array(writer, cu->totcol, cu->mat);
if (cu->adt) {
- write_animdata(writer, cu->adt);
+ BKE_animdata_blend_write(writer, cu->adt);
}
if (cu->vfont) {
@@ -2045,206 +1763,6 @@ static void write_curve(BlendWriter *writer, Curve *cu, const void *id_address)
}
}
-static void write_dverts(BlendWriter *writer, int count, MDeformVert *dvlist)
-{
- if (dvlist) {
-
- /* Write the dvert list */
- BLO_write_struct_array(writer, MDeformVert, count, dvlist);
-
- /* Write deformation data for each dvert */
- for (int i = 0; i < count; i++) {
- if (dvlist[i].dw) {
- BLO_write_struct_array(writer, MDeformWeight, dvlist[i].totweight, dvlist[i].dw);
- }
- }
- }
-}
-
-static void write_mdisps(BlendWriter *writer, int count, MDisps *mdlist, int external)
-{
- if (mdlist) {
- int i;
-
- BLO_write_struct_array(writer, MDisps, count, mdlist);
- for (i = 0; i < count; i++) {
- MDisps *md = &mdlist[i];
- if (md->disps) {
- if (!external) {
- BLO_write_float3_array(writer, md->totdisp, &md->disps[0][0]);
- }
- }
-
- if (md->hidden) {
- BLO_write_raw(writer, BLI_BITMAP_SIZE(md->totdisp), md->hidden);
- }
- }
- }
-}
-
-static void write_grid_paint_mask(BlendWriter *writer, int count, GridPaintMask *grid_paint_mask)
-{
- if (grid_paint_mask) {
- int i;
-
- BLO_write_struct_array(writer, GridPaintMask, count, grid_paint_mask);
- for (i = 0; i < count; i++) {
- GridPaintMask *gpm = &grid_paint_mask[i];
- if (gpm->data) {
- const int gridsize = BKE_ccg_gridsize(gpm->level);
- BLO_write_raw(writer, sizeof(*gpm->data) * gridsize * gridsize, gpm->data);
- }
- }
- }
-}
-
-static void write_customdata(BlendWriter *writer,
- ID *id,
- int count,
- CustomData *data,
- CustomDataLayer *layers,
- CustomDataMask cddata_mask)
-{
- int i;
-
- /* write external customdata (not for undo) */
- if (data->external && !BLO_write_is_undo(writer)) {
- CustomData_external_write(data, id, cddata_mask, count, 0);
- }
-
- BLO_write_struct_array_at_address(writer, CustomDataLayer, data->totlayer, data->layers, layers);
-
- for (i = 0; i < data->totlayer; i++) {
- CustomDataLayer *layer = &layers[i];
- const char *structname;
- int structnum, datasize;
-
- if (layer->type == CD_MDEFORMVERT) {
- /* layer types that allocate own memory need special handling */
- write_dverts(writer, count, layer->data);
- }
- else if (layer->type == CD_MDISPS) {
- write_mdisps(writer, count, layer->data, layer->flag & CD_FLAG_EXTERNAL);
- }
- else if (layer->type == CD_PAINT_MASK) {
- const float *layer_data = layer->data;
- BLO_write_raw(writer, sizeof(*layer_data) * count, layer_data);
- }
- else if (layer->type == CD_SCULPT_FACE_SETS) {
- const float *layer_data = layer->data;
- BLO_write_raw(writer, sizeof(*layer_data) * count, layer_data);
- }
- else if (layer->type == CD_GRID_PAINT_MASK) {
- write_grid_paint_mask(writer, count, layer->data);
- }
- else if (layer->type == CD_FACEMAP) {
- const int *layer_data = layer->data;
- BLO_write_raw(writer, sizeof(*layer_data) * count, layer_data);
- }
- else {
- CustomData_file_write_info(layer->type, &structname, &structnum);
- if (structnum) {
- datasize = structnum * count;
- BLO_write_struct_array_by_name(writer, structname, datasize, layer->data);
- }
- else if (!BLO_write_is_undo(writer)) { /* Do not warn on undo. */
- printf("%s error: layer '%s':%d - can't be written to file\n",
- __func__,
- structname,
- layer->type);
- }
- }
- }
-
- if (data->external) {
- BLO_write_struct(writer, CustomDataExternal, data->external);
- }
-}
-
-static void write_mesh(BlendWriter *writer, Mesh *mesh, const void *id_address)
-{
- if (mesh->id.us > 0 || BLO_write_is_undo(writer)) {
- /* cache only - don't write */
- mesh->mface = NULL;
- mesh->totface = 0;
- memset(&mesh->fdata, 0, sizeof(mesh->fdata));
- memset(&mesh->runtime, 0, sizeof(mesh->runtime));
-
- /* Reduce xdata layers, fill xlayers with layers to be written.
- * This makes xdata invalid for Blender, which is why we made a
- * temporary local copy. */
- CustomDataLayer *vlayers = NULL, vlayers_buff[CD_TEMP_CHUNK_SIZE];
- CustomDataLayer *elayers = NULL, elayers_buff[CD_TEMP_CHUNK_SIZE];
- CustomDataLayer *flayers = NULL, flayers_buff[CD_TEMP_CHUNK_SIZE];
- CustomDataLayer *llayers = NULL, llayers_buff[CD_TEMP_CHUNK_SIZE];
- CustomDataLayer *players = NULL, players_buff[CD_TEMP_CHUNK_SIZE];
-
- CustomData_file_write_prepare(&mesh->vdata, &vlayers, vlayers_buff, ARRAY_SIZE(vlayers_buff));
- CustomData_file_write_prepare(&mesh->edata, &elayers, elayers_buff, ARRAY_SIZE(elayers_buff));
- flayers = flayers_buff;
- CustomData_file_write_prepare(&mesh->ldata, &llayers, llayers_buff, ARRAY_SIZE(llayers_buff));
- CustomData_file_write_prepare(&mesh->pdata, &players, players_buff, ARRAY_SIZE(players_buff));
-
- BLO_write_id_struct(writer, Mesh, id_address, &mesh->id);
- write_iddata(writer, &mesh->id);
-
- /* direct data */
- if (mesh->adt) {
- write_animdata(writer, mesh->adt);
- }
-
- BLO_write_pointer_array(writer, mesh->totcol, mesh->mat);
- BLO_write_raw(writer, sizeof(MSelect) * mesh->totselect, mesh->mselect);
-
- write_customdata(writer, &mesh->id, mesh->totvert, &mesh->vdata, vlayers, CD_MASK_MESH.vmask);
- write_customdata(writer, &mesh->id, mesh->totedge, &mesh->edata, elayers, CD_MASK_MESH.emask);
- /* fdata is really a dummy - written so slots align */
- write_customdata(writer, &mesh->id, mesh->totface, &mesh->fdata, flayers, CD_MASK_MESH.fmask);
- write_customdata(writer, &mesh->id, mesh->totloop, &mesh->ldata, llayers, CD_MASK_MESH.lmask);
- write_customdata(writer, &mesh->id, mesh->totpoly, &mesh->pdata, players, CD_MASK_MESH.pmask);
-
- /* free temporary data */
- if (vlayers && vlayers != vlayers_buff) {
- MEM_freeN(vlayers);
- }
- if (elayers && elayers != elayers_buff) {
- MEM_freeN(elayers);
- }
- if (flayers && flayers != flayers_buff) {
- MEM_freeN(flayers);
- }
- if (llayers && llayers != llayers_buff) {
- MEM_freeN(llayers);
- }
- if (players && players != players_buff) {
- MEM_freeN(players);
- }
- }
-}
-
-static void write_lattice(BlendWriter *writer, Lattice *lt, const void *id_address)
-{
- if (lt->id.us > 0 || BLO_write_is_undo(writer)) {
- /* Clean up, important in undo case to reduce false detection of changed datablocks. */
- lt->editlatt = NULL;
- lt->batch_cache = NULL;
-
- /* write LibData */
- BLO_write_id_struct(writer, Lattice, id_address, &lt->id);
- write_iddata(writer, &lt->id);
-
- /* write animdata */
- if (lt->adt) {
- write_animdata(writer, lt->adt);
- }
-
- /* direct data */
- BLO_write_struct_array(writer, BPoint, lt->pntsu * lt->pntsv * lt->pntsw, lt->def);
-
- write_dverts(writer, lt->pntsu * lt->pntsv * lt->pntsw, lt->dvert);
- }
-}
-
static void write_image(BlendWriter *writer, Image *ima, const void *id_address)
{
if (ima->id.us > 0 || BLO_write_is_undo(writer)) {
@@ -2259,7 +1777,7 @@ static void write_image(BlendWriter *writer, Image *ima, const void *id_address)
/* write LibData */
BLO_write_id_struct(writer, Image, id_address, &ima->id);
- write_iddata(writer, &ima->id);
+ BKE_id_blend_write(writer, &ima->id);
for (imapf = ima->packedfiles.first; imapf; imapf = imapf->next) {
BLO_write_struct(writer, ImagePackedFile, imapf);
@@ -2290,10 +1808,10 @@ static void write_texture(BlendWriter *writer, Tex *tex, const void *id_address)
if (tex->id.us > 0 || BLO_write_is_undo(writer)) {
/* write LibData */
BLO_write_id_struct(writer, Tex, id_address, &tex->id);
- write_iddata(writer, &tex->id);
+ BKE_id_blend_write(writer, &tex->id);
if (tex->adt) {
- write_animdata(writer, tex->adt);
+ BKE_animdata_blend_write(writer, tex->adt);
}
/* direct data */
@@ -2320,10 +1838,10 @@ static void write_material(BlendWriter *writer, Material *ma, const void *id_add
/* write LibData */
BLO_write_id_struct(writer, Material, id_address, &ma->id);
- write_iddata(writer, &ma->id);
+ BKE_id_blend_write(writer, &ma->id);
if (ma->adt) {
- write_animdata(writer, ma->adt);
+ BKE_animdata_blend_write(writer, ma->adt);
}
/* nodetree is integral part of material, no libdata */
@@ -2349,10 +1867,10 @@ static void write_world(BlendWriter *writer, World *wrld, const void *id_address
/* write LibData */
BLO_write_id_struct(writer, World, id_address, &wrld->id);
- write_iddata(writer, &wrld->id);
+ BKE_id_blend_write(writer, &wrld->id);
if (wrld->adt) {
- write_animdata(writer, wrld->adt);
+ BKE_animdata_blend_write(writer, wrld->adt);
}
/* nodetree is integral part of world, no libdata */
@@ -2370,10 +1888,10 @@ static void write_light(BlendWriter *writer, Light *la, const void *id_address)
if (la->id.us > 0 || BLO_write_is_undo(writer)) {
/* write LibData */
BLO_write_id_struct(writer, Light, id_address, &la->id);
- write_iddata(writer, &la->id);
+ BKE_id_blend_write(writer, &la->id);
if (la->adt) {
- write_animdata(writer, la->adt);
+ BKE_animdata_blend_write(writer, la->adt);
}
if (la->curfalloff) {
@@ -2415,7 +1933,7 @@ static void write_collection(BlendWriter *writer, Collection *collection, const
/* write LibData */
BLO_write_id_struct(writer, Collection, id_address, &collection->id);
- write_iddata(writer, &collection->id);
+ BKE_id_blend_write(writer, &collection->id);
write_collection_nolib(writer, collection);
}
@@ -2423,9 +1941,7 @@ static void write_collection(BlendWriter *writer, Collection *collection, const
static void write_sequence_modifiers(BlendWriter *writer, ListBase *modbase)
{
- SequenceModifierData *smd;
-
- for (smd = modbase->first; smd; smd = smd->next) {
+ LISTBASE_FOREACH (SequenceModifierData *, smd, modbase) {
const SequenceModifierTypeInfo *smti = BKE_sequence_modifier_type_info_get(smd->type);
if (smti) {
@@ -2458,7 +1974,7 @@ static void write_view_settings(BlendWriter *writer, ColorManagedViewSettings *v
static void write_view3dshading(BlendWriter *writer, View3DShading *shading)
{
if (shading->prop) {
- IDP_WriteProperty(shading->prop, writer);
+ IDP_BlendWrite(writer, shading->prop);
}
}
@@ -2485,7 +2001,7 @@ static void write_view_layer(BlendWriter *writer, ViewLayer *view_layer)
BLO_write_struct_list(writer, Base, &view_layer->object_bases);
if (view_layer->id_properties) {
- IDP_WriteProperty(view_layer->id_properties, writer);
+ IDP_BlendWrite(writer, view_layer->id_properties);
}
LISTBASE_FOREACH (FreestyleModuleConfig *, fmc, &view_layer->freestyle_config.modules) {
@@ -2543,10 +2059,10 @@ static void write_scene(BlendWriter *writer, Scene *sce, const void *id_address)
/* write LibData */
BLO_write_id_struct(writer, Scene, id_address, &sce->id);
- write_iddata(writer, &sce->id);
+ BKE_id_blend_write(writer, &sce->id);
if (sce->adt) {
- write_animdata(writer, sce->adt);
+ BKE_animdata_blend_write(writer, sce->adt);
}
write_keyingsets(writer, &sce->keyingsets);
@@ -2612,15 +2128,15 @@ static void write_scene(BlendWriter *writer, Scene *sce, const void *id_address)
/* reset write flags too */
- SEQ_BEGIN (ed, seq) {
+ SEQ_ALL_BEGIN (ed, seq) {
if (seq->strip) {
seq->strip->done = false;
}
BLO_write_struct(writer, Sequence, seq);
}
- SEQ_END;
+ SEQ_ALL_END;
- SEQ_BEGIN (ed, seq) {
+ SEQ_ALL_BEGIN (ed, seq) {
if (seq->strip && seq->strip->done == 0) {
/* write strip with 'done' at 0 because readfile */
@@ -2680,12 +2196,12 @@ static void write_scene(BlendWriter *writer, Scene *sce, const void *id_address)
}
if (seq->prop) {
- IDP_WriteProperty(seq->prop, writer);
+ IDP_BlendWrite(writer, seq->prop);
}
write_sequence_modifiers(writer, &seq->modifiers);
}
- SEQ_END;
+ SEQ_ALL_END;
/* new; meta stack too, even when its nasty restore code */
LISTBASE_FOREACH (MetaStack *, ms, &ed->metastack) {
@@ -2703,7 +2219,7 @@ static void write_scene(BlendWriter *writer, Scene *sce, const void *id_address)
}
}
if (sce->r.ffcodecdata.properties) {
- IDP_WriteProperty(sce->r.ffcodecdata.properties, writer);
+ IDP_BlendWrite(writer, sce->r.ffcodecdata.properties);
}
/* writing dynamic list of TimeMarkers to the blend file */
@@ -2777,10 +2293,10 @@ static void write_gpencil(BlendWriter *writer, bGPdata *gpd, const void *id_addr
/* write gpd data block to file */
BLO_write_id_struct(writer, bGPdata, id_address, &gpd->id);
- write_iddata(writer, &gpd->id);
+ BKE_id_blend_write(writer, &gpd->id);
if (gpd->adt) {
- write_animdata(writer, gpd->adt);
+ BKE_animdata_blend_write(writer, gpd->adt);
}
BLO_write_pointer_array(writer, gpd->totcol, gpd->mat);
@@ -2798,7 +2314,7 @@ static void write_gpencil(BlendWriter *writer, bGPdata *gpd, const void *id_addr
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
BLO_write_struct_array(writer, bGPDspoint, gps->totpoints, gps->points);
BLO_write_struct_array(writer, bGPDtriangle, gps->tot_triangles, gps->triangles);
- write_dverts(writer, gps->totpoints, gps->dvert);
+ BKE_defvert_blend_write(writer, gps->totpoints, gps->dvert);
}
}
}
@@ -2847,7 +2363,7 @@ static void write_uilist(BlendWriter *writer, uiList *ui_list)
BLO_write_struct(writer, uiList, ui_list);
if (ui_list->properties) {
- IDP_WriteProperty(ui_list->properties, writer);
+ IDP_BlendWrite(writer, ui_list->properties);
}
}
@@ -2993,18 +2509,16 @@ static void write_area_regions(BlendWriter *writer, ScrArea *area)
}
else if (sl->spacetype == SPACE_NODE) {
SpaceNode *snode = (SpaceNode *)sl;
- bNodeTreePath *path;
BLO_write_struct(writer, SpaceNode, snode);
- for (path = snode->treepath.first; path; path = path->next) {
+ LISTBASE_FOREACH (bNodeTreePath *, path, &snode->treepath) {
BLO_write_struct(writer, bNodeTreePath, path);
}
}
else if (sl->spacetype == SPACE_CONSOLE) {
SpaceConsole *con = (SpaceConsole *)sl;
- ConsoleLine *cl;
- for (cl = con->history.first; cl; cl = cl->next) {
+ LISTBASE_FOREACH (ConsoleLine *, cl, &con->history) {
/* 'len_alloc' is invalid on write, set from 'len' on read */
BLO_write_struct(writer, ConsoleLine, cl);
BLO_write_raw(writer, cl->len + 1, cl->line);
@@ -3053,7 +2567,7 @@ static void write_area_map(BlendWriter *writer, ScrAreaMap *area_map)
static void write_windowmanager(BlendWriter *writer, wmWindowManager *wm, const void *id_address)
{
BLO_write_id_struct(writer, wmWindowManager, id_address, &wm->id);
- write_iddata(writer, &wm->id);
+ BKE_id_blend_write(writer, &wm->id);
write_wm_xr_data(writer, &wm->xr);
LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
@@ -3088,7 +2602,7 @@ static void write_screen(BlendWriter *writer, bScreen *screen, const void *id_ad
/* write LibData */
/* in 2.50+ files, the file identifier for screens is patched, forward compatibility */
writestruct_at_address(writer->wd, ID_SCRN, bScreen, 1, id_address, screen);
- write_iddata(writer, &screen->id);
+ BKE_id_blend_write(writer, &screen->id);
write_previews(writer, screen->preview);
@@ -3108,7 +2622,7 @@ static void write_bone(BlendWriter *writer, Bone *bone)
/* Write ID Properties -- and copy this comment EXACTLY for easy finding
* of library blocks that implement this.*/
if (bone->prop) {
- IDP_WriteProperty(bone->prop, writer);
+ IDP_BlendWrite(writer, bone->prop);
}
/* Write Children */
@@ -3128,10 +2642,10 @@ static void write_armature(BlendWriter *writer, bArmature *arm, const void *id_a
arm->act_edbone = NULL;
BLO_write_id_struct(writer, bArmature, id_address, &arm->id);
- write_iddata(writer, &arm->id);
+ BKE_id_blend_write(writer, &arm->id);
if (arm->adt) {
- write_animdata(writer, arm->adt);
+ BKE_animdata_blend_write(writer, arm->adt);
}
/* Direct data */
@@ -3153,7 +2667,7 @@ static void write_text(BlendWriter *writer, Text *text, const void *id_address)
/* write LibData */
BLO_write_id_struct(writer, Text, id_address, &text->id);
- write_iddata(writer, &text->id);
+ BKE_id_blend_write(writer, &text->id);
if (text->filepath) {
BLO_write_string(writer, text->filepath);
@@ -3176,10 +2690,10 @@ static void write_speaker(BlendWriter *writer, Speaker *spk, const void *id_addr
if (spk->id.us > 0 || BLO_write_is_undo(writer)) {
/* write LibData */
BLO_write_id_struct(writer, Speaker, id_address, &spk->id);
- write_iddata(writer, &spk->id);
+ BKE_id_blend_write(writer, &spk->id);
if (spk->adt) {
- write_animdata(writer, spk->adt);
+ BKE_animdata_blend_write(writer, spk->adt);
}
}
}
@@ -3195,7 +2709,7 @@ static void write_sound(BlendWriter *writer, bSound *sound, const void *id_addre
/* write LibData */
BLO_write_id_struct(writer, bSound, id_address, &sound->id);
- write_iddata(writer, &sound->id);
+ BKE_id_blend_write(writer, &sound->id);
if (sound->packedfile) {
PackedFile *pf = sound->packedfile;
@@ -3210,10 +2724,10 @@ static void write_probe(BlendWriter *writer, LightProbe *prb, const void *id_add
if (prb->id.us > 0 || BLO_write_is_undo(writer)) {
/* write LibData */
BLO_write_id_struct(writer, LightProbe, id_address, &prb->id);
- write_iddata(writer, &prb->id);
+ BKE_id_blend_write(writer, &prb->id);
if (prb->adt) {
- write_animdata(writer, prb->adt);
+ BKE_animdata_blend_write(writer, prb->adt);
}
}
}
@@ -3232,7 +2746,7 @@ static void write_nodetree(BlendWriter *writer, bNodeTree *ntree, const void *id
BLO_write_id_struct(writer, bNodeTree, id_address, &ntree->id);
/* Note that trees directly used by other IDs (materials etc.) are not 'real' ID, they cannot
* be linked, etc., so we write actual id data here only, for 'real' ID trees. */
- write_iddata(writer, &ntree->id);
+ BKE_id_blend_write(writer, &ntree->id);
write_nodetree_nolib(writer, ntree);
}
@@ -3242,7 +2756,7 @@ static void write_brush(BlendWriter *writer, Brush *brush, const void *id_addres
{
if (brush->id.us > 0 || BLO_write_is_undo(writer)) {
BLO_write_id_struct(writer, Brush, id_address, &brush->id);
- write_iddata(writer, &brush->id);
+ BKE_id_blend_write(writer, &brush->id);
if (brush->curve) {
BKE_curvemapping_blend_write(writer, brush->curve);
@@ -3290,7 +2804,7 @@ static void write_palette(BlendWriter *writer, Palette *palette, const void *id_
if (palette->id.us > 0 || BLO_write_is_undo(writer)) {
PaletteColor *color;
BLO_write_id_struct(writer, Palette, id_address, &palette->id);
- write_iddata(writer, &palette->id);
+ BKE_id_blend_write(writer, &palette->id);
for (color = palette->colors.first; color; color = color->next) {
BLO_write_struct(writer, PaletteColor, color);
@@ -3302,7 +2816,7 @@ static void write_paintcurve(BlendWriter *writer, PaintCurve *pc, const void *id
{
if (pc->id.us > 0 || BLO_write_is_undo(writer)) {
BLO_write_id_struct(writer, PaintCurve, id_address, &pc->id);
- write_iddata(writer, &pc->id);
+ BKE_id_blend_write(writer, &pc->id);
BLO_write_struct_array(writer, PaintCurvePoint, pc->tot_points, pc->points);
}
@@ -3358,10 +2872,10 @@ static void write_movieclip(BlendWriter *writer, MovieClip *clip, const void *id
MovieTrackingObject *object;
BLO_write_id_struct(writer, MovieClip, id_address, &clip->id);
- write_iddata(writer, &clip->id);
+ BKE_id_blend_write(writer, &clip->id);
if (clip->adt) {
- write_animdata(writer, clip->adt);
+ BKE_animdata_blend_write(writer, clip->adt);
}
write_movieTracks(writer, &tracking->tracks);
@@ -3387,10 +2901,10 @@ static void write_mask(BlendWriter *writer, Mask *mask, const void *id_address)
MaskLayer *masklay;
BLO_write_id_struct(writer, Mask, id_address, &mask->id);
- write_iddata(writer, &mask->id);
+ BKE_id_blend_write(writer, &mask->id);
if (mask->adt) {
- write_animdata(writer, mask->adt);
+ BKE_animdata_blend_write(writer, mask->adt);
}
for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
@@ -3697,10 +3211,10 @@ static void write_linestyle(BlendWriter *writer,
{
if (linestyle->id.us > 0 || BLO_write_is_undo(writer)) {
BLO_write_id_struct(writer, FreestyleLineStyle, id_address, &linestyle->id);
- write_iddata(writer, &linestyle->id);
+ BKE_id_blend_write(writer, &linestyle->id);
if (linestyle->adt) {
- write_animdata(writer, linestyle->adt);
+ BKE_animdata_blend_write(writer, linestyle->adt);
}
write_linestyle_color_modifiers(writer, &linestyle->color_modifiers);
@@ -3731,7 +3245,7 @@ static void write_cachefile(BlendWriter *writer, CacheFile *cache_file, const vo
BLO_write_id_struct(writer, CacheFile, id_address, &cache_file->id);
if (cache_file->adt) {
- write_animdata(writer, cache_file->adt);
+ BKE_animdata_blend_write(writer, cache_file->adt);
}
}
}
@@ -3739,14 +3253,14 @@ static void write_cachefile(BlendWriter *writer, CacheFile *cache_file, const vo
static void write_workspace(BlendWriter *writer, WorkSpace *workspace, const void *id_address)
{
BLO_write_id_struct(writer, WorkSpace, id_address, &workspace->id);
- write_iddata(writer, &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_WriteProperty(tref->properties, writer);
+ IDP_BlendWrite(writer, tref->properties);
}
}
}
@@ -3754,29 +3268,16 @@ static void write_workspace(BlendWriter *writer, WorkSpace *workspace, const voi
static void write_hair(BlendWriter *writer, Hair *hair, const void *id_address)
{
if (hair->id.us > 0 || BLO_write_is_undo(writer)) {
- CustomDataLayer *players = NULL, players_buff[CD_TEMP_CHUNK_SIZE];
- CustomDataLayer *clayers = NULL, clayers_buff[CD_TEMP_CHUNK_SIZE];
- CustomData_file_write_prepare(&hair->pdata, &players, players_buff, ARRAY_SIZE(players_buff));
- CustomData_file_write_prepare(&hair->cdata, &clayers, clayers_buff, ARRAY_SIZE(clayers_buff));
-
/* Write LibData */
BLO_write_id_struct(writer, Hair, id_address, &hair->id);
- write_iddata(writer, &hair->id);
+ BKE_id_blend_write(writer, &hair->id);
/* Direct data */
- write_customdata(writer, &hair->id, hair->totpoint, &hair->pdata, players, CD_MASK_ALL);
- write_customdata(writer, &hair->id, hair->totcurve, &hair->cdata, clayers, CD_MASK_ALL);
+ CustomData_blend_write(writer, &hair->pdata, hair->totpoint, CD_MASK_ALL, &hair->id);
+ CustomData_blend_write(writer, &hair->cdata, hair->totcurve, CD_MASK_ALL, &hair->id);
BLO_write_pointer_array(writer, hair->totcol, hair->mat);
if (hair->adt) {
- write_animdata(writer, hair->adt);
- }
-
- /* Remove temporary data. */
- if (players && players != players_buff) {
- MEM_freeN(players);
- }
- if (clayers && clayers != clayers_buff) {
- MEM_freeN(clayers);
+ BKE_animdata_blend_write(writer, hair->adt);
}
}
}
@@ -3790,14 +3291,14 @@ static void write_pointcloud(BlendWriter *writer, PointCloud *pointcloud, const
/* Write LibData */
BLO_write_id_struct(writer, PointCloud, id_address, &pointcloud->id);
- write_iddata(writer, &pointcloud->id);
+ BKE_id_blend_write(writer, &pointcloud->id);
/* Direct data */
- write_customdata(
- writer, &pointcloud->id, pointcloud->totpoint, &pointcloud->pdata, players, CD_MASK_ALL);
+ CustomData_blend_write(
+ writer, &pointcloud->pdata, pointcloud->totpoint, CD_MASK_ALL, &pointcloud->id);
BLO_write_pointer_array(writer, pointcloud->totcol, pointcloud->mat);
if (pointcloud->adt) {
- write_animdata(writer, pointcloud->adt);
+ BKE_animdata_blend_write(writer, pointcloud->adt);
}
/* Remove temporary data. */
@@ -3815,12 +3316,12 @@ static void write_volume(BlendWriter *writer, Volume *volume, const void *id_add
/* write LibData */
BLO_write_id_struct(writer, Volume, id_address, &volume->id);
- write_iddata(writer, &volume->id);
+ BKE_id_blend_write(writer, &volume->id);
/* direct data */
BLO_write_pointer_array(writer, volume->totcol, volume->mat);
if (volume->adt) {
- write_animdata(writer, volume->adt);
+ BKE_animdata_blend_write(writer, volume->adt);
}
if (volume->packedfile) {
@@ -3835,10 +3336,10 @@ static void write_simulation(BlendWriter *writer, Simulation *simulation, const
{
if (simulation->id.us > 0 || BLO_write_is_undo(writer)) {
BLO_write_id_struct(writer, Simulation, id_address, &simulation->id);
- write_iddata(writer, &simulation->id);
+ BKE_id_blend_write(writer, &simulation->id);
if (simulation->adt) {
- write_animdata(writer, simulation->adt);
+ BKE_animdata_blend_write(writer, simulation->adt);
}
/* nodetree is integral part of simulation, no libdata */
@@ -3855,21 +3356,11 @@ static void write_simulation(BlendWriter *writer, Simulation *simulation, const
ParticleSimulationState *particle_state = (ParticleSimulationState *)state;
BLO_write_struct(writer, ParticleSimulationState, particle_state);
- CustomDataLayer *layers = NULL;
- CustomDataLayer layers_buff[CD_TEMP_CHUNK_SIZE];
- CustomData_file_write_prepare(
- &particle_state->attributes, &layers, layers_buff, ARRAY_SIZE(layers_buff));
-
- write_customdata(writer,
- &simulation->id,
- particle_state->tot_particles,
- &particle_state->attributes,
- layers,
- CD_MASK_ALL);
-
- if (layers != NULL && layers != layers_buff) {
- MEM_freeN(layers);
- }
+ CustomData_blend_write(writer,
+ &particle_state->attributes,
+ particle_state->tot_particles,
+ CD_MASK_ALL,
+ &simulation->id);
}
else if (STREQ(state->type, SIM_TYPE_NAME_PARTICLE_MESH_EMITTER)) {
ParticleMeshEmitterSimulationState *emitter_state = (ParticleMeshEmitterSimulationState *)
@@ -3925,7 +3416,7 @@ static void write_libraries(WriteData *wd, Main *main)
BlendWriter writer = {wd};
writestruct(wd, ID_LI, Library, 1, main->curlib);
- write_iddata(&writer, &main->curlib->id);
+ BKE_id_blend_write(&writer, &main->curlib->id);
if (main->curlib->packedfile) {
PackedFile *pf = main->curlib->packedfile;
@@ -4130,12 +3621,17 @@ static bool write_file_handle(Main *mainvar,
memcpy(id_buffer, id, idtype_struct_size);
((ID *)id_buffer)->tag = 0;
- /* Those listbase data change every time we add/remove an ID, and also often when renaming
- * one (due to re-sorting). This avoids generating a lot of false 'is changed' detections
- * between undo steps. */
+ /* Those listbase data change every time we add/remove an ID, and also often when
+ * renaming one (due to re-sorting). This avoids generating a lot of false 'is changed'
+ * detections between undo steps. */
((ID *)id_buffer)->prev = NULL;
((ID *)id_buffer)->next = NULL;
+ const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id);
+ if (id_type->blend_write != NULL) {
+ id_type->blend_write(&writer, (ID *)id_buffer, id);
+ }
+
switch ((ID_Type)GS(id->name)) {
case ID_WM:
write_windowmanager(&writer, (wmWindowManager *)id_buffer, id);
@@ -4170,9 +3666,6 @@ static bool write_file_handle(Main *mainvar,
case ID_LA:
write_light(&writer, (Light *)id_buffer, id);
break;
- case ID_LT:
- write_lattice(&writer, (Lattice *)id_buffer, id);
- break;
case ID_VF:
write_vfont(&writer, (VFont *)id_buffer, id);
break;
@@ -4212,9 +3705,6 @@ static bool write_file_handle(Main *mainvar,
case ID_TE:
write_texture(&writer, (Tex *)id_buffer, id);
break;
- case ID_ME:
- write_mesh(&writer, (Mesh *)id_buffer, id);
- break;
case ID_PA:
write_particlesettings(&writer, (ParticleSettings *)id_buffer, id);
break;
@@ -4251,6 +3741,10 @@ static bool write_file_handle(Main *mainvar,
case ID_SIM:
write_simulation(&writer, (Simulation *)id_buffer, id);
break;
+ case ID_ME:
+ case ID_LT:
+ /* Do nothing, handled in IDTypeInfo callback. */
+ break;
case ID_LI:
/* Do nothing, handled below - and should never be reached. */
BLI_assert(0);
@@ -4409,7 +3903,8 @@ bool BLO_write_file(Main *mainvar,
if (remap_mode != BLO_WRITE_PATH_REMAP_NONE) {
if (remap_mode == BLO_WRITE_PATH_REMAP_RELATIVE) {
- /* Make all relative as none of the existing paths can be relative in an unsaved document. */
+ /* Make all relative as none of the existing paths can be relative in an unsaved document.
+ */
if (G.relbase_valid == false) {
remap_mode = BLO_WRITE_PATH_REMAP_RELATIVE_ALL;
}
diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt
index 7368e3d044d..d2b747aa68f 100644
--- a/source/blender/bmesh/CMakeLists.txt
+++ b/source/blender/bmesh/CMakeLists.txt
@@ -138,6 +138,8 @@ set(SRC
tools/bmesh_bevel.h
tools/bmesh_bisect_plane.c
tools/bmesh_bisect_plane.h
+ tools/bmesh_boolean.cc
+ tools/bmesh_boolean.h
tools/bmesh_decimate.h
tools/bmesh_decimate_collapse.c
tools/bmesh_decimate_dissolve.c
@@ -204,6 +206,18 @@ if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
+if(WITH_GMP)
+ add_definitions(-DWITH_GMP)
+
+ list(APPEND INC_SYS
+ ${GMP_INCLUDE_DIRS}
+ )
+
+ list(APPEND LIB
+ ${GMP_LIBRARIES}
+ )
+endif()
+
blender_add_lib(bf_bmesh "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
if(WITH_GTESTS)
diff --git a/source/blender/bmesh/bmesh_tools.h b/source/blender/bmesh/bmesh_tools.h
index ee53cb9804d..daecbac63ab 100644
--- a/source/blender/bmesh/bmesh_tools.h
+++ b/source/blender/bmesh/bmesh_tools.h
@@ -30,6 +30,7 @@ extern "C" {
#include "tools/bmesh_beautify.h"
#include "tools/bmesh_bevel.h"
#include "tools/bmesh_bisect_plane.h"
+#include "tools/bmesh_boolean.h"
#include "tools/bmesh_decimate.h"
#include "tools/bmesh_edgenet.h"
#include "tools/bmesh_edgesplit.h"
diff --git a/source/blender/bmesh/tools/bmesh_boolean.cc b/source/blender/bmesh/tools/bmesh_boolean.cc
new file mode 100644
index 00000000000..5d410d60496
--- /dev/null
+++ b/source/blender/bmesh/tools/bmesh_boolean.cc
@@ -0,0 +1,479 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup bmesh
+ *
+ * Main functions for boolean on a #BMesh (used by the tool and modifier)
+ */
+
+#include "BLI_array.hh"
+#include "BLI_math.h"
+#include "BLI_math_mpq.hh"
+#include "BLI_mesh_boolean.hh"
+#include "BLI_mesh_intersect.hh"
+
+#include "bmesh.h"
+#include "bmesh_boolean.h"
+#include "bmesh_edgesplit.h"
+
+namespace blender {
+namespace meshintersect {
+
+#ifdef WITH_GMP
+
+/** Make a #blender::meshintersect::Mesh from #BMesh bm.
+ * We are given a triangulation of it from the caller via #looptris,
+ * which are looptris_tot triples of loops that together tessellate
+ * the faces of bm.
+ * Return a second #IMesh in *r_triangulated that has the triangulated
+ * mesh, with face "orig" fields that connect the triangles back to
+ * the faces in the returned (polygonal) mesh.
+ */
+static IMesh mesh_from_bm(BMesh *bm,
+ struct BMLoop *(*looptris)[3],
+ const int looptris_tot,
+ IMesh *r_triangulated,
+ IMeshArena *arena)
+{
+ BLI_assert(r_triangulated != nullptr);
+ BM_mesh_elem_index_ensure(bm, BM_VERT | BM_EDGE | BM_FACE);
+ BM_mesh_elem_table_ensure(bm, BM_VERT | BM_EDGE | BM_FACE);
+ /* Account for triangulation and intersects. */
+ const int estimate_num_outv = (3 * bm->totvert) / 2;
+ const int estimate_num_outf = 4 * bm->totface;
+ arena->reserve(estimate_num_outv, estimate_num_outf);
+ Array<const Vert *> vert(bm->totvert);
+ for (int v = 0; v < bm->totvert; ++v) {
+ BMVert *bmv = BM_vert_at_index(bm, v);
+ vert[v] = arena->add_or_find_vert(mpq3(bmv->co[0], bmv->co[1], bmv->co[2]), v);
+ }
+ Array<Face *> face(bm->totface);
+ constexpr int estimated_max_facelen = 100;
+ Vector<const Vert *, estimated_max_facelen> face_vert;
+ Vector<int, estimated_max_facelen> face_edge_orig;
+ for (int f = 0; f < bm->totface; ++f) {
+ BMFace *bmf = BM_face_at_index(bm, f);
+ int flen = bmf->len;
+ face_vert.clear();
+ face_edge_orig.clear();
+ BMLoop *l = bmf->l_first;
+ for (int i = 0; i < flen; ++i) {
+ const Vert *v = vert[BM_elem_index_get(l->v)];
+ face_vert.append(v);
+ int e_index = BM_elem_index_get(l->e);
+ face_edge_orig.append(e_index);
+ l = l->next;
+ }
+ face[f] = arena->add_face(face_vert, f, face_edge_orig);
+ }
+ /* Now do the triangulation mesh.
+ * The loop_tris have accurate v and f members for the triangles,
+ * but their next and e pointers are not correct for the loops
+ * that start added-diagonal edges. */
+ Array<Face *> tri_face(looptris_tot);
+ face_vert.resize(3);
+ face_edge_orig.resize(3);
+ for (int i = 0; i < looptris_tot; ++i) {
+ BMFace *bmf = looptris[i][0]->f;
+ int f = BM_elem_index_get(bmf);
+ for (int j = 0; j < 3; ++j) {
+ BMLoop *l = looptris[i][j];
+ int v_index = BM_elem_index_get(l->v);
+ int e_index;
+ if (l->next->v == looptris[i][(j + 1) % 3]->v) {
+ e_index = BM_elem_index_get(l->e);
+ }
+ else {
+ e_index = NO_INDEX;
+ }
+ face_vert[j] = vert[v_index];
+ face_edge_orig[j] = e_index;
+ }
+ tri_face[i] = arena->add_face(face_vert, f, face_edge_orig);
+ }
+ r_triangulated->set_faces(tri_face);
+ return IMesh(face);
+}
+
+static bool bmvert_attached_to_wire(const BMVert *bmv)
+{
+ /* This is not quite right. It returns true if the only edges
+ * Attached to \a bmv are wire edges. TODO: iterate through edges
+ * attached to \a bmv and check #BM_edge_is_wire. */
+ return BM_vert_is_wire(bmv);
+}
+
+static bool face_has_verts_in_order(BMesh *bm, BMFace *bmf, const BMVert *v1, const BMVert *v2)
+{
+ BMIter liter;
+ BMLoop *l = static_cast<BMLoop *>(BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, bmf));
+ while (l != NULL) {
+ if (l->v == v1 && l->next->v == v2) {
+ return true;
+ }
+ l = static_cast<BMLoop *>(BM_iter_step(&liter));
+ }
+ return false;
+}
+
+/** Use the unused _BM_ELEM_TAG_ALT #BMElem.hflag to mark geometry we will keep. */
+constexpr uint KEEP_FLAG = (1 << 6);
+
+/**
+ * Change #BMesh bm to have the mesh match m_out. Return true if there were any changes at all.
+ * Vertices, faces, and edges in the current bm that are not used in the output are killed,
+ * except we don't kill wire edges and we don't kill hidden geometry.
+ * Also, the #BM_ELEM_TAG header flag is set for those #BMEdge's that come from intersections
+ * resulting from the intersection needed by the Boolean operation.
+ */
+static bool apply_mesh_output_to_bmesh(BMesh *bm, IMesh &m_out)
+{
+ bool any_change = false;
+
+ m_out.populate_vert();
+
+ /* Initially mark all existing verts as "don't keep", except hidden verts
+ * and verts attached to wire edges. */
+ for (int v = 0; v < bm->totvert; ++v) {
+ BMVert *bmv = BM_vert_at_index(bm, v);
+ if (BM_elem_flag_test(bmv, BM_ELEM_HIDDEN) || bmvert_attached_to_wire(bmv)) {
+ BM_elem_flag_enable(bmv, KEEP_FLAG);
+ }
+ else {
+ BM_elem_flag_disable(bmv, KEEP_FLAG);
+ }
+ }
+
+ /* Reuse old or make new #BMVert's, depending on if there's an orig or not.
+ * For those reused, mark them "keep".
+ * Store needed old #BMVert's in new_bmvs first, as the table may be unusable after
+ * creating a new #BMVert. */
+ Array<BMVert *> new_bmvs(m_out.vert_size());
+ for (int v : m_out.vert_index_range()) {
+ const Vert *vertp = m_out.vert(v);
+ int orig = vertp->orig;
+ if (orig != NO_INDEX) {
+ BLI_assert(orig >= 0 && orig < bm->totvert);
+ BMVert *bmv = BM_vert_at_index(bm, orig);
+ new_bmvs[v] = bmv;
+ BM_elem_flag_enable(bmv, KEEP_FLAG);
+ }
+ else {
+ new_bmvs[v] = NULL;
+ }
+ }
+ for (int v : m_out.vert_index_range()) {
+ const Vert *vertp = m_out.vert(v);
+ if (new_bmvs[v] == NULL) {
+ float co[3];
+ const double3 &d_co = vertp->co;
+ for (int i = 0; i < 3; ++i) {
+ co[i] = static_cast<float>(d_co[i]);
+ }
+ BMVert *bmv = BM_vert_create(bm, co, NULL, BM_CREATE_NOP);
+ new_bmvs[v] = bmv;
+ BM_elem_flag_enable(bmv, KEEP_FLAG);
+ any_change = true;
+ }
+ }
+
+ /* Initially mark all existing faces as "don't keep", except hidden faces.
+ * Also, save current #BMFace pointers as creating faces will disturb the table. */
+ Array<BMFace *> old_bmfs(bm->totface);
+ for (int f = 0; f < bm->totface; ++f) {
+ BMFace *bmf = BM_face_at_index(bm, f);
+ old_bmfs[f] = bmf;
+ if (BM_elem_flag_test(bmf, BM_ELEM_HIDDEN)) {
+ BM_elem_flag_enable(bmf, KEEP_FLAG);
+ }
+ else {
+ BM_elem_flag_disable(bmf, KEEP_FLAG);
+ }
+ }
+
+ /* Save the original #BMEdge's so we can use them as examples. */
+ Array<BMEdge *> old_edges(bm->totedge);
+ std::copy(bm->etable, bm->etable + bm->totedge, old_edges.begin());
+
+ /* Reuse or make new #BMFace's, as the faces are identical to old ones or not.
+ * If reusing, mark them as "keep". First find the maximum face length
+ * so we can declare some arrays outside of the face-creating loop. */
+ int maxflen = 0;
+ for (const Face *f : m_out.faces()) {
+ maxflen = max_ii(maxflen, f->size());
+ }
+ Array<BMVert *> face_bmverts(maxflen);
+ Array<BMEdge *> face_bmedges(maxflen);
+ for (const Face *f : m_out.faces()) {
+ const Face &face = *f;
+ int flen = face.size();
+ for (int i = 0; i < flen; ++i) {
+ const Vert *v = face[i];
+ int v_index = m_out.lookup_vert(v);
+ BLI_assert(v_index < new_bmvs.size());
+ face_bmverts[i] = new_bmvs[v_index];
+ }
+ BMFace *bmf = BM_face_exists(face_bmverts.data(), flen);
+ /* #BM_face_exists checks if the face exists with the vertices in either order.
+ * We can only reuse the face if the orientations are the same. */
+ if (bmf != NULL && face_has_verts_in_order(bm, bmf, face_bmverts[0], face_bmverts[1])) {
+ BM_elem_flag_enable(bmf, KEEP_FLAG);
+ }
+ else {
+ int orig = face.orig;
+ BMFace *orig_face;
+ /* There should always be an orig face, but just being extra careful here. */
+ if (orig != NO_INDEX) {
+ orig_face = old_bmfs[orig];
+ }
+ else {
+ orig_face = NULL;
+ }
+ /* Make or find #BMEdge's. */
+ for (int i = 0; i < flen; ++i) {
+ BMVert *bmv1 = face_bmverts[i];
+ BMVert *bmv2 = face_bmverts[(i + 1) % flen];
+ BMEdge *bme = BM_edge_exists(bmv1, bmv2);
+ if (bme == NULL) {
+ BMEdge *orig_edge = NULL;
+ if (face.edge_orig[i] != NO_INDEX) {
+ orig_edge = old_edges[face.edge_orig[i]];
+ }
+ bme = BM_edge_create(bm, bmv1, bmv2, orig_edge, BM_CREATE_NOP);
+ if (orig_edge != NULL) {
+ BM_elem_select_copy(bm, bme, orig_edge);
+ }
+ }
+ face_bmedges[i] = bme;
+ if (face.is_intersect[i]) {
+ BM_elem_flag_enable(bme, BM_ELEM_TAG);
+ }
+ else {
+ BM_elem_flag_disable(bme, BM_ELEM_TAG);
+ }
+ }
+ BMFace *bmf = BM_face_create(
+ bm, face_bmverts.data(), face_bmedges.data(), flen, orig_face, BM_CREATE_NOP);
+ if (orig_face != NULL) {
+ BM_elem_select_copy(bm, bmf, orig_face);
+ }
+ BM_elem_flag_enable(bmf, KEEP_FLAG);
+ /* Now do interpolation of loop data (e.g., UV's) using the example face. */
+ if (orig_face != NULL) {
+ BMIter liter;
+ BMLoop *l = static_cast<BMLoop *>(BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, bmf));
+ while (l != NULL) {
+ BM_loop_interp_from_face(bm, l, orig_face, true, true);
+ l = static_cast<BMLoop *>(BM_iter_step(&liter));
+ }
+ }
+ any_change = true;
+ }
+ }
+
+ /* Now kill the unused faces and verts, and clear flags for kept ones. */
+ /* #BM_ITER_MESH_MUTABLE macro needs type casts for C++, so expand here.
+ * TODO(howard): make some nice C++ iterators for #BMesh. */
+ BMIter iter;
+ BMFace *bmf = static_cast<BMFace *>(BM_iter_new(&iter, bm, BM_FACES_OF_MESH, NULL));
+ while (bmf != NULL) {
+# ifdef DEBUG
+ iter.count = BM_iter_mesh_count(BM_FACES_OF_MESH, bm);
+# endif
+ BMFace *bmf_next = static_cast<BMFace *>(BM_iter_step(&iter));
+ if (BM_elem_flag_test(bmf, KEEP_FLAG)) {
+ BM_elem_flag_disable(bmf, KEEP_FLAG);
+ }
+ else {
+ BM_face_kill_loose(bm, bmf);
+# if 0
+ BM_face_kill(bm, bmf);
+# endif
+ any_change = true;
+ }
+ bmf = bmf_next;
+ }
+ BMVert *bmv = static_cast<BMVert *>(BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL));
+ while (bmv != NULL) {
+# ifdef DEBUG
+ iter.count = BM_iter_mesh_count(BM_VERTS_OF_MESH, bm);
+# endif
+ BMVert *bmv_next = static_cast<BMVert *>(BM_iter_step(&iter));
+ if (BM_elem_flag_test(bmv, KEEP_FLAG)) {
+ BM_elem_flag_disable(bmv, KEEP_FLAG);
+ }
+ else {
+ BM_vert_kill(bm, bmv);
+ any_change = true;
+ }
+ bmv = bmv_next;
+ }
+
+ return any_change;
+}
+
+static bool bmesh_boolean(BMesh *bm,
+ struct BMLoop *(*looptris)[3],
+ const int looptris_tot,
+ int (*test_fn)(BMFace *f, void *user_data),
+ void *user_data,
+ const bool use_self,
+ const bool use_separate_all,
+ const BoolOpType boolean_mode)
+{
+ IMeshArena arena;
+ IMesh m_triangulated;
+ IMesh m_in = mesh_from_bm(bm, looptris, looptris_tot, &m_triangulated, &arena);
+ std::function<int(int)> shape_fn;
+ int nshapes;
+ if (use_self) {
+ /* Unary boolean operation. Want every face where test_fn doesn't return -1. */
+ nshapes = 1;
+ shape_fn = [bm, test_fn, user_data](int f) {
+ BMFace *bmf = BM_face_at_index(bm, f);
+ if (test_fn(bmf, user_data) != -1) {
+ return 0;
+ }
+ return -1;
+ };
+ }
+ else {
+ nshapes = 2;
+ shape_fn = [bm, test_fn, user_data](int f) {
+ BMFace *bmf = BM_face_at_index(bm, f);
+ int test_val = test_fn(bmf, user_data);
+ if (test_val == 0) {
+ return 0;
+ }
+ if (test_val == 1) {
+ return 1;
+ }
+ return -1;
+ };
+ }
+ IMesh m_out = boolean_mesh(
+ m_in, boolean_mode, nshapes, shape_fn, use_self, &m_triangulated, &arena);
+ bool any_change = apply_mesh_output_to_bmesh(bm, m_out);
+ if (use_separate_all) {
+ /* We are supposed to separate all faces that are incident on intersection edges. */
+ BM_mesh_edgesplit(bm, false, true, false);
+ }
+ return any_change;
+}
+
+#endif // WITH_GMP
+
+} // namespace meshintersect
+} // namespace blender
+
+extern "C" {
+/**
+ * Perform the boolean operation specified by boolean_mode on the mesh bm.
+ * The inputs to the boolean operation are either one sub-mesh (if use_self is true),
+ * or two sub-meshes. The sub-meshes are specified by providing a test_fn which takes
+ * a face and the supplied user_data and says with 'side' of the boolean operation
+ * that face is for: 0 for the first side (side A), 1 for the second side (side B),
+ * and -1 if the face is to be ignored completely in the boolean operation.
+ *
+ * If use_self is true, all operations do the same: the sub-mesh is self-intersected
+ * and all pieces inside that result are removed.
+ * Otherwise, the operations can be one of #BMESH_ISECT_BOOLEAN_ISECT, #BMESH_ISECT_BOOLEAN_UNION,
+ * or #BMESH_ISECT_BOOLEAN_DIFFERENCE.
+ *
+ * (The actual library function called to do the boolean is internally capable of handling
+ * n-ary operands, so maybe in the future we can expose that functionality to users.)
+ */
+#ifdef WITH_GMP
+bool BM_mesh_boolean(BMesh *bm,
+ struct BMLoop *(*looptris)[3],
+ const int looptris_tot,
+ int (*test_fn)(BMFace *f, void *user_data),
+ void *user_data,
+ const bool use_self,
+ const int boolean_mode)
+{
+ return blender::meshintersect::bmesh_boolean(
+ bm,
+ looptris,
+ looptris_tot,
+ test_fn,
+ user_data,
+ use_self,
+ false,
+ static_cast<blender::meshintersect::BoolOpType>(boolean_mode));
+}
+
+/**
+ * Perform a Knife Intersection operation on the mesh bm.
+ * There are either one or two operands, the same as described above for BM_mesh_boolean().
+ * If use_separate_all is true, each edge that is created from the intersection should
+ * be used to separate all its incident faces. TODO: implement that.
+ * TODO: need to ensure that "selected/non-selected" flag of original faces gets propagated
+ * to the intersection result faces.
+ */
+bool BM_mesh_boolean_knife(BMesh *bm,
+ struct BMLoop *(*looptris)[3],
+ const int looptris_tot,
+ int (*test_fn)(BMFace *f, void *user_data),
+ void *user_data,
+ const bool use_self,
+ const bool use_separate_all)
+{
+ return blender::meshintersect::bmesh_boolean(bm,
+ looptris,
+ looptris_tot,
+ test_fn,
+ user_data,
+ use_self,
+ use_separate_all,
+ blender::meshintersect::BoolOpType::None);
+}
+#else
+bool BM_mesh_boolean(BMesh *UNUSED(bm),
+ struct BMLoop *(*looptris)[3],
+ const int UNUSED(looptris_tot),
+ int (*test_fn)(BMFace *, void *),
+ void *UNUSED(user_data),
+ const bool UNUSED(use_self),
+ const int UNUSED(boolean_mode))
+{
+ UNUSED_VARS(looptris, test_fn);
+ return false;
+}
+
+/**
+ * Perform a Knife Intersection operation on the mesh bm.
+ * There are either one or two operands, the same as described above for #BM_mesh_boolean().
+ * If use_separate_all is true, each edge that is created from the intersection should
+ * be used to separate all its incident faces. TODO: implement that.
+ * TODO: need to ensure that "selected/non-selected" flag of original faces gets propagated
+ * to the intersection result faces.
+ */
+bool BM_mesh_boolean_knife(BMesh *UNUSED(bm),
+ struct BMLoop *(*looptris)[3],
+ const int UNUSED(looptris_tot),
+ int (*test_fn)(BMFace *, void *),
+ void *UNUSED(user_data),
+ const bool UNUSED(use_self),
+ const bool UNUSED(use_separate_all))
+{
+ UNUSED_VARS(looptris, test_fn);
+ return false;
+}
+#endif
+
+} /* extern "C" */
diff --git a/source/blender/bmesh/tools/bmesh_boolean.h b/source/blender/bmesh/tools/bmesh_boolean.h
new file mode 100644
index 00000000000..d1b4fb2509c
--- /dev/null
+++ b/source/blender/bmesh/tools/bmesh_boolean.h
@@ -0,0 +1,45 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+/** \file
+ * \ingroup bmesh
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+bool BM_mesh_boolean(BMesh *bm,
+ struct BMLoop *(*looptris)[3],
+ const int looptris_tot,
+ int (*test_fn)(BMFace *f, void *user_data),
+ void *user_data,
+ const bool use_self,
+ const int boolean_mode);
+
+bool BM_mesh_boolean_knife(BMesh *bm,
+ struct BMLoop *(*looptris)[3],
+ const int looptris_tot,
+ int (*test_fn)(BMFace *f, void *user_data),
+ void *user_data,
+ const bool use_self,
+ const bool use_separate_all);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/bmesh/tools/bmesh_decimate_collapse.c b/source/blender/bmesh/tools/bmesh_decimate_collapse.c
index a2a949b5567..8d4c831cbc1 100644
--- a/source/blender/bmesh/tools/bmesh_decimate_collapse.c
+++ b/source/blender/bmesh/tools/bmesh_decimate_collapse.c
@@ -117,7 +117,7 @@ static void bm_decim_build_quadrics(BMesh *bm, Quadric *vquadrics)
cross_v3_v3v3(edge_plane, edge_vector, f->no);
copy_v3db_v3fl(edge_plane_db, edge_plane);
- if (normalize_v3_d(edge_plane_db) > (double)FLT_EPSILON) {
+ if (normalize_v3_db(edge_plane_db) > (double)FLT_EPSILON) {
Quadric q;
float center[3];
diff --git a/source/blender/bmesh/tools/bmesh_edgesplit.h b/source/blender/bmesh/tools/bmesh_edgesplit.h
index 4b8c07fc992..4d3db67ef5f 100644
--- a/source/blender/bmesh/tools/bmesh_edgesplit.h
+++ b/source/blender/bmesh/tools/bmesh_edgesplit.h
@@ -20,7 +20,15 @@
* \ingroup bmesh
*/
+#ifdef __cplusplus
+extern "C" {
+#endif
+
void BM_mesh_edgesplit(BMesh *bm,
const bool use_verts,
const bool tag_only,
const bool copy_select);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/depsgraph/DEG_depsgraph.h b/source/blender/depsgraph/DEG_depsgraph.h
index 8f33e9f480d..0ded511b8f8 100644
--- a/source/blender/depsgraph/DEG_depsgraph.h
+++ b/source/blender/depsgraph/DEG_depsgraph.h
@@ -127,6 +127,12 @@ void DEG_graph_id_tag_update(struct Main *bmain,
struct ID *id,
int flag);
+/* Tag all dependency graphs when time has changed. */
+void DEG_time_tag_update(struct Main *bmain);
+
+/* Tag a dependency graph when time has changed. */
+void DEG_graph_time_tag_update(struct Depsgraph *depsgraph);
+
/* Mark a particular datablock type as having changing. This does
* not cause any updates but is used by external render engines to detect if for
* example a datablock was removed. */
@@ -155,8 +161,6 @@ void DEG_evaluate_on_framechange(Depsgraph *graph, float ctime);
/* Data changed recalculation entry point. */
void DEG_evaluate_on_refresh(Depsgraph *graph);
-bool DEG_needs_eval(Depsgraph *graph);
-
/* Editors Integration -------------------------- */
/* Mechanism to allow editors to be informed of depsgraph updates,
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
index e262c880421..670827dc4d8 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
@@ -1129,9 +1129,11 @@ void DepsgraphNodeBuilder::build_rigidbody(Scene *scene)
if (object->rigidbody_object == nullptr) {
continue;
}
- if (object->rigidbody_object->type == RBO_TYPE_PASSIVE) {
+
+ if (!BKE_rigidbody_is_affected_by_simulation(object)) {
continue;
}
+
/* Create operation for flushing results. */
/* Object's transform component - where the rigidbody operation
* lives. */
@@ -1828,7 +1830,7 @@ void DepsgraphNodeBuilder::build_scene_sequencer(Scene *scene)
function_bind(BKE_scene_eval_sequencer_sequences, _1, scene_cow));
/* Make sure data for sequences is in the graph. */
Sequence *seq;
- SEQ_BEGIN (scene->ed, seq) {
+ SEQ_ALL_BEGIN (scene->ed, seq) {
build_idproperties(seq->prop);
if (seq->sound != nullptr) {
build_sound(seq->sound);
@@ -1845,7 +1847,7 @@ void DepsgraphNodeBuilder::build_scene_sequencer(Scene *scene)
}
/* TODO(sergey): Movie clip, scene, camera, mask. */
}
- SEQ_END;
+ SEQ_ALL_END;
}
void DepsgraphNodeBuilder::build_scene_audio(Scene *scene)
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
index c0b692f28c1..5f637a92069 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
@@ -1703,9 +1703,6 @@ void DepsgraphRelationBuilder::build_rigidbody(Scene *scene)
if (object->rigidbody_object == nullptr) {
continue;
}
- if (object->rigidbody_object->type == RBO_TYPE_PASSIVE) {
- continue;
- }
if (object->parent != nullptr && object->parent->rigidbody_object != nullptr &&
object->parent->rigidbody_object->shape == RB_SHAPE_COMPOUND) {
@@ -1716,10 +1713,6 @@ void DepsgraphRelationBuilder::build_rigidbody(Scene *scene)
continue;
}
- OperationKey rb_transform_copy_key(
- &object->id, NodeType::TRANSFORM, OperationCode::RIGIDBODY_TRANSFORM_COPY);
- /* Rigid body synchronization depends on the actual simulation. */
- add_relation(rb_simulate_key, rb_transform_copy_key, "Rigidbody Sim Eval -> RBO Sync");
/* Simulation uses object transformation after parenting and solving constraints. */
OperationKey object_transform_simulation_init_key(
&object->id, NodeType::TRANSFORM, OperationCode::TRANSFORM_SIMULATION_INIT);
@@ -1737,47 +1730,27 @@ void DepsgraphRelationBuilder::build_rigidbody(Scene *scene)
ComponentKey object_geometry_key(&object->id, NodeType::GEOMETRY);
add_relation(object_geometry_key,
rb_simulate_key,
- "Object Geom Eval -> Rigidbody Rebuild",
+ "Object Geom Eval -> Rigidbody Sim Eval",
RELATION_FLAG_GODMODE);
}
+
/* Final transform is whetever solver gave to us. */
- OperationKey object_transform_final_key(
- &object->id, NodeType::TRANSFORM, OperationCode::TRANSFORM_FINAL);
- add_relation(
- rb_transform_copy_key, object_transform_final_key, "Rigidbody Sync -> Transform Final");
- }
- FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
- }
- /* Constraints. */
- if (rbw->constraints != nullptr) {
- FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (rbw->constraints, object) {
- RigidBodyCon *rbc = object->rigidbody_constraint;
- if (rbc == nullptr || rbc->ob1 == nullptr || rbc->ob2 == nullptr) {
- /* When either ob1 or ob2 is nullptr, the constraint doesn't
- * work. */
- continue;
- }
- /* Make sure indirectly linked objects are fully built. */
- build_object(object);
- build_object(rbc->ob1);
- build_object(rbc->ob2);
- /* final result of the constraint object's transform controls how
- * the constraint affects the physics sim for these objects. */
- ComponentKey trans_key(&object->id, NodeType::TRANSFORM);
- if (rbc->ob1->rigidbody_object->type == RBO_TYPE_ACTIVE) {
- OperationKey ob1_key(
- &rbc->ob1->id, NodeType::TRANSFORM, OperationCode::RIGIDBODY_TRANSFORM_COPY);
- /* Constrained-objects sync depends on the constraint-holder. */
- add_relation(trans_key, ob1_key, "RigidBodyConstraint -> RBC.Object_1");
- }
- if (rbc->ob2->rigidbody_object->type == RBO_TYPE_ACTIVE) {
- OperationKey ob2_key(
- &rbc->ob2->id, NodeType::TRANSFORM, OperationCode::RIGIDBODY_TRANSFORM_COPY);
- /* Constrained-objects sync depends on the constraint-holder. */
- add_relation(trans_key, ob2_key, "RigidBodyConstraint -> RBC.Object_2");
+ if (BKE_rigidbody_is_affected_by_simulation(object)) {
+ /* We do not have to update the objects final transform after the simulation if it is
+ * passive or controlled by the animation system in blender.
+ * (Bullet doesn't move the object at all in these cases)
+ */
+ OperationKey rb_transform_copy_key(
+ &object->id, NodeType::TRANSFORM, OperationCode::RIGIDBODY_TRANSFORM_COPY);
+ /* Rigid body synchronization depends on the actual simulation. */
+ add_relation(rb_simulate_key, rb_transform_copy_key, "Rigidbody Sim Eval -> RBO Sync");
+
+ OperationKey object_transform_final_key(
+ &object->id, NodeType::TRANSFORM, OperationCode::TRANSFORM_FINAL);
+ add_relation(rb_transform_copy_key,
+ object_transform_final_key,
+ "Rigidbody Sync -> Transform Final");
}
- /* Ensure that sim depends on this constraint's transform. */
- add_relation(trans_key, rb_simulate_key, "RigidBodyConstraint Transform -> RB Simulation");
}
FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
}
@@ -2688,7 +2661,7 @@ void DepsgraphRelationBuilder::build_scene_sequencer(Scene *scene)
ComponentKey sequencer_key(&scene->id, NodeType::SEQUENCER);
Sequence *seq;
bool has_audio_strips = false;
- SEQ_BEGIN (scene->ed, seq) {
+ SEQ_ALL_BEGIN (scene->ed, seq) {
build_idproperties(seq->prop);
if (seq->sound != nullptr) {
build_sound(seq->sound);
@@ -2714,7 +2687,7 @@ void DepsgraphRelationBuilder::build_scene_sequencer(Scene *scene)
}
/* TODO(sergey): Movie clip, camera, mask. */
}
- SEQ_END;
+ SEQ_ALL_END;
if (has_audio_strips) {
add_relation(sequencer_key, scene_audio_key, "Sequencer -> Audio");
}
diff --git a/source/blender/depsgraph/intern/depsgraph.cc b/source/blender/depsgraph/intern/depsgraph.cc
index 4a9c840dd9f..99804a7cd7d 100644
--- a/source/blender/depsgraph/intern/depsgraph.cc
+++ b/source/blender/depsgraph/intern/depsgraph.cc
@@ -63,7 +63,6 @@ namespace deg {
Depsgraph::Depsgraph(Main *bmain, Scene *scene, ViewLayer *view_layer, eEvaluationMode mode)
: time_source(nullptr),
need_update(true),
- need_update_time(false),
bmain(bmain),
scene(scene),
view_layer(view_layer),
@@ -78,6 +77,8 @@ Depsgraph::Depsgraph(Main *bmain, Scene *scene, ViewLayer *view_layer, eEvaluati
memset(id_type_updated, 0, sizeof(id_type_updated));
memset(id_type_exist, 0, sizeof(id_type_exist));
memset(physics_relations, 0, sizeof(physics_relations));
+
+ add_time_source();
}
Depsgraph::~Depsgraph()
@@ -103,6 +104,11 @@ TimeSourceNode *Depsgraph::find_time_source() const
return time_source;
}
+void Depsgraph::tag_time_source()
+{
+ time_source->tag_update(this, DEG_UPDATE_SOURCE_TIME);
+}
+
IDNode *Depsgraph::find_id_node(const ID *id) const
{
return id_hash.lookup_default(id, nullptr);
diff --git a/source/blender/depsgraph/intern/depsgraph.h b/source/blender/depsgraph/intern/depsgraph.h
index ea579a4958e..e03846f81e2 100644
--- a/source/blender/depsgraph/intern/depsgraph.h
+++ b/source/blender/depsgraph/intern/depsgraph.h
@@ -68,6 +68,7 @@ struct Depsgraph {
TimeSourceNode *add_time_source();
TimeSourceNode *find_time_source() const;
+ void tag_time_source();
IDNode *find_id_node(const ID *id) const;
IDNode *add_id_node(ID *id, ID *id_cow_hint = nullptr);
@@ -121,10 +122,6 @@ struct Depsgraph {
/* Nodes which have been tagged as "directly modified". */
Set<OperationNode *> entry_tags;
- /* Special entry tag for time source. Allows to tag invisible dependency graphs for update when
- * scene frame changes, so then when dependency graph becomes visible it is on a proper state. */
- bool need_update_time;
-
/* Convenience Data ................... */
/* XXX: should be collected after building (if actually needed?) */
diff --git a/source/blender/depsgraph/intern/depsgraph_eval.cc b/source/blender/depsgraph/intern/depsgraph_eval.cc
index 0c116f5863c..1ad3fdbc9da 100644
--- a/source/blender/depsgraph/intern/depsgraph_eval.cc
+++ b/source/blender/depsgraph/intern/depsgraph_eval.cc
@@ -47,38 +47,37 @@
namespace deg = blender::deg;
-/* Evaluate all nodes tagged for updating. */
-void DEG_evaluate_on_refresh(Depsgraph *graph)
+static void deg_flush_updates_and_refresh(deg::Depsgraph *deg_graph)
{
- deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(graph);
- deg_graph->ctime = BKE_scene_frame_get(deg_graph->scene);
- /* Update time in scene. */
+ /* Update the time on the cow scene. */
if (deg_graph->scene_cow) {
BKE_scene_frame_set(deg_graph->scene_cow, deg_graph->ctime);
}
+
deg::deg_graph_flush_updates(deg_graph);
deg::deg_evaluate_on_refresh(deg_graph);
- deg_graph->need_update_time = false;
}
-/* Frame-change happened for root scene that graph belongs to. */
-void DEG_evaluate_on_framechange(Depsgraph *graph, float ctime)
+/* Evaluate all nodes tagged for updating. */
+void DEG_evaluate_on_refresh(Depsgraph *graph)
{
deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(graph);
- deg_graph->ctime = ctime;
- deg_graph->need_update_time = true;
- deg::deg_graph_flush_updates(deg_graph);
- /* Update time in scene. */
- if (deg_graph->scene_cow) {
- BKE_scene_frame_set(deg_graph->scene_cow, deg_graph->ctime);
+ const Scene *scene = DEG_get_input_scene(graph);
+ const float ctime = BKE_scene_frame_get(scene);
+
+ if (ctime != deg_graph->ctime) {
+ deg_graph->tag_time_source();
+ deg_graph->ctime = ctime;
}
- /* Perform recalculation updates. */
- deg::deg_evaluate_on_refresh(deg_graph);
- deg_graph->need_update_time = false;
+
+ deg_flush_updates_and_refresh(deg_graph);
}
-bool DEG_needs_eval(Depsgraph *graph)
+/* Frame-change happened for root scene that graph belongs to. */
+void DEG_evaluate_on_framechange(Depsgraph *graph, float ctime)
{
deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(graph);
- return !deg_graph->entry_tags.is_empty() || deg_graph->need_update_time;
+ deg_graph->tag_time_source();
+ deg_graph->ctime = ctime;
+ deg_flush_updates_and_refresh(deg_graph);
}
diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc
index 4a2d47f9379..868f88d8fcd 100644
--- a/source/blender/depsgraph/intern/depsgraph_tag.cc
+++ b/source/blender/depsgraph/intern/depsgraph_tag.cc
@@ -66,6 +66,7 @@
#include "intern/node/deg_node_factory.h"
#include "intern/node/deg_node_id.h"
#include "intern/node/deg_node_operation.h"
+#include "intern/node/deg_node_time.h"
namespace deg = blender::deg;
@@ -230,9 +231,6 @@ void depsgraph_tag_to_component_opcode(const ID *id,
case ID_RECALC_SOURCE:
*component_type = NodeType::PARAMETERS;
break;
- case ID_RECALC_TIME:
- BLI_assert(!"Should be handled outside of this function");
- break;
case ID_RECALC_ALL:
case ID_RECALC_PSYS_ALL:
BLI_assert(!"Should not happen");
@@ -372,12 +370,6 @@ void graph_id_tag_update_single_flag(Main *bmain,
}
return;
}
- if (tag == ID_RECALC_TIME) {
- if (graph != nullptr) {
- graph->need_update_time = true;
- }
- return;
- }
/* Get description of what is to be tagged. */
NodeType component_type;
OperationCode operation_code;
@@ -462,8 +454,8 @@ const char *update_source_as_string(eUpdateSource source)
int deg_recalc_flags_for_legacy_zero()
{
- return ID_RECALC_ALL & ~(ID_RECALC_PSYS_ALL | ID_RECALC_ANIMATION | ID_RECALC_SOURCE |
- ID_RECALC_TIME | ID_RECALC_EDITORS);
+ return ID_RECALC_ALL &
+ ~(ID_RECALC_PSYS_ALL | ID_RECALC_ANIMATION | ID_RECALC_SOURCE | ID_RECALC_EDITORS);
}
int deg_recalc_flags_effective(Depsgraph *graph, int flags)
@@ -734,8 +726,6 @@ const char *DEG_update_tag_as_string(IDRecalcFlag flag)
return "AUDIO";
case ID_RECALC_PARAMETERS:
return "PARAMETERS";
- case ID_RECALC_TIME:
- return "TIME";
case ID_RECALC_SOURCE:
return "SOURCE";
case ID_RECALC_ALL:
@@ -772,6 +762,19 @@ void DEG_graph_id_tag_update(struct Main *bmain,
deg::graph_id_tag_update(bmain, graph, id, flag, deg::DEG_UPDATE_SOURCE_USER_EDIT);
}
+void DEG_time_tag_update(struct Main *bmain)
+{
+ for (deg::Depsgraph *depsgraph : deg::get_all_registered_graphs(bmain)) {
+ DEG_graph_time_tag_update(reinterpret_cast<::Depsgraph *>(depsgraph));
+ }
+}
+
+void DEG_graph_time_tag_update(struct Depsgraph *depsgraph)
+{
+ deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(depsgraph);
+ deg_graph->tag_time_source();
+}
+
/* Mark a particular datablock type as having changing. */
void DEG_graph_id_type_tag(Depsgraph *depsgraph, short id_type)
{
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc
index dea23c9f96d..5ccdcbec858 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc
@@ -357,14 +357,9 @@ void deg_graph_flush_updates(Depsgraph *graph)
BLI_assert(graph != nullptr);
Main *bmain = graph->bmain;
+ graph->time_source->flush_update_tag(graph);
+
/* Nothing to update, early out. */
- if (graph->need_update_time) {
- const Scene *scene_orig = graph->scene;
- const float ctime = BKE_scene_frame_get(scene_orig);
- TimeSourceNode *time_source = graph->find_time_source();
- graph->ctime = ctime;
- time_source->tag_update(graph, DEG_UPDATE_SOURCE_TIME);
- }
if (graph->entry_tags.is_empty()) {
return;
}
@@ -412,6 +407,8 @@ void deg_graph_clear_tags(Depsgraph *graph)
}
/* Clear any entry tags which haven't been flushed. */
graph->entry_tags.clear();
+
+ graph->time_source->tagged_for_update = false;
}
} // namespace deg
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.cc
index ba7d20c0ba8..4d79480a5ad 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.cc
@@ -41,7 +41,7 @@ SequencerBackup::SequencerBackup(const Depsgraph *depsgraph) : depsgraph(depsgra
void SequencerBackup::init_from_scene(Scene *scene)
{
Sequence *sequence;
- SEQ_BEGIN (scene->ed, sequence) {
+ SEQ_ALL_BEGIN (scene->ed, sequence) {
SequenceBackup sequence_backup(depsgraph);
sequence_backup.init_from_sequence(sequence);
if (!sequence_backup.isEmpty()) {
@@ -50,13 +50,13 @@ void SequencerBackup::init_from_scene(Scene *scene)
sequences_backup.add(session_uuid, sequence_backup);
}
}
- SEQ_END;
+ SEQ_ALL_END;
}
void SequencerBackup::restore_to_scene(Scene *scene)
{
Sequence *sequence;
- SEQ_BEGIN (scene->ed, sequence) {
+ SEQ_ALL_BEGIN (scene->ed, sequence) {
const SessionUUID &session_uuid = sequence->runtime.session_uuid;
BLI_assert(BLI_session_uuid_is_generated(&session_uuid));
SequenceBackup *sequence_backup = sequences_backup.lookup_ptr(session_uuid);
@@ -64,7 +64,7 @@ void SequencerBackup::restore_to_scene(Scene *scene)
sequence_backup->restore_to_sequence(sequence);
}
}
- SEQ_END;
+ SEQ_ALL_END;
/* Cleanup audio while the scene is still known. */
for (SequenceBackup &sequence_backup : sequences_backup.values()) {
if (sequence_backup.scene_sound != nullptr) {
diff --git a/source/blender/depsgraph/intern/node/deg_node_time.cc b/source/blender/depsgraph/intern/node/deg_node_time.cc
index af931fbae34..4f7f70fef33 100644
--- a/source/blender/depsgraph/intern/node/deg_node_time.cc
+++ b/source/blender/depsgraph/intern/node/deg_node_time.cc
@@ -31,8 +31,16 @@
namespace blender {
namespace deg {
-void TimeSourceNode::tag_update(Depsgraph *graph, eUpdateSource /*source*/)
+void TimeSourceNode::tag_update(Depsgraph * /*graph*/, eUpdateSource /*source*/)
{
+ tagged_for_update = true;
+}
+
+void TimeSourceNode::flush_update_tag(Depsgraph *graph)
+{
+ if (!tagged_for_update) {
+ return;
+ }
for (Relation *rel : outlinks) {
Node *node = rel->to;
node->tag_update(graph, DEG_UPDATE_SOURCE_TIME);
diff --git a/source/blender/depsgraph/intern/node/deg_node_time.h b/source/blender/depsgraph/intern/node/deg_node_time.h
index fe17684abb0..79ad92f336f 100644
--- a/source/blender/depsgraph/intern/node/deg_node_time.h
+++ b/source/blender/depsgraph/intern/node/deg_node_time.h
@@ -30,10 +30,14 @@ namespace deg {
/* Time Source Node. */
struct TimeSourceNode : public Node {
+ bool tagged_for_update = false;
+
// TODO: evaluate() operation needed
virtual void tag_update(Depsgraph *graph, eUpdateSource source) override;
+ void flush_update_tag(Depsgraph *graph);
+
DEG_DEPSNODE_DECLARE;
};
diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt
index f85b03dc517..83d39b606fe 100644
--- a/source/blender/draw/CMakeLists.txt
+++ b/source/blender/draw/CMakeLists.txt
@@ -419,6 +419,28 @@ if(WITH_XR_OPENXR)
add_definitions(-DWITH_XR_OPENXR)
endif()
+if(WITH_GTESTS)
+ if(WITH_OPENGL_DRAW_TESTS)
+ add_definitions(-DWITH_OPENGL_DRAW_TESTS)
+ endif()
+endif()
+
add_definitions(${GL_DEFINITIONS})
blender_add_lib(bf_draw "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
+
+if(WITH_GTESTS)
+ if(WITH_OPENGL_DRAW_TESTS)
+ set(TEST_SRC
+ tests/shaders_test.cc
+ )
+ set(TEST_INC
+ "../../../intern/ghost/"
+ )
+ set(TEST_LIB
+ bf_draw
+ )
+ include(GTestTesting)
+ blender_add_test_lib(bf_draw_tests "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB};${TEST_LIB}")
+ endif()
+endif() \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee/eevee_data.c b/source/blender/draw/engines/eevee/eevee_data.c
index e18c43fc643..5c4ee015c86 100644
--- a/source/blender/draw/engines/eevee/eevee_data.c
+++ b/source/blender/draw/engines/eevee/eevee_data.c
@@ -254,7 +254,7 @@ EEVEE_ViewLayerData *EEVEE_view_layer_data_get(void)
static void eevee_view_layer_init(EEVEE_ViewLayerData *sldata)
{
- sldata->common_ubo = DRW_uniformbuffer_create(sizeof(sldata->common_data), NULL);
+ sldata->common_ubo = GPU_uniformbuf_create(sizeof(sldata->common_data));
}
EEVEE_ViewLayerData *EEVEE_view_layer_data_ensure_ex(struct ViewLayer *view_layer)
diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c
index 72f008ea66a..6ab267ceb03 100644
--- a/source/blender/draw/engines/eevee/eevee_engine.c
+++ b/source/blender/draw/engines/eevee/eevee_engine.c
@@ -267,7 +267,7 @@ static void eevee_draw_scene(void *vedata)
/* Set ray type. */
sldata->common_data.ray_type = EEVEE_RAY_CAMERA;
sldata->common_data.ray_depth = 0.0f;
- DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data);
+ GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data);
GPU_framebuffer_bind(fbl->main_fb);
eGPUFrameBufferBits clear_bits = GPU_DEPTH_BIT;
diff --git a/source/blender/draw/engines/eevee/eevee_lightcache.c b/source/blender/draw/engines/eevee/eevee_lightcache.c
index a785d27c2db..088a08fb51a 100644
--- a/source/blender/draw/engines/eevee/eevee_lightcache.c
+++ b/source/blender/draw/engines/eevee/eevee_lightcache.c
@@ -942,7 +942,7 @@ static void eevee_lightbake_render_world_sample(void *ved, void *user_data)
sldata->common_data.ray_type = EEVEE_RAY_GLOSSY;
sldata->common_data.ray_depth = 1;
- DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data);
+ GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data);
EEVEE_lightbake_render_world(sldata, vedata, lbake->rt_fb);
EEVEE_lightbake_filter_glossy(sldata,
vedata,
@@ -956,7 +956,7 @@ static void eevee_lightbake_render_world_sample(void *ved, void *user_data)
sldata->common_data.ray_type = EEVEE_RAY_DIFFUSE;
sldata->common_data.ray_depth = 1;
- DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data);
+ GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data);
EEVEE_lightbake_render_world(sldata, vedata, lbake->rt_fb);
EEVEE_lightbake_filter_diffuse(sldata, vedata, lbake->rt_color, lbake->store_fb, 0, 1.0f);
@@ -1079,7 +1079,7 @@ static void eevee_lightbake_render_grid_sample(void *ved, void *user_data)
if (lbake->bounce_curr == 0) {
common_data->prb_num_render_grid = 0;
}
- DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data);
+ GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data);
EEVEE_lightbake_render_scene(sldata, vedata, lbake->rt_fb, pos, prb->clipsta, prb->clipend);
@@ -1145,7 +1145,7 @@ static void eevee_lightbake_render_probe_sample(void *ved, void *user_data)
common_data->prb_num_render_cube = 0;
common_data->ray_type = EEVEE_RAY_GLOSSY;
common_data->ray_depth = 1;
- DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data);
+ GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data);
EEVEE_lightbake_render_scene(
sldata, vedata, lbake->rt_fb, eprobe->position, prb->clipsta, prb->clipend);
@@ -1430,7 +1430,7 @@ void EEVEE_lightbake_update_world_quick(EEVEE_ViewLayerData *sldata,
sldata->common_data.ray_type = EEVEE_RAY_GLOSSY;
sldata->common_data.ray_depth = 1;
- DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data);
+ GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data);
EEVEE_lightbake_render_world(sldata, vedata, lbake.rt_fb);
EEVEE_lightbake_filter_glossy(sldata,
vedata,
@@ -1444,7 +1444,7 @@ void EEVEE_lightbake_update_world_quick(EEVEE_ViewLayerData *sldata,
sldata->common_data.ray_type = EEVEE_RAY_DIFFUSE;
sldata->common_data.ray_depth = 1;
- DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data);
+ GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data);
EEVEE_lightbake_render_world(sldata, vedata, lbake.rt_fb);
EEVEE_lightbake_filter_diffuse(sldata, vedata, lbake.rt_color, lbake.store_fb, 0, 1.0f);
diff --git a/source/blender/draw/engines/eevee/eevee_lightprobes.c b/source/blender/draw/engines/eevee/eevee_lightprobes.c
index 0f4a9dc79b6..63cc07c321e 100644
--- a/source/blender/draw/engines/eevee/eevee_lightprobes.c
+++ b/source/blender/draw/engines/eevee/eevee_lightprobes.c
@@ -39,6 +39,7 @@
#include "GPU_extensions.h"
#include "GPU_material.h"
#include "GPU_texture.h"
+#include "GPU_uniform_buffer.h"
#include "DEG_depsgraph_query.h"
@@ -203,10 +204,9 @@ void EEVEE_lightprobes_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
if (!sldata->probes) {
sldata->probes = MEM_callocN(sizeof(EEVEE_LightProbesInfo), "EEVEE_LightProbesInfo");
- sldata->probe_ubo = DRW_uniformbuffer_create(sizeof(EEVEE_LightProbe) * MAX_PROBE, NULL);
- sldata->grid_ubo = DRW_uniformbuffer_create(sizeof(EEVEE_LightGrid) * MAX_GRID, NULL);
- sldata->planar_ubo = DRW_uniformbuffer_create(sizeof(EEVEE_PlanarReflection) * MAX_PLANAR,
- NULL);
+ sldata->probe_ubo = GPU_uniformbuf_create(sizeof(EEVEE_LightProbe) * MAX_PROBE);
+ sldata->grid_ubo = GPU_uniformbuf_create(sizeof(EEVEE_LightGrid) * MAX_GRID);
+ sldata->planar_ubo = GPU_uniformbuf_create(sizeof(EEVEE_PlanarReflection) * MAX_PLANAR);
}
common_data->prb_num_planar = 0;
@@ -724,8 +724,8 @@ void EEVEE_lightprobes_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *ved
eevee_lightprobes_extract_from_cache(sldata->probes, light_cache);
- DRW_uniformbuffer_update(sldata->probe_ubo, &sldata->probes->probe_data);
- DRW_uniformbuffer_update(sldata->grid_ubo, &sldata->probes->grid_data);
+ GPU_uniformbuf_update(sldata->probe_ubo, &sldata->probes->probe_data);
+ GPU_uniformbuf_update(sldata->grid_ubo, &sldata->probes->grid_data);
/* For shading, save max level of the octahedron map */
sldata->common_data.prb_lod_cube_max = (float)light_cache->mips_len;
@@ -1241,7 +1241,7 @@ void EEVEE_lightprobes_refresh_planar(EEVEE_ViewLayerData *sldata, EEVEE_Data *v
common_data->ray_type = EEVEE_RAY_GLOSSY;
common_data->ray_depth = 1.0f;
- DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data);
+ GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data);
/* Rendering happens here! */
eevee_lightbake_render_scene_to_planars(sldata, vedata);
@@ -1249,7 +1249,7 @@ void EEVEE_lightprobes_refresh_planar(EEVEE_ViewLayerData *sldata, EEVEE_Data *v
/* Make sure no additional visibility check runs after this. */
pinfo->vis_data.collection = NULL;
- DRW_uniformbuffer_update(sldata->planar_ubo, &sldata->probes->planar_data);
+ GPU_uniformbuf_update(sldata->planar_ubo, &sldata->probes->planar_data);
/* Restore */
common_data->prb_num_planar = pinfo->num_planar;
diff --git a/source/blender/draw/engines/eevee/eevee_lights.c b/source/blender/draw/engines/eevee/eevee_lights.c
index c6e8bac0949..b7112c07cab 100644
--- a/source/blender/draw/engines/eevee/eevee_lights.c
+++ b/source/blender/draw/engines/eevee/eevee_lights.c
@@ -214,5 +214,5 @@ void EEVEE_lights_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *UNUSED(v
sldata->common_data.la_num_light = linfo->num_light;
- DRW_uniformbuffer_update(sldata->light_ubo, &linfo->light_data);
+ GPU_uniformbuf_update(sldata->light_ubo, &linfo->light_data);
}
diff --git a/source/blender/draw/engines/eevee/eevee_lookdev.c b/source/blender/draw/engines/eevee/eevee_lookdev.c
index f79d90500bd..6253203bab6 100644
--- a/source/blender/draw/engines/eevee/eevee_lookdev.c
+++ b/source/blender/draw/engines/eevee/eevee_lookdev.c
@@ -275,7 +275,7 @@ void EEVEE_lookdev_draw(EEVEE_Data *vedata)
common->ao_dist = 0.0f;
common->ao_factor = 0.0f;
common->ao_settings = 0.0f;
- DRW_uniformbuffer_update(sldata->common_ubo, common);
+ GPU_uniformbuf_update(sldata->common_ubo, common);
/* override matrices */
float winmat[4][4], viewmat[4][4];
diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c
index fb07208be47..b6e20416dfb 100644
--- a/source/blender/draw/engines/eevee/eevee_materials.c
+++ b/source/blender/draw/engines/eevee/eevee_materials.c
@@ -243,33 +243,34 @@ void EEVEE_materials_init(EEVEE_ViewLayerData *sldata,
{
/* Create RenderPass UBO */
if (sldata->renderpass_ubo.combined == NULL) {
- sldata->renderpass_ubo.combined = DRW_uniformbuffer_create(
- sizeof(EEVEE_RenderPassData),
- &(const EEVEE_RenderPassData){true, true, true, true, true, false, false});
-
- sldata->renderpass_ubo.diff_color = DRW_uniformbuffer_create(
- sizeof(EEVEE_RenderPassData),
- &(const EEVEE_RenderPassData){true, false, false, false, false, true, false});
-
- sldata->renderpass_ubo.diff_light = DRW_uniformbuffer_create(
- sizeof(EEVEE_RenderPassData),
- &(const EEVEE_RenderPassData){true, true, false, false, false, false, false});
-
- sldata->renderpass_ubo.spec_color = DRW_uniformbuffer_create(
- sizeof(EEVEE_RenderPassData),
- &(const EEVEE_RenderPassData){false, false, true, false, false, false, false});
-
- sldata->renderpass_ubo.spec_light = DRW_uniformbuffer_create(
- sizeof(EEVEE_RenderPassData),
- &(const EEVEE_RenderPassData){false, false, true, true, false, false, false});
-
- sldata->renderpass_ubo.emit = DRW_uniformbuffer_create(
- sizeof(EEVEE_RenderPassData),
- &(const EEVEE_RenderPassData){false, false, false, false, true, false, false});
-
- sldata->renderpass_ubo.environment = DRW_uniformbuffer_create(
- sizeof(EEVEE_RenderPassData),
- &(const EEVEE_RenderPassData){true, true, true, true, true, false, true});
+ EEVEE_RenderPassData data;
+ data = (EEVEE_RenderPassData){true, true, true, true, true, false, false};
+ sldata->renderpass_ubo.combined = GPU_uniformbuf_create_ex(
+ sizeof(data), &data, "renderpass_ubo.combined");
+
+ data = (EEVEE_RenderPassData){true, false, false, false, false, true, false};
+ sldata->renderpass_ubo.diff_color = GPU_uniformbuf_create_ex(
+ sizeof(data), &data, "renderpass_ubo.diff_color");
+
+ data = (EEVEE_RenderPassData){true, true, false, false, false, false, false};
+ sldata->renderpass_ubo.diff_light = GPU_uniformbuf_create_ex(
+ sizeof(data), &data, "renderpass_ubo.diff_light");
+
+ data = (EEVEE_RenderPassData){false, false, true, false, false, false, false};
+ sldata->renderpass_ubo.spec_color = GPU_uniformbuf_create_ex(
+ sizeof(data), &data, "renderpass_ubo.spec_color");
+
+ data = (EEVEE_RenderPassData){false, false, true, true, false, false, false};
+ sldata->renderpass_ubo.spec_light = GPU_uniformbuf_create_ex(
+ sizeof(data), &data, "renderpass_ubo.spec_light");
+
+ data = (EEVEE_RenderPassData){false, false, false, false, true, false, false};
+ sldata->renderpass_ubo.emit = GPU_uniformbuf_create_ex(
+ sizeof(data), &data, "renderpass_ubo.emit");
+
+ data = (EEVEE_RenderPassData){true, true, true, true, true, false, true};
+ sldata->renderpass_ubo.environment = GPU_uniformbuf_create_ex(
+ sizeof(data), &data, "renderpass_ubo.environment");
}
/* Used combined pass by default. */
@@ -962,7 +963,7 @@ static void material_renderpass_accumulate(EEVEE_FramebufferList *fbl,
DRWPass *renderpass,
EEVEE_PrivateData *pd,
GPUTexture *output_tx,
- struct GPUUniformBuffer *renderpass_option_ubo)
+ struct GPUUniformBuf *renderpass_option_ubo)
{
GPU_framebuffer_texture_attach(fbl->material_accum_fb, output_tx, 0, 0);
GPU_framebuffer_bind(fbl->material_accum_fb);
diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h
index 40d7676c38c..b25f21ce929 100644
--- a/source/blender/draw/engines/eevee/eevee_private.h
+++ b/source/blender/draw/engines/eevee/eevee_private.h
@@ -32,6 +32,10 @@
#include "BKE_camera.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
struct EEVEE_ShadowCasterBuffer;
struct GPUFrameBuffer;
struct Object;
@@ -818,9 +822,9 @@ typedef struct EEVEE_ViewLayerData {
/* Lights */
struct EEVEE_LightsInfo *lights;
- struct GPUUniformBuffer *light_ubo;
- struct GPUUniformBuffer *shadow_ubo;
- struct GPUUniformBuffer *shadow_samples_ubo;
+ struct GPUUniformBuf *light_ubo;
+ struct GPUUniformBuf *shadow_ubo;
+ struct GPUUniformBuf *shadow_samples_ubo;
struct GPUFrameBuffer *shadow_fb;
@@ -832,24 +836,24 @@ typedef struct EEVEE_ViewLayerData {
/* Probes */
struct EEVEE_LightProbesInfo *probes;
- struct GPUUniformBuffer *probe_ubo;
- struct GPUUniformBuffer *grid_ubo;
- struct GPUUniformBuffer *planar_ubo;
+ struct GPUUniformBuf *probe_ubo;
+ struct GPUUniformBuf *grid_ubo;
+ struct GPUUniformBuf *planar_ubo;
/* Material Render passes */
struct {
- struct GPUUniformBuffer *combined;
- struct GPUUniformBuffer *environment;
- struct GPUUniformBuffer *diff_color;
- struct GPUUniformBuffer *diff_light;
- struct GPUUniformBuffer *spec_color;
- struct GPUUniformBuffer *spec_light;
- struct GPUUniformBuffer *emit;
+ struct GPUUniformBuf *combined;
+ struct GPUUniformBuf *environment;
+ struct GPUUniformBuf *diff_color;
+ struct GPUUniformBuf *diff_light;
+ struct GPUUniformBuf *spec_color;
+ struct GPUUniformBuf *spec_light;
+ struct GPUUniformBuf *emit;
} renderpass_ubo;
/* Common Uniform Buffer */
struct EEVEE_CommonUniformBuffer common_data;
- struct GPUUniformBuffer *common_ubo;
+ struct GPUUniformBuf *common_ubo;
struct LightCache *fallback_lightcache;
@@ -953,7 +957,7 @@ typedef struct EEVEE_PrivateData {
GPUTexture *renderpass_col_input;
GPUTexture *renderpass_light_input;
/* Renderpass ubo reference used by material pass. */
- struct GPUUniformBuffer *renderpass_ubo;
+ struct GPUUniformBuf *renderpass_ubo;
/** For rendering shadows. */
struct DRWView *cube_views[6];
/** For rendering probes. */
@@ -1363,3 +1367,7 @@ static const float cubefacemat[6][4][4] = {
{0.0f, 0.0f, 1.0f, 0.0f},
{0.0f, 0.0f, 0.0f, 1.0f}},
};
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/draw/engines/eevee/eevee_render.c b/source/blender/draw/engines/eevee/eevee_render.c
index 65a856c39e1..2351b06db98 100644
--- a/source/blender/draw/engines/eevee/eevee_render.c
+++ b/source/blender/draw/engines/eevee/eevee_render.c
@@ -574,7 +574,7 @@ void EEVEE_render_draw(EEVEE_Data *vedata, RenderEngine *engine, RenderLayer *rl
/* Set ray type. */
sldata->common_data.ray_type = EEVEE_RAY_CAMERA;
sldata->common_data.ray_depth = 0.0f;
- DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data);
+ GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data);
GPU_framebuffer_bind(fbl->main_fb);
GPU_framebuffer_clear_color_depth_stencil(fbl->main_fb, clear_col, clear_depth, clear_stencil);
diff --git a/source/blender/draw/engines/eevee/eevee_shadows.c b/source/blender/draw/engines/eevee/eevee_shadows.c
index 71a4da9fcab..79d89ab620c 100644
--- a/source/blender/draw/engines/eevee/eevee_shadows.c
+++ b/source/blender/draw/engines/eevee/eevee_shadows.c
@@ -71,8 +71,8 @@ void EEVEE_shadows_init(EEVEE_ViewLayerData *sldata)
if (!sldata->lights) {
sldata->lights = MEM_callocN(sizeof(EEVEE_LightsInfo), "EEVEE_LightsInfo");
- sldata->light_ubo = DRW_uniformbuffer_create(sizeof(EEVEE_Light) * MAX_LIGHT, NULL);
- sldata->shadow_ubo = DRW_uniformbuffer_create(shadow_ubo_size, NULL);
+ sldata->light_ubo = GPU_uniformbuf_create_ex(sizeof(EEVEE_Light) * MAX_LIGHT, NULL, "evLight");
+ sldata->shadow_ubo = GPU_uniformbuf_create_ex(shadow_ubo_size, NULL, "evShadow");
for (int i = 0; i < 2; i++) {
sldata->shcasters_buffers[i].bbox = MEM_callocN(
@@ -338,7 +338,7 @@ void EEVEE_shadows_draw(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, DRWView
if (any_visible) {
sldata->common_data.ray_type = EEVEE_RAY_SHADOW;
- DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data);
+ GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data);
}
DRW_stats_group_start("Cube Shadow Maps");
@@ -361,11 +361,11 @@ void EEVEE_shadows_draw(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, DRWView
DRW_view_set_active(view);
- DRW_uniformbuffer_update(sldata->shadow_ubo, &linfo->shadow_data); /* Update all data at once */
+ GPU_uniformbuf_update(sldata->shadow_ubo, &linfo->shadow_data); /* Update all data at once */
if (any_visible) {
sldata->common_data.ray_type = saved_ray_type;
- DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data);
+ GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data);
}
}
diff --git a/source/blender/draw/engines/eevee/eevee_subsurface.c b/source/blender/draw/engines/eevee/eevee_subsurface.c
index 74fb7ac99b7..5daa6e7c622 100644
--- a/source/blender/draw/engines/eevee/eevee_subsurface.c
+++ b/source/blender/draw/engines/eevee/eevee_subsurface.c
@@ -212,7 +212,7 @@ void EEVEE_subsurface_add_pass(EEVEE_ViewLayerData *sldata,
GPUTexture **depth_src = GPU_depth_blitting_workaround() ? &effects->sss_stencil : &dtxl->depth;
struct GPUTexture *sss_tex_profile = NULL;
- struct GPUUniformBuffer *sss_profile = GPU_material_sss_profile_get(
+ struct GPUUniformBuf *sss_profile = GPU_material_sss_profile_get(
gpumat, stl->effects->sss_sample_count, &sss_tex_profile);
if (!sss_profile) {
diff --git a/source/blender/draw/engines/eevee/eevee_temporal_sampling.c b/source/blender/draw/engines/eevee/eevee_temporal_sampling.c
index 5976a9505e8..4f2cfe224c2 100644
--- a/source/blender/draw/engines/eevee/eevee_temporal_sampling.c
+++ b/source/blender/draw/engines/eevee/eevee_temporal_sampling.c
@@ -247,8 +247,11 @@ int EEVEE_temporal_sampling_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data
effects->taa_total_sample = first_sample_only ? 1 : scene_eval->eevee.taa_samples;
MAX2(effects->taa_total_sample, 0);
- DRW_view_persmat_get(NULL, persmat, false);
- view_is_valid = view_is_valid && compare_m4m4(persmat, effects->prev_drw_persmat, FLT_MIN);
+ /* Motion blur steps could reset the sampling when camera is animated (see T79970). */
+ if (!DRW_state_is_scene_render()) {
+ DRW_view_persmat_get(NULL, persmat, false);
+ view_is_valid = view_is_valid && compare_m4m4(persmat, effects->prev_drw_persmat, FLT_MIN);
+ }
/* Prevent ghosting from probe data. */
view_is_valid = view_is_valid && (effects->prev_drw_support == DRW_state_draw_support()) &&
diff --git a/source/blender/draw/engines/eevee/eevee_volumes.c b/source/blender/draw/engines/eevee/eevee_volumes.c
index f8c7a6e16db..9e7e545167a 100644
--- a/source/blender/draw/engines/eevee/eevee_volumes.c
+++ b/source/blender/draw/engines/eevee/eevee_volumes.c
@@ -372,6 +372,8 @@ void EEVEE_volumes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
/* If no world or volume material is present just clear the buffer with this drawcall */
grp = DRW_shgroup_create(e_data.volumetric_clear_sh, psl->volumetric_world_ps);
DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
+ DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo);
+ DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo);
DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
DRW_shgroup_call_procedural_triangles(grp, NULL, common_data->vol_tex_size[2]);
@@ -656,6 +658,7 @@ void EEVEE_volumes_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
DRW_shgroup_uniform_texture_ref(grp, "inTransmittance", &txl->volume_transmit);
DRW_shgroup_uniform_texture_ref(grp, "inSceneDepth", &e_data.depth_src);
DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
+ DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo);
DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
diff --git a/source/blender/draw/engines/gpencil/gpencil_draw_data.c b/source/blender/draw/engines/gpencil/gpencil_draw_data.c
index 51152475a06..7faf426c4e0 100644
--- a/source/blender/draw/engines/gpencil/gpencil_draw_data.c
+++ b/source/blender/draw/engines/gpencil/gpencil_draw_data.c
@@ -30,7 +30,7 @@
#include "BLI_math_color.h"
#include "BLI_memblock.h"
-#include "GPU_uniformbuffer.h"
+#include "GPU_uniform_buffer.h"
#include "IMB_imbuf_types.h"
@@ -46,7 +46,7 @@ static GPENCIL_MaterialPool *gpencil_material_pool_add(GPENCIL_PrivateData *pd)
matpool->next = NULL;
matpool->used_count = 0;
if (matpool->ubo == NULL) {
- matpool->ubo = GPU_uniformbuffer_create(sizeof(matpool->mat_data), NULL, NULL);
+ matpool->ubo = GPU_uniformbuf_create(sizeof(matpool->mat_data));
}
pd->last_material_pool = matpool;
return matpool;
@@ -301,7 +301,7 @@ void gpencil_material_resources_get(GPENCIL_MaterialPool *first_pool,
int mat_id,
GPUTexture **r_tex_stroke,
GPUTexture **r_tex_fill,
- GPUUniformBuffer **r_ubo_mat)
+ GPUUniformBuf **r_ubo_mat)
{
GPENCIL_MaterialPool *matpool = first_pool;
int pool_id = mat_id / GP_MATERIAL_BUFFER_LEN;
@@ -331,7 +331,7 @@ GPENCIL_LightPool *gpencil_light_pool_add(GPENCIL_PrivateData *pd)
/* Tag light list end. */
lightpool->light_data[0].color[0] = -1.0;
if (lightpool->ubo == NULL) {
- lightpool->ubo = GPU_uniformbuffer_create(sizeof(lightpool->light_data), NULL, NULL);
+ lightpool->ubo = GPU_uniformbuf_create(sizeof(lightpool->light_data));
}
pd->last_light_pool = lightpool;
return lightpool;
diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.c b/source/blender/draw/engines/gpencil/gpencil_engine.c
index 015e631dc14..368530fde05 100644
--- a/source/blender/draw/engines/gpencil/gpencil_engine.c
+++ b/source/blender/draw/engines/gpencil/gpencil_engine.c
@@ -42,7 +42,7 @@
#include "DNA_view3d_types.h"
#include "GPU_texture.h"
-#include "GPU_uniformbuffer.h"
+#include "GPU_uniform_buffer.h"
#include "gpencil_engine.h"
@@ -345,8 +345,8 @@ typedef struct gpIterPopulateData {
GPENCIL_MaterialPool *matpool;
DRWShadingGroup *grp;
/* Last material UBO bound. Used to avoid uneeded buffer binding. */
- GPUUniformBuffer *ubo_mat;
- GPUUniformBuffer *ubo_lights;
+ GPUUniformBuf *ubo_mat;
+ GPUUniformBuf *ubo_lights;
/* Last texture bound. */
GPUTexture *tex_fill;
GPUTexture *tex_stroke;
@@ -487,9 +487,10 @@ static void gpencil_stroke_cache_populate(bGPDlayer *gpl,
MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(iter->ob, gps->mat_nr + 1);
+ const bool is_render = iter->pd->is_render;
bool hide_material = (gp_style->flag & GP_MATERIAL_HIDE) != 0;
bool show_stroke = ((gp_style->flag & GP_MATERIAL_STROKE_SHOW) != 0) ||
- ((gps->flag & GP_STROKE_NOFILL) != 0);
+ (!is_render && ((gps->flag & GP_STROKE_NOFILL) != 0));
bool show_fill = (gps->tot_triangles > 0) && ((gp_style->flag & GP_MATERIAL_FILL_SHOW) != 0) &&
(!iter->pd->simplify_fill) && ((gps->flag & GP_STROKE_NOFILL) == 0);
@@ -501,7 +502,7 @@ static void gpencil_stroke_cache_populate(bGPDlayer *gpl,
return;
}
- GPUUniformBuffer *ubo_mat;
+ GPUUniformBuf *ubo_mat;
GPUTexture *tex_stroke, *tex_fill;
gpencil_material_resources_get(
iter->matpool, iter->mat_ofs + gps->mat_nr, &tex_stroke, &tex_fill, &ubo_mat);
@@ -654,13 +655,13 @@ void GPENCIL_cache_finish(void *ved)
BLI_memblock_iternew(pd->gp_material_pool, &iter);
GPENCIL_MaterialPool *pool;
while ((pool = (GPENCIL_MaterialPool *)BLI_memblock_iterstep(&iter))) {
- GPU_uniformbuffer_update(pool->ubo, pool->mat_data);
+ GPU_uniformbuf_update(pool->ubo, pool->mat_data);
}
BLI_memblock_iternew(pd->gp_light_pool, &iter);
GPENCIL_LightPool *lpool;
while ((lpool = (GPENCIL_LightPool *)BLI_memblock_iterstep(&iter))) {
- GPU_uniformbuffer_update(lpool->ubo, lpool->light_data);
+ GPU_uniformbuf_update(lpool->ubo, lpool->light_data);
}
/* Sort object by decreasing Z to avoid most of alpha ordering issues. */
diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.h b/source/blender/draw/engines/gpencil/gpencil_engine.h
index a406df530fc..852945b25c3 100644
--- a/source/blender/draw/engines/gpencil/gpencil_engine.h
+++ b/source/blender/draw/engines/gpencil/gpencil_engine.h
@@ -24,10 +24,16 @@
#include "DNA_gpencil_types.h"
+#include "DRW_render.h"
+
#include "BLI_bitmap.h"
#include "GPU_batch.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
extern DrawEngineType draw_engine_gpencil_type;
struct GPENCIL_Data;
@@ -108,7 +114,7 @@ typedef struct GPENCIL_MaterialPool {
/* GPU representatin of materials. */
gpMaterial mat_data[GP_MATERIAL_BUFFER_LEN];
/* Matching ubo. */
- struct GPUUniformBuffer *ubo;
+ struct GPUUniformBuf *ubo;
/* Texture per material. NULL means none. */
struct GPUTexture *tex_fill[GP_MATERIAL_BUFFER_LEN];
struct GPUTexture *tex_stroke[GP_MATERIAL_BUFFER_LEN];
@@ -120,7 +126,7 @@ typedef struct GPENCIL_LightPool {
/* GPU representatin of materials. */
gpLight light_data[GPENCIL_LIGHT_BUFFER_LEN];
/* Matching ubo. */
- struct GPUUniformBuffer *ubo;
+ struct GPUUniformBuf *ubo;
/* Number of light in the pool. */
int light_used;
} GPENCIL_LightPool;
@@ -384,7 +390,7 @@ void gpencil_material_resources_get(GPENCIL_MaterialPool *first_pool,
int mat_id,
struct GPUTexture **r_tex_stroke,
struct GPUTexture **r_tex_fill,
- struct GPUUniformBuffer **r_ubo_mat);
+ struct GPUUniformBuf **r_ubo_mat);
void gpencil_light_ambient_add(GPENCIL_LightPool *lightpool, const float color[3]);
void gpencil_light_pool_populate(GPENCIL_LightPool *matpool, Object *ob);
@@ -397,7 +403,6 @@ void gpencil_vfx_cache_populate(GPENCIL_Data *vedata, Object *ob, GPENCIL_tObjec
/* Shaders */
struct GPUShader *GPENCIL_shader_antialiasing(int stage);
struct GPUShader *GPENCIL_shader_geometry_get(void);
-struct GPUShader *GPENCIL_shader_composite_get(void);
struct GPUShader *GPENCIL_shader_layer_blend_get(void);
struct GPUShader *GPENCIL_shader_mask_invert_get(void);
struct GPUShader *GPENCIL_shader_depth_merge_get(void);
@@ -438,3 +443,6 @@ void GPENCIL_render_to_image(void *vedata,
void gpencil_light_pool_free(void *storage);
void gpencil_material_pool_free(void *storage);
GPENCIL_ViewLayerData *GPENCIL_view_layer_data_ensure(void);
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/draw/engines/overlay/overlay_private.h b/source/blender/draw/engines/overlay/overlay_private.h
index 7e93382796f..c9d29af91e6 100644
--- a/source/blender/draw/engines/overlay/overlay_private.h
+++ b/source/blender/draw/engines/overlay/overlay_private.h
@@ -22,6 +22,12 @@
#pragma once
+#include "DRW_render.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#ifdef __APPLE__
# define USE_GEOM_SHADER_WORKAROUND 1
#else
@@ -627,3 +633,7 @@ GPUShader *OVERLAY_shader_xray_fade(void);
OVERLAY_InstanceFormats *OVERLAY_shader_instance_formats_get(void);
void OVERLAY_shader_free(void);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl
index eb61edca6c7..122c99ca536 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl
@@ -1,6 +1,5 @@
#define EPSILON 0.00001
-#define M_PI 3.14159265358979323846
#define CAVITY_BUFFER_RANGE 4.0
diff --git a/source/blender/draw/engines/workbench/workbench_data.c b/source/blender/draw/engines/workbench/workbench_data.c
index 0d7f4ee660b..b6cfc019b8d 100644
--- a/source/blender/draw/engines/workbench/workbench_data.c
+++ b/source/blender/draw/engines/workbench/workbench_data.c
@@ -32,24 +32,24 @@
#include "UI_resources.h"
-#include "GPU_uniformbuffer.h"
+#include "GPU_uniform_buffer.h"
/* -------------------------------------------------------------------- */
/** \name World Data
* \{ */
-GPUUniformBuffer *workbench_material_ubo_alloc(WORKBENCH_PrivateData *wpd)
+GPUUniformBuf *workbench_material_ubo_alloc(WORKBENCH_PrivateData *wpd)
{
- struct GPUUniformBuffer **ubo = BLI_memblock_alloc(wpd->material_ubo);
+ struct GPUUniformBuf **ubo = BLI_memblock_alloc(wpd->material_ubo);
if (*ubo == NULL) {
- *ubo = GPU_uniformbuffer_create(sizeof(WORKBENCH_UBO_Material) * MAX_MATERIAL, NULL, NULL);
+ *ubo = GPU_uniformbuf_create(sizeof(WORKBENCH_UBO_Material) * MAX_MATERIAL);
}
return *ubo;
}
static void workbench_ubo_free(void *elem)
{
- GPUUniformBuffer **ubo = elem;
+ GPUUniformBuf **ubo = elem;
DRW_UBO_FREE_SAFE(*ubo);
}
@@ -78,7 +78,7 @@ static WORKBENCH_ViewLayerData *workbench_view_layer_data_ensure_ex(struct ViewL
size_t matbuf_size = sizeof(WORKBENCH_UBO_Material) * MAX_MATERIAL;
(*vldata)->material_ubo_data = BLI_memblock_create_ex(matbuf_size, matbuf_size * 2);
(*vldata)->material_ubo = BLI_memblock_create_ex(sizeof(void *), sizeof(void *) * 8);
- (*vldata)->world_ubo = DRW_uniformbuffer_create(sizeof(WORKBENCH_UBO_World), NULL);
+ (*vldata)->world_ubo = GPU_uniformbuf_create_ex(sizeof(WORKBENCH_UBO_World), NULL, "wb_World");
}
return *vldata;
@@ -275,7 +275,7 @@ void workbench_update_world_ubo(WORKBENCH_PrivateData *wpd)
workbench_shadow_data_update(wpd, &wd);
workbench_cavity_data_update(wpd, &wd);
- DRW_uniformbuffer_update(wpd->world_ubo, &wd);
+ GPU_uniformbuf_update(wpd->world_ubo, &wd);
}
void workbench_update_material_ubos(WORKBENCH_PrivateData *UNUSED(wpd))
@@ -288,9 +288,9 @@ void workbench_update_material_ubos(WORKBENCH_PrivateData *UNUSED(wpd))
BLI_memblock_iternew(vldata->material_ubo_data, &iter_data);
WORKBENCH_UBO_Material *matchunk;
while ((matchunk = BLI_memblock_iterstep(&iter_data))) {
- GPUUniformBuffer **ubo = BLI_memblock_iterstep(&iter);
+ GPUUniformBuf **ubo = BLI_memblock_iterstep(&iter);
BLI_assert(*ubo != NULL);
- GPU_uniformbuffer_update(*ubo, matchunk);
+ GPU_uniformbuf_update(*ubo, matchunk);
}
BLI_memblock_clear(vldata->material_ubo, workbench_ubo_free);
diff --git a/source/blender/draw/engines/workbench/workbench_effect_cavity.c b/source/blender/draw/engines/workbench/workbench_effect_cavity.c
index 4a8db65c02e..c9ac6660445 100644
--- a/source/blender/draw/engines/workbench/workbench_effect_cavity.c
+++ b/source/blender/draw/engines/workbench/workbench_effect_cavity.c
@@ -139,8 +139,8 @@ void workbench_cavity_samples_ubo_ensure(WORKBENCH_PrivateData *wpd)
float *samples = create_disk_samples(cavity_sample_count_single_iteration, max_iter_count);
wpd->vldata->cavity_jitter_tx = create_jitter_texture(cavity_sample_count);
/* NOTE: Uniform buffer needs to always be filled to be valid. */
- wpd->vldata->cavity_sample_ubo = DRW_uniformbuffer_create(
- sizeof(float[4]) * CAVITY_MAX_SAMPLES, samples);
+ wpd->vldata->cavity_sample_ubo = GPU_uniformbuf_create_ex(
+ sizeof(float[4]) * CAVITY_MAX_SAMPLES, samples, "wb_CavitySamples");
wpd->vldata->cavity_sample_count = cavity_sample_count;
MEM_freeN(samples);
}
diff --git a/source/blender/draw/engines/workbench/workbench_effect_dof.c b/source/blender/draw/engines/workbench/workbench_effect_dof.c
index 32f6a3392b5..abbca0988d4 100644
--- a/source/blender/draw/engines/workbench/workbench_effect_dof.c
+++ b/source/blender/draw/engines/workbench/workbench_effect_dof.c
@@ -74,7 +74,7 @@ static void square_to_circle(float x, float y, float *r, float *T)
#define KERNEL_RAD (3)
#define SAMP_LEN SQUARE_UNSAFE(KERNEL_RAD * 2 + 1)
-static void workbench_dof_setup_samples(struct GPUUniformBuffer **ubo,
+static void workbench_dof_setup_samples(struct GPUUniformBuf **ubo,
float **data,
float bokeh_sides,
float bokeh_rotation,
@@ -84,7 +84,7 @@ static void workbench_dof_setup_samples(struct GPUUniformBuffer **ubo,
*data = MEM_callocN(sizeof(float[4]) * SAMP_LEN, "workbench dof samples");
}
if (*ubo == NULL) {
- *ubo = DRW_uniformbuffer_create(sizeof(float[4]) * SAMP_LEN, NULL);
+ *ubo = GPU_uniformbuf_create(sizeof(float[4]) * SAMP_LEN);
}
float *samp = *data;
@@ -120,7 +120,7 @@ static void workbench_dof_setup_samples(struct GPUUniformBuffer **ubo,
}
}
- DRW_uniformbuffer_update(*ubo, *data);
+ GPU_uniformbuf_update(*ubo, *data);
}
void workbench_dof_engine_init(WORKBENCH_Data *vedata)
diff --git a/source/blender/draw/engines/workbench/workbench_engine.c b/source/blender/draw/engines/workbench/workbench_engine.c
index ca80b6a9002..80a8f310191 100644
--- a/source/blender/draw/engines/workbench/workbench_engine.c
+++ b/source/blender/draw/engines/workbench/workbench_engine.c
@@ -114,7 +114,7 @@ static void workbench_cache_sculpt_populate(WORKBENCH_PrivateData *wpd,
eV3DShadingColorType color_type)
{
const bool use_single_drawcall = !ELEM(color_type, V3D_SHADING_MATERIAL_COLOR);
- BLI_assert(wpd->shading.color_type != V3D_SHADING_TEXTURE_COLOR);
+ BLI_assert(color_type != V3D_SHADING_TEXTURE_COLOR);
if (use_single_drawcall) {
DRWShadingGroup *grp = workbench_material_setup(wpd, ob, 0, color_type, NULL);
@@ -309,6 +309,11 @@ static eV3DShadingColorType workbench_color_type_get(WORKBENCH_PrivateData *wpd,
}
}
+ if (is_sculpt_pbvh && color_type == V3D_SHADING_TEXTURE_COLOR) {
+ /* Force use of material color for sculpt. */
+ color_type = V3D_SHADING_MATERIAL_COLOR;
+ }
+
if (r_draw_shadow) {
*r_draw_shadow = (ob->dtx & OB_DRAW_NO_SHADOW_CAST) == 0 && SHADOW_ENABLED(wpd);
/* Currently unsupported in sculpt mode. We could revert to the slow
diff --git a/source/blender/draw/engines/workbench/workbench_materials.c b/source/blender/draw/engines/workbench/workbench_materials.c
index 538083b4beb..6aa794bda51 100644
--- a/source/blender/draw/engines/workbench/workbench_materials.c
+++ b/source/blender/draw/engines/workbench/workbench_materials.c
@@ -33,7 +33,7 @@
#include "DNA_mesh_types.h"
#include "DNA_node_types.h"
-#include "GPU_uniformbuffer.h"
+#include "GPU_uniform_buffer.h"
#include "ED_uvedit.h"
diff --git a/source/blender/draw/engines/workbench/workbench_private.h b/source/blender/draw/engines/workbench/workbench_private.h
index 4a6dadc32fd..8983826f16f 100644
--- a/source/blender/draw/engines/workbench/workbench_private.h
+++ b/source/blender/draw/engines/workbench/workbench_private.h
@@ -33,6 +33,10 @@
#include "workbench_engine.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
extern struct DrawEngineType draw_engine_workbench;
#define WORKBENCH_ENGINE "BLENDER_WORKBENCH"
@@ -238,7 +242,7 @@ typedef struct WORKBENCH_PrivateData {
/** Copy of context mode for faster access. */
eContextObjectMode ctx_mode;
/** Shorthand for wpd->vldata->world_ubo. */
- struct GPUUniformBuffer *world_ubo;
+ struct GPUUniformBuf *world_ubo;
/** Background color to clear the color buffer with. */
float background_color[4];
@@ -309,7 +313,7 @@ typedef struct WORKBENCH_PrivateData {
struct BLI_memblock *material_ubo_data;
/** Current material chunk being filled by workbench_material_setup_ex(). */
WORKBENCH_UBO_Material *material_ubo_data_curr;
- struct GPUUniformBuffer *material_ubo_curr;
+ struct GPUUniformBuf *material_ubo_curr;
/** Copy of txl->dummy_image_tx for faster access. */
struct GPUTexture *dummy_image_tx;
/** Total number of used material chunk. */
@@ -359,11 +363,11 @@ typedef struct WORKBENCH_ObjectData {
typedef struct WORKBENCH_ViewLayerData {
/** Depth of field sample location array.*/
- struct GPUUniformBuffer *dof_sample_ubo;
+ struct GPUUniformBuf *dof_sample_ubo;
/** All constant data used for a render loop.*/
- struct GPUUniformBuffer *world_ubo;
+ struct GPUUniformBuf *world_ubo;
/** Cavity sample location array.*/
- struct GPUUniformBuffer *cavity_sample_ubo;
+ struct GPUUniformBuf *cavity_sample_ubo;
/** Blue noise texture used to randomize the sampling of some effects.*/
struct GPUTexture *cavity_jitter_tx;
/** Materials ubos allocated in a memblock for easy bookeeping. */
@@ -490,7 +494,7 @@ DRWShadingGroup *workbench_image_setup_ex(WORKBENCH_PrivateData *wpd,
void workbench_private_data_init(WORKBENCH_PrivateData *wpd);
void workbench_update_world_ubo(WORKBENCH_PrivateData *wpd);
void workbench_update_material_ubos(WORKBENCH_PrivateData *wpd);
-struct GPUUniformBuffer *workbench_material_ubo_alloc(WORKBENCH_PrivateData *wpd);
+struct GPUUniformBuf *workbench_material_ubo_alloc(WORKBENCH_PrivateData *wpd);
/* workbench_volume.c */
void workbench_volume_engine_init(WORKBENCH_Data *vedata);
@@ -519,3 +523,6 @@ void workbench_render(void *ved,
void workbench_render_update_passes(struct RenderEngine *engine,
struct Scene *scene,
struct ViewLayer *view_layer);
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h
index 63625fae185..697f4f77601 100644
--- a/source/blender/draw/intern/DRW_render.h
+++ b/source/blender/draw/intern/DRW_render.h
@@ -48,6 +48,7 @@
#include "GPU_primitive.h"
#include "GPU_shader.h"
#include "GPU_texture.h"
+#include "GPU_uniform_buffer.h"
#include "draw_cache.h"
#include "draw_common.h"
@@ -67,7 +68,7 @@ struct GPUFrameBuffer;
struct GPUMaterial;
struct GPUShader;
struct GPUTexture;
-struct GPUUniformBuffer;
+struct GPUUniformBuf;
struct Object;
struct ParticleSystem;
struct RenderEngineType;
@@ -184,14 +185,10 @@ void DRW_texture_free(struct GPUTexture *tex);
} \
} while (0)
-/* UBOs */
-struct GPUUniformBuffer *DRW_uniformbuffer_create(int size, const void *data);
-void DRW_uniformbuffer_update(struct GPUUniformBuffer *ubo, const void *data);
-void DRW_uniformbuffer_free(struct GPUUniformBuffer *ubo);
#define DRW_UBO_FREE_SAFE(ubo) \
do { \
if (ubo != NULL) { \
- DRW_uniformbuffer_free(ubo); \
+ GPU_uniformbuf_free(ubo); \
ubo = NULL; \
} \
} while (0)
@@ -516,10 +513,10 @@ void DRW_shgroup_uniform_texture_ref(DRWShadingGroup *shgroup,
struct GPUTexture **tex);
void DRW_shgroup_uniform_block(DRWShadingGroup *shgroup,
const char *name,
- const struct GPUUniformBuffer *ubo);
+ const struct GPUUniformBuf *ubo);
void DRW_shgroup_uniform_block_ref(DRWShadingGroup *shgroup,
const char *name,
- struct GPUUniformBuffer **ubo);
+ struct GPUUniformBuf **ubo);
void DRW_shgroup_uniform_float(DRWShadingGroup *shgroup,
const char *name,
const float *value,
diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h
index 2653b3127ae..5f77dac98be 100644
--- a/source/blender/draw/intern/draw_cache_extract.h
+++ b/source/blender/draw/intern/draw_cache_extract.h
@@ -260,6 +260,7 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
Mesh *me,
const bool is_editmode,
const bool is_paint_mode,
+ const bool is_mode_active,
const float obmat[4][4],
const bool do_final,
const bool do_uvedit,
diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.c b/source/blender/draw/intern/draw_cache_extract_mesh.c
index 934b47d583e..528a6eec69e 100644
--- a/source/blender/draw/intern/draw_cache_extract_mesh.c
+++ b/source/blender/draw/intern/draw_cache_extract_mesh.c
@@ -99,8 +99,6 @@ typedef struct MeshRenderData {
float obmat[4][4];
const ToolSettings *toolsettings;
- /* HACK not supposed to be there but it's needed. */
- struct MeshBatchCache *cache;
/** Edit Mesh */
BMEditMesh *edit_bmesh;
BMesh *bm;
@@ -313,9 +311,14 @@ static void mesh_render_data_update_normals(MeshRenderData *mr,
}
}
+/**
+ * \param is_mode_active: When true, use the modifiers from the edit-data,
+ * otherwise don't use modifiers as they are not from this object.
+ */
static MeshRenderData *mesh_render_data_create(Mesh *me,
const bool is_editmode,
const bool is_paint_mode,
+ const bool is_mode_active,
const float obmat[4][4],
const bool do_final,
const bool do_uvedit,
@@ -335,7 +338,7 @@ static MeshRenderData *mesh_render_data_create(Mesh *me,
mr->bm = me->edit_mesh->bm;
mr->edit_bmesh = me->edit_mesh;
mr->me = (do_final) ? me->edit_mesh->mesh_eval_final : me->edit_mesh->mesh_eval_cage;
- mr->edit_data = mr->me->runtime.edit_data;
+ mr->edit_data = is_mode_active ? mr->me->runtime.edit_data : NULL;
if (mr->edit_data) {
EditMeshData *emd = mr->edit_data;
@@ -350,8 +353,9 @@ static MeshRenderData *mesh_render_data_create(Mesh *me,
mr->bm_poly_centers = mr->edit_data->polyCos;
}
- bool has_mdata = (mr->me->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA);
- bool use_mapped = has_mdata && !do_uvedit && mr->me && !mr->me->runtime.is_original;
+ bool has_mdata = is_mode_active && (mr->me->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA);
+ bool use_mapped = is_mode_active &&
+ (has_mdata && !do_uvedit && mr->me && !mr->me->runtime.is_original);
int bm_ensure_types = BM_VERT | BM_EDGE | BM_LOOP | BM_FACE;
@@ -751,8 +755,13 @@ typedef void(ExtractLVertMeshFn)(const MeshRenderData *mr,
/** \name Mesh Elements Extract Struct
* \{ */
-typedef void *(ExtractInitFn)(const MeshRenderData *mr, void *buffer);
-typedef void(ExtractFinishFn)(const MeshRenderData *mr, void *buffer, void *data);
+typedef void *(ExtractInitFn)(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ void *buffer);
+typedef void(ExtractFinishFn)(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ void *buffer,
+ void *data);
typedef struct MeshExtract {
/** Executed on main thread and return user data for iteration functions. */
@@ -796,7 +805,9 @@ typedef struct MeshExtract_Tri_Data {
int *tri_mat_end;
} MeshExtract_Tri_Data;
-static void *extract_tris_init(const MeshRenderData *mr, void *UNUSED(ibo))
+static void *extract_tris_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *UNUSED(ibo))
{
MeshExtract_Tri_Data *data = MEM_callocN(sizeof(*data), __func__);
@@ -882,14 +893,17 @@ static void extract_tris_iter_looptri_mesh(const MeshRenderData *mr,
EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END;
}
-static void extract_tris_finish(const MeshRenderData *mr, void *ibo, void *_data)
+static void extract_tris_finish(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ void *ibo,
+ void *_data)
{
MeshExtract_Tri_Data *data = _data;
GPU_indexbuf_build_in_place(&data->elb, ibo);
/* HACK: Create ibo sub-ranges and assign them to each #GPUBatch. */
/* The `surface_per_mat` tests are there when object shading type is set to Wire or Bounds. In
* these cases there isn't a surface per material. */
- if (mr->use_final_mesh && mr->cache->surface_per_mat && mr->cache->surface_per_mat[0]) {
+ if (mr->use_final_mesh && cache->surface_per_mat && cache->surface_per_mat[0]) {
for (int i = 0; i < mr->mat_len; i++) {
/* Multiply by 3 because these are triangle indices. */
const int mat_start = data->tri_mat_start[i];
@@ -898,7 +912,7 @@ static void extract_tris_finish(const MeshRenderData *mr, void *ibo, void *_data
const int len = (mat_end - mat_start) * 3;
GPUIndexBuf *sub_ibo = GPU_indexbuf_create_subrange(ibo, start, len);
/* WARNING: We modify the #GPUBatch here! */
- GPU_batch_elembuf_set(mr->cache->surface_per_mat[i], sub_ibo, true);
+ GPU_batch_elembuf_set(cache->surface_per_mat[i], sub_ibo, true);
}
}
MEM_freeN(data->tri_mat_start);
@@ -921,7 +935,9 @@ static const MeshExtract extract_tris = {
/** \name Extract Edges Indices
* \{ */
-static void *extract_lines_init(const MeshRenderData *mr, void *UNUSED(buf))
+static void *extract_lines_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *UNUSED(buf))
{
GPUIndexBufBuilder *elb = MEM_mallocN(sizeof(*elb), __func__);
/* Put loose edges at the end. */
@@ -1039,7 +1055,10 @@ static void extract_lines_iter_ledge_mesh(const MeshRenderData *mr,
EXTRACT_LEDGE_FOREACH_MESH_END;
}
-static void extract_lines_finish(const MeshRenderData *UNUSED(mr), void *ibo, void *elb)
+static void extract_lines_finish(const MeshRenderData *UNUSED(mr),
+ struct MeshBatchCache *UNUSED(cache),
+ void *ibo,
+ void *elb)
{
GPU_indexbuf_build_in_place(elb, ibo);
MEM_freeN(elb);
@@ -1061,21 +1080,24 @@ static const MeshExtract extract_lines = {
/** \name Extract Loose Edges Sub Buffer
* \{ */
-static void extract_lines_loose_subbuffer(const MeshRenderData *mr)
+static void extract_lines_loose_subbuffer(const MeshRenderData *mr, struct MeshBatchCache *cache)
{
- BLI_assert(mr->cache->final.ibo.lines);
+ BLI_assert(cache->final.ibo.lines);
/* Multiply by 2 because these are edges indices. */
const int start = mr->edge_len * 2;
const int len = mr->edge_loose_len * 2;
GPU_indexbuf_create_subrange_in_place(
- mr->cache->final.ibo.lines_loose, mr->cache->final.ibo.lines, start, len);
- mr->cache->no_loose_wire = (len == 0);
+ cache->final.ibo.lines_loose, cache->final.ibo.lines, start, len);
+ cache->no_loose_wire = (len == 0);
}
-static void extract_lines_with_lines_loose_finish(const MeshRenderData *mr, void *ibo, void *elb)
+static void extract_lines_with_lines_loose_finish(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ void *ibo,
+ void *elb)
{
GPU_indexbuf_build_in_place(elb, ibo);
- extract_lines_loose_subbuffer(mr);
+ extract_lines_loose_subbuffer(mr, cache);
MEM_freeN(elb);
}
@@ -1096,7 +1118,9 @@ static const MeshExtract extract_lines_with_lines_loose = {
/** \name Extract Point Indices
* \{ */
-static void *extract_points_init(const MeshRenderData *mr, void *UNUSED(buf))
+static void *extract_points_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *UNUSED(buf))
{
GPUIndexBufBuilder *elb = MEM_mallocN(sizeof(*elb), __func__);
GPU_indexbuf_init(elb, GPU_PRIM_POINTS, mr->vert_len, mr->loop_len + mr->loop_loose_len);
@@ -1200,7 +1224,10 @@ static void extract_points_iter_lvert_mesh(const MeshRenderData *mr,
EXTRACT_LVERT_FOREACH_MESH_END;
}
-static void extract_points_finish(const MeshRenderData *UNUSED(mr), void *ibo, void *elb)
+static void extract_points_finish(const MeshRenderData *UNUSED(mr),
+ struct MeshBatchCache *UNUSED(cache),
+ void *ibo,
+ void *elb)
{
GPU_indexbuf_build_in_place(elb, ibo);
MEM_freeN(elb);
@@ -1225,7 +1252,9 @@ static const MeshExtract extract_points = {
/** \name Extract Facedots Indices
* \{ */
-static void *extract_fdots_init(const MeshRenderData *mr, void *UNUSED(buf))
+static void *extract_fdots_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *UNUSED(buf))
{
GPUIndexBufBuilder *elb = MEM_mallocN(sizeof(*elb), __func__);
GPU_indexbuf_init(elb, GPU_PRIM_POINTS, mr->poly_len, mr->poly_len);
@@ -1280,7 +1309,10 @@ static void extract_fdots_iter_poly_mesh(const MeshRenderData *mr,
}
}
-static void extract_fdots_finish(const MeshRenderData *UNUSED(mr), void *ibo, void *elb)
+static void extract_fdots_finish(const MeshRenderData *UNUSED(mr),
+ struct MeshBatchCache *UNUSED(cache),
+ void *ibo,
+ void *elb)
{
GPU_indexbuf_build_in_place(elb, ibo);
MEM_freeN(elb);
@@ -1307,7 +1339,9 @@ typedef struct MeshExtract_LinePaintMask_Data {
BLI_bitmap select_map[0];
} MeshExtract_LinePaintMask_Data;
-static void *extract_lines_paint_mask_init(const MeshRenderData *mr, void *UNUSED(buf))
+static void *extract_lines_paint_mask_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *UNUSED(buf))
{
size_t bitmap_size = BLI_BITMAP_SIZE(mr->edge_len);
MeshExtract_LinePaintMask_Data *data = MEM_callocN(sizeof(*data) + bitmap_size, __func__);
@@ -1354,6 +1388,7 @@ static void extract_lines_paint_mask_iter_poly_mesh(const MeshRenderData *mr,
EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
}
static void extract_lines_paint_mask_finish(const MeshRenderData *UNUSED(mr),
+ struct MeshBatchCache *UNUSED(cache),
void *ibo,
void *_data)
{
@@ -1387,7 +1422,9 @@ typedef struct MeshExtract_LineAdjacency_Data {
uint vert_to_loop[0];
} MeshExtract_LineAdjacency_Data;
-static void *extract_lines_adjacency_init(const MeshRenderData *mr, void *UNUSED(buf))
+static void *extract_lines_adjacency_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *UNUSED(buf))
{
/* Similar to poly_to_tri_count().
* There is always (loop + triangle - 1) edges inside a polygon.
@@ -1483,7 +1520,10 @@ static void extract_lines_adjacency_iter_looptri_mesh(const MeshRenderData *mr,
EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END;
}
-static void extract_lines_adjacency_finish(const MeshRenderData *mr, void *ibo, void *_data)
+static void extract_lines_adjacency_finish(const MeshRenderData *UNUSED(mr),
+ struct MeshBatchCache *cache,
+ void *ibo,
+ void *_data)
{
MeshExtract_LineAdjacency_Data *data = _data;
/* Create edges for remaining non manifold edges. */
@@ -1506,7 +1546,7 @@ static void extract_lines_adjacency_finish(const MeshRenderData *mr, void *ibo,
BLI_edgehashIterator_free(ehi);
BLI_edgehash_free(data->eh, NULL);
- mr->cache->is_manifold = data->is_manifold;
+ cache->is_manifold = data->is_manifold;
GPU_indexbuf_build_in_place(&data->elb, ibo);
MEM_freeN(data);
@@ -1534,7 +1574,9 @@ typedef struct MeshExtract_EditUvElem_Data {
bool sync_selection;
} MeshExtract_EditUvElem_Data;
-static void *extract_edituv_tris_init(const MeshRenderData *mr, void *UNUSED(ibo))
+static void *extract_edituv_tris_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *UNUSED(ibo))
{
MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__);
GPU_indexbuf_init(&data->elb, GPU_PRIM_TRIS, mr->tri_len, mr->loop_len);
@@ -1583,7 +1625,10 @@ static void extract_edituv_tris_iter_looptri_mesh(const MeshRenderData *mr,
EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END;
}
-static void extract_edituv_tris_finish(const MeshRenderData *UNUSED(mr), void *ibo, void *data)
+static void extract_edituv_tris_finish(const MeshRenderData *UNUSED(mr),
+ struct MeshBatchCache *UNUSED(cache),
+ void *ibo,
+ void *data)
{
MeshExtract_EditUvElem_Data *extract_data = data;
GPU_indexbuf_build_in_place(&extract_data->elb, ibo);
@@ -1605,7 +1650,9 @@ static const MeshExtract extract_edituv_tris = {
/** \name Extract Edit UV Line Indices around faces
* \{ */
-static void *extract_edituv_lines_init(const MeshRenderData *mr, void *UNUSED(ibo))
+static void *extract_edituv_lines_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *UNUSED(ibo))
{
MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__);
GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES, mr->loop_len, mr->loop_len);
@@ -1655,7 +1702,10 @@ static void extract_edituv_lines_iter_poly_mesh(const MeshRenderData *mr,
EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
}
-static void extract_edituv_lines_finish(const MeshRenderData *UNUSED(mr), void *ibo, void *data)
+static void extract_edituv_lines_finish(const MeshRenderData *UNUSED(mr),
+ struct MeshBatchCache *UNUSED(cache),
+ void *ibo,
+ void *data)
{
MeshExtract_EditUvElem_Data *extract_data = data;
GPU_indexbuf_build_in_place(&extract_data->elb, ibo);
@@ -1677,7 +1727,9 @@ static const MeshExtract extract_edituv_lines = {
/** \name Extract Edit UV Points Indices
* \{ */
-static void *extract_edituv_points_init(const MeshRenderData *mr, void *UNUSED(ibo))
+static void *extract_edituv_points_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *UNUSED(ibo))
{
MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__);
GPU_indexbuf_init(&data->elb, GPU_PRIM_POINTS, mr->loop_len, mr->loop_len);
@@ -1724,7 +1776,10 @@ static void extract_edituv_points_iter_poly_mesh(const MeshRenderData *mr,
EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
}
-static void extract_edituv_points_finish(const MeshRenderData *UNUSED(mr), void *ibo, void *data)
+static void extract_edituv_points_finish(const MeshRenderData *UNUSED(mr),
+ struct MeshBatchCache *UNUSED(cache),
+ void *ibo,
+ void *data)
{
MeshExtract_EditUvElem_Data *extract_data = data;
GPU_indexbuf_build_in_place(&extract_data->elb, ibo);
@@ -1746,7 +1801,9 @@ static const MeshExtract extract_edituv_points = {
/** \name Extract Edit UV Facedots Indices
* \{ */
-static void *extract_edituv_fdots_init(const MeshRenderData *mr, void *UNUSED(ibo))
+static void *extract_edituv_fdots_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *UNUSED(ibo))
{
MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__);
GPU_indexbuf_init(&data->elb, GPU_PRIM_POINTS, mr->poly_len, mr->poly_len);
@@ -1813,7 +1870,10 @@ static void extract_edituv_fdots_iter_poly_mesh(const MeshRenderData *mr,
}
}
-static void extract_edituv_fdots_finish(const MeshRenderData *UNUSED(mr), void *ibo, void *_data)
+static void extract_edituv_fdots_finish(const MeshRenderData *UNUSED(mr),
+ struct MeshBatchCache *UNUSED(cache),
+ void *ibo,
+ void *_data)
{
MeshExtract_EditUvElem_Data *data = _data;
GPU_indexbuf_build_in_place(&data->elb, ibo);
@@ -1845,7 +1905,9 @@ typedef struct MeshExtract_PosNor_Data {
GPUPackedNormal packed_nor[];
} MeshExtract_PosNor_Data;
-static void *extract_pos_nor_init(const MeshRenderData *mr, void *buf)
+static void *extract_pos_nor_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf)
{
static GPUVertFormat format = {0};
if (format.attr_len == 0) {
@@ -1991,7 +2053,10 @@ static void extract_pos_nor_iter_lvert_mesh(const MeshRenderData *mr,
EXTRACT_LVERT_FOREACH_MESH_END;
}
-static void extract_pos_nor_finish(const MeshRenderData *UNUSED(mr), void *UNUSED(vbo), void *data)
+static void extract_pos_nor_finish(const MeshRenderData *UNUSED(mr),
+ struct MeshBatchCache *UNUSED(cache),
+ void *UNUSED(vbo),
+ void *data)
{
MEM_freeN(data);
}
@@ -2018,7 +2083,9 @@ typedef struct gpuHQNor {
short x, y, z, w;
} gpuHQNor;
-static void *extract_lnor_hq_init(const MeshRenderData *mr, void *buf)
+static void *extract_lnor_hq_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf)
{
static GPUVertFormat format = {0};
if (format.attr_len == 0) {
@@ -2104,7 +2171,9 @@ static const MeshExtract extract_lnor_hq = {
/** \name Extract Loop Normal
* \{ */
-static void *extract_lnor_init(const MeshRenderData *mr, void *buf)
+static void *extract_lnor_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf)
{
static GPUVertFormat format = {0};
if (format.attr_len == 0) {
@@ -2195,16 +2264,15 @@ static const MeshExtract extract_lnor = {
/** \name Extract UV layers
* \{ */
-static void *extract_uv_init(const MeshRenderData *mr, void *buf)
+static void *extract_uv_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf)
{
GPUVertFormat format = {0};
GPU_vertformat_deinterleave(&format);
CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata;
- uint32_t uv_layers = mr->cache->cd_used.uv;
-
+ uint32_t uv_layers = cache->cd_used.uv;
/* HACK to fix T68857 */
- if (mr->extract_type == MR_EXTRACT_BMESH && mr->cache->cd_used.edit_uv == 1) {
+ if (mr->extract_type == MR_EXTRACT_BMESH && cache->cd_used.edit_uv == 1) {
int layer = CustomData_get_active_layer(cd_ldata, CD_MLOOPUV);
if (layer != -1) {
uv_layers |= (1 << layer);
@@ -2292,7 +2360,10 @@ static const MeshExtract extract_uv = {
/** \name Extract Tangent layers
* \{ */
-static void extract_tan_ex(const MeshRenderData *mr, GPUVertBuf *vbo, const bool do_hq)
+static void extract_tan_ex(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ GPUVertBuf *vbo,
+ const bool do_hq)
{
GPUVertCompType comp_type = do_hq ? GPU_COMP_I16 : GPU_COMP_I10;
GPUVertFetchMode fetch_mode = GPU_FETCH_INT_TO_FLOAT_UNIT;
@@ -2302,10 +2373,10 @@ static void extract_tan_ex(const MeshRenderData *mr, GPUVertBuf *vbo, const bool
CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata;
CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata;
- uint32_t tan_layers = mr->cache->cd_used.tan;
+ uint32_t tan_layers = cache->cd_used.tan;
float(*orco)[3] = CustomData_get_layer(cd_vdata, CD_ORCO);
bool orco_allocated = false;
- const bool use_orco_tan = mr->cache->cd_used.tan_orco != 0;
+ const bool use_orco_tan = cache->cd_used.tan_orco != 0;
int tan_len = 0;
char tangent_names[MAX_MTFACE][MAX_CUSTOMDATA_LAYER_NAME];
@@ -2461,9 +2532,9 @@ static void extract_tan_ex(const MeshRenderData *mr, GPUVertBuf *vbo, const bool
CustomData_free(&loop_data, mr->loop_len);
}
-static void *extract_tan_init(const MeshRenderData *mr, void *buf)
+static void *extract_tan_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf)
{
- extract_tan_ex(mr, buf, false);
+ extract_tan_ex(mr, cache, buf, false);
return NULL;
}
@@ -2479,9 +2550,9 @@ static const MeshExtract extract_tan = {
/** \name Extract HQ Tangent layers
* \{ */
-static void *extract_tan_hq_init(const MeshRenderData *mr, void *buf)
+static void *extract_tan_hq_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf)
{
- extract_tan_ex(mr, buf, true);
+ extract_tan_ex(mr, cache, buf, true);
return NULL;
}
@@ -2497,15 +2568,15 @@ static const MeshExtract extract_tan_hq = {
/** \name Extract VCol
* \{ */
-static void *extract_vcol_init(const MeshRenderData *mr, void *buf)
+static void *extract_vcol_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf)
{
GPUVertFormat format = {0};
GPU_vertformat_deinterleave(&format);
CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata;
CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata;
- uint32_t vcol_layers = mr->cache->cd_used.vcol;
- uint32_t svcol_layers = mr->cache->cd_used.sculpt_vcol;
+ uint32_t vcol_layers = cache->cd_used.vcol;
+ uint32_t svcol_layers = cache->cd_used.sculpt_vcol;
for (int i = 0; i < MAX_MCOL; i++) {
if (vcol_layers & (1 << i)) {
@@ -2652,7 +2723,9 @@ typedef struct MeshExtract_Orco_Data {
float (*orco)[3];
} MeshExtract_Orco_Data;
-static void *extract_orco_init(const MeshRenderData *mr, void *buf)
+static void *extract_orco_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf)
{
static GPUVertFormat format = {0};
if (format.attr_len == 0) {
@@ -2705,7 +2778,10 @@ static void extract_orco_iter_poly_mesh(const MeshRenderData *mr,
EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
}
-static void extract_orco_finish(const MeshRenderData *UNUSED(mr), void *UNUSED(buf), void *data)
+static void extract_orco_finish(const MeshRenderData *UNUSED(mr),
+ struct MeshBatchCache *UNUSED(cache),
+ void *UNUSED(buf),
+ void *data)
{
MEM_freeN(data);
}
@@ -2749,7 +2825,9 @@ static float loop_edge_factor_get(const float f_no[3],
return d;
}
-static void *extract_edge_fac_init(const MeshRenderData *mr, void *buf)
+static void *extract_edge_fac_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf)
{
static GPUVertFormat format = {0};
if (format.attr_len == 0) {
@@ -2874,7 +2952,10 @@ static void extract_edge_fac_iter_ledge_mesh(const MeshRenderData *mr,
EXTRACT_LEDGE_FOREACH_MESH_END;
}
-static void extract_edge_fac_finish(const MeshRenderData *mr, void *buf, void *_data)
+static void extract_edge_fac_finish(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *_data)
{
MeshExtract_EdgeFac_Data *data = _data;
@@ -2981,7 +3062,9 @@ static float evaluate_vertex_weight(const MDeformVert *dvert, const DRW_MeshWeig
return input;
}
-static void *extract_weights_init(const MeshRenderData *mr, void *buf)
+static void *extract_weights_init(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ void *buf)
{
static GPUVertFormat format = {0};
if (format.attr_len == 0) {
@@ -2993,7 +3076,7 @@ static void *extract_weights_init(const MeshRenderData *mr, void *buf)
MeshExtract_Weight_Data *data = MEM_callocN(sizeof(*data), __func__);
data->vbo_data = (float *)vbo->data;
- data->wstate = &mr->cache->weight_state;
+ data->wstate = &cache->weight_state;
if (data->wstate->defgroup_active == -1) {
/* Nothing to show. */
@@ -3056,7 +3139,10 @@ static void extract_weights_iter_poly_mesh(const MeshRenderData *mr,
}
}
-static void extract_weights_finish(const MeshRenderData *UNUSED(mr), void *UNUSED(buf), void *data)
+static void extract_weights_finish(const MeshRenderData *UNUSED(mr),
+ struct MeshBatchCache *UNUSED(cache),
+ void *UNUSED(buf),
+ void *data)
{
MEM_freeN(data);
}
@@ -3214,7 +3300,9 @@ static void mesh_render_data_vert_flag(const MeshRenderData *mr, BMVert *eve, Ed
}
}
-static void *extract_edit_data_init(const MeshRenderData *mr, void *buf)
+static void *extract_edit_data_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf)
{
static GPUVertFormat format = {0};
if (format.attr_len == 0) {
@@ -3365,7 +3453,9 @@ typedef struct MeshExtract_EditUVData_Data {
int cd_ofs;
} MeshExtract_EditUVData_Data;
-static void *extract_edituv_data_init(const MeshRenderData *mr, void *buf)
+static void *extract_edituv_data_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf)
{
static GPUVertFormat format = {0};
if (format.attr_len == 0) {
@@ -3442,6 +3532,7 @@ static void extract_edituv_data_iter_poly_mesh(const MeshRenderData *mr,
}
static void extract_edituv_data_finish(const MeshRenderData *UNUSED(mr),
+ struct MeshBatchCache *UNUSED(cache),
void *UNUSED(buf),
void *data)
{
@@ -3463,7 +3554,9 @@ static const MeshExtract extract_edituv_data = {
/** \name Extract Edit UV area stretch
* \{ */
-static void *extract_stretch_area_init(const MeshRenderData *mr, void *buf)
+static void *extract_stretch_area_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf)
{
static GPUVertFormat format = {0};
if (format.attr_len == 0) {
@@ -3492,7 +3585,10 @@ BLI_INLINE float area_ratio_to_stretch(float ratio, float tot_ratio, float inv_t
return (ratio > 1.0f) ? (1.0f / ratio) : ratio;
}
-static void mesh_stretch_area_finish(const MeshRenderData *mr, void *buf, void *UNUSED(data))
+static void mesh_stretch_area_finish(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ void *buf,
+ void *UNUSED(data))
{
float tot_area = 0.0f, tot_uv_area = 0.0f;
float *area_ratio = MEM_mallocN(sizeof(float) * mr->poly_len, __func__);
@@ -3528,8 +3624,8 @@ static void mesh_stretch_area_finish(const MeshRenderData *mr, void *buf, void *
BLI_assert(0);
}
- mr->cache->tot_area = tot_area;
- mr->cache->tot_uv_area = tot_uv_area;
+ cache->tot_area = tot_area;
+ cache->tot_uv_area = tot_uv_area;
/* Convert in place to avoid an extra allocation */
uint16_t *poly_stretch = (uint16_t *)area_ratio;
@@ -3634,7 +3730,9 @@ static void edituv_get_stretch_angle(float auv[2][2],
#endif
}
-static void *extract_stretch_angle_init(const MeshRenderData *mr, void *buf)
+static void *extract_stretch_angle_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf)
{
static GPUVertFormat format = {0};
if (format.attr_len == 0) {
@@ -3755,6 +3853,7 @@ static void extract_stretch_angle_iter_poly_mesh(const MeshRenderData *mr,
}
static void extract_stretch_angle_finish(const MeshRenderData *UNUSED(mr),
+ struct MeshBatchCache *UNUSED(cache),
void *UNUSED(buf),
void *data)
{
@@ -3776,7 +3875,9 @@ static const MeshExtract extract_stretch_angle = {
/** \name Extract Edit Mesh Analysis Colors
* \{ */
-static void *extract_mesh_analysis_init(const MeshRenderData *mr, void *buf)
+static void *extract_mesh_analysis_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf)
{
static GPUVertFormat format = {0};
if (format.attr_len == 0) {
@@ -4337,7 +4438,10 @@ static void statvis_calc_sharp(const MeshRenderData *mr, float *r_sharp)
MEM_freeN(vert_angles);
}
-static void extract_mesh_analysis_finish(const MeshRenderData *mr, void *buf, void *UNUSED(data))
+static void extract_mesh_analysis_finish(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *UNUSED(data))
{
BLI_assert(mr->edit_bmesh);
@@ -4378,7 +4482,9 @@ static const MeshExtract extract_mesh_analysis = {
/** \name Extract Facedots positions
* \{ */
-static void *extract_fdots_pos_init(const MeshRenderData *mr, void *buf)
+static void *extract_fdots_pos_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf)
{
static GPUVertFormat format = {0};
if (format.attr_len == 0) {
@@ -4464,7 +4570,9 @@ static const MeshExtract extract_fdots_pos = {
#define NOR_AND_FLAG_ACTIVE -1
#define NOR_AND_FLAG_HIDDEN -2
-static void *extract_fdots_nor_init(const MeshRenderData *mr, void *buf)
+static void *extract_fdots_nor_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf)
{
static GPUVertFormat format = {0};
if (format.attr_len == 0) {
@@ -4477,7 +4585,10 @@ static void *extract_fdots_nor_init(const MeshRenderData *mr, void *buf)
return NULL;
}
-static void extract_fdots_nor_finish(const MeshRenderData *mr, void *buf, void *UNUSED(data))
+static void extract_fdots_nor_finish(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *UNUSED(data))
{
static float invalid_normal[3] = {0.0f, 0.0f, 0.0f};
GPUVertBuf *vbo = buf;
@@ -4542,7 +4653,9 @@ typedef struct MeshExtract_FdotUV_Data {
int cd_ofs;
} MeshExtract_FdotUV_Data;
-static void *extract_fdots_uv_init(const MeshRenderData *mr, void *buf)
+static void *extract_fdots_uv_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf)
{
static GPUVertFormat format = {0};
if (format.attr_len == 0) {
@@ -4611,6 +4724,7 @@ static void extract_fdots_uv_iter_poly_mesh(const MeshRenderData *mr,
}
static void extract_fdots_uv_finish(const MeshRenderData *UNUSED(mr),
+ struct MeshBatchCache *UNUSED(cache),
void *UNUSED(buf),
void *data)
{
@@ -4636,7 +4750,9 @@ typedef struct MeshExtract_EditUVFdotData_Data {
int cd_ofs;
} MeshExtract_EditUVFdotData_Data;
-static void *extract_fdots_edituv_data_init(const MeshRenderData *mr, void *buf)
+static void *extract_fdots_edituv_data_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf)
{
static GPUVertFormat format = {0};
if (format.attr_len == 0) {
@@ -4684,6 +4800,7 @@ static void extract_fdots_edituv_data_iter_poly_mesh(const MeshRenderData *mr,
}
static void extract_fdots_edituv_data_finish(const MeshRenderData *UNUSED(mr),
+ struct MeshBatchCache *UNUSED(cache),
void *UNUSED(buf),
void *data)
{
@@ -4709,7 +4826,9 @@ typedef struct SkinRootData {
float local_pos[3];
} SkinRootData;
-static void *extract_skin_roots_init(const MeshRenderData *mr, void *buf)
+static void *extract_skin_roots_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf)
{
/* Exclusively for edit mode. */
BLI_assert(mr->bm);
@@ -4758,7 +4877,9 @@ static const MeshExtract extract_skin_roots = {
/** \name Extract Selection Index
* \{ */
-static void *extract_select_idx_init(const MeshRenderData *mr, void *buf)
+static void *extract_select_idx_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf)
{
static GPUVertFormat format = {0};
if (format.attr_len == 0) {
@@ -4950,7 +5071,9 @@ static const MeshExtract extract_vert_idx = {
.use_threading = true,
};
-static void *extract_select_fdot_idx_init(const MeshRenderData *mr, void *buf)
+static void *extract_select_fdot_idx_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf)
{
static GPUVertFormat format = {0};
if (format.attr_len == 0) {
@@ -5019,6 +5142,7 @@ typedef enum ExtractTaskDataType {
typedef struct ExtractTaskData {
void *next, *prev;
const MeshRenderData *mr;
+ struct MeshBatchCache *cache;
const MeshExtract *extract;
ExtractTaskDataType tasktype;
eMRIterType iter_type;
@@ -5030,6 +5154,7 @@ typedef struct ExtractTaskData {
} ExtractTaskData;
static ExtractTaskData *extract_task_data_create_mesh_extract(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
const MeshExtract *extract,
void *buf,
int32_t *task_counter)
@@ -5039,6 +5164,7 @@ static ExtractTaskData *extract_task_data_create_mesh_extract(const MeshRenderDa
taskdata->prev = NULL;
taskdata->tasktype = EXTRACT_MESH_EXTRACT;
taskdata->mr = mr;
+ taskdata->cache = cache;
taskdata->extract = extract;
taskdata->buf = buf;
@@ -5056,11 +5182,13 @@ static ExtractTaskData *extract_task_data_create_mesh_extract(const MeshRenderDa
return taskdata;
}
-static ExtractTaskData *extract_task_data_create_lines_loose(const MeshRenderData *mr)
+static ExtractTaskData *extract_task_data_create_lines_loose(const MeshRenderData *mr,
+ struct MeshBatchCache *cache)
{
ExtractTaskData *taskdata = MEM_callocN(sizeof(*taskdata), __func__);
taskdata->tasktype = EXTRACT_LINES_LOOSE;
taskdata->mr = mr;
+ taskdata->cache = cache;
return taskdata;
}
@@ -5152,7 +5280,7 @@ BLI_INLINE void mesh_extract_iter(const MeshRenderData *mr,
static void extract_init(ExtractTaskData *data)
{
if (data->tasktype == EXTRACT_MESH_EXTRACT) {
- data->user_data->user_data = data->extract->init(data->mr, data->buf);
+ data->user_data->user_data = data->extract->init(data->mr, data->cache, data->buf);
}
}
@@ -5170,11 +5298,11 @@ static void extract_run(void *__restrict taskdata)
/* If this is the last task, we do the finish function. */
int remainin_tasks = atomic_sub_and_fetch_int32(data->task_counter, 1);
if (remainin_tasks == 0 && data->extract->finish != NULL) {
- data->extract->finish(data->mr, data->buf, data->user_data->user_data);
+ data->extract->finish(data->mr, data->cache, data->buf, data->user_data->user_data);
}
}
else if (data->tasktype == EXTRACT_LINES_LOOSE) {
- extract_lines_loose_subbuffer(data->mr);
+ extract_lines_loose_subbuffer(data->mr, data->cache);
}
}
@@ -5341,6 +5469,7 @@ static void extract_task_create(struct TaskGraph *task_graph,
ListBase *user_data_init_task_datas,
const Scene *scene,
const MeshRenderData *mr,
+ MeshBatchCache *cache,
const MeshExtract *extract,
void *buf,
int32_t *task_counter)
@@ -5356,7 +5485,7 @@ static void extract_task_create(struct TaskGraph *task_graph,
/* Divide extraction of the VBO/IBO into sensible chunks of works. */
ExtractTaskData *taskdata = extract_task_data_create_mesh_extract(
- mr, extract, buf, task_counter);
+ mr, cache, extract, buf, task_counter);
/* Simple heuristic. */
const int chunk_size = 8192;
@@ -5411,6 +5540,7 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
const bool is_editmode,
const bool is_paint_mode,
+ const bool is_mode_active,
const float obmat[4][4],
const bool do_final,
const bool do_uvedit,
@@ -5510,6 +5640,7 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
MeshRenderData *mr = mesh_render_data_create(me,
is_editmode,
is_paint_mode,
+ is_mode_active,
obmat,
do_final,
do_uvedit,
@@ -5517,7 +5648,6 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
ts,
iter_flag,
data_flag);
- mr->cache = cache; /* HACK */
mr->use_hide = use_hide;
mr->use_subsurf_fdots = use_subsurf_fdots;
mr->use_final_mesh = do_final;
@@ -5549,6 +5679,7 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
&user_data_init_task_data->task_datas, \
scene, \
mr, \
+ cache, \
&extract_##name, \
mbc.buf.name, \
&task_counters[counter_used++]); \
@@ -5592,13 +5723,14 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
&user_data_init_task_data->task_datas,
scene,
mr,
+ cache,
lines_extractor,
mbc.ibo.lines,
&task_counters[counter_used++]);
}
else {
if (do_lines_loose_subbuffer) {
- ExtractTaskData *taskdata = extract_task_data_create_lines_loose(mr);
+ ExtractTaskData *taskdata = extract_task_data_create_lines_loose(mr, cache);
BLI_addtail(&single_threaded_task_data->task_datas, taskdata);
}
}
diff --git a/source/blender/draw/intern/draw_cache_impl_curve.c b/source/blender/draw/intern/draw_cache_impl_curve.c
index 73e0ff7ef83..b93c782a5b9 100644
--- a/source/blender/draw/intern/draw_cache_impl_curve.c
+++ b/source/blender/draw/intern/draw_cache_impl_curve.c
@@ -307,7 +307,7 @@ static int curve_render_data_normal_len_get(const CurveRenderData *rdata)
return rdata->normal.len;
}
-static void curve_cd_calc_used_gpu_layers(int *cd_layers,
+static void curve_cd_calc_used_gpu_layers(CustomDataMask *cd_layers,
struct GPUMaterial **gpumat_array,
int gpumat_array_len)
{
@@ -334,16 +334,16 @@ static void curve_cd_calc_used_gpu_layers(int *cd_layers,
switch (type) {
case CD_MTFACE:
- *cd_layers |= CD_MLOOPUV;
+ *cd_layers |= CD_MASK_MLOOPUV;
break;
case CD_TANGENT:
- *cd_layers |= CD_TANGENT;
+ *cd_layers |= CD_MASK_TANGENT;
break;
case CD_MCOL:
/* Curve object don't have Color data. */
break;
case CD_ORCO:
- *cd_layers |= CD_ORCO;
+ *cd_layers |= CD_MASK_ORCO;
break;
}
}
@@ -397,7 +397,7 @@ typedef struct CurveBatchCache {
GPUIndexBuf **surf_per_mat_tris;
GPUBatch **surf_per_mat;
int mat_len;
- int cd_used, cd_needed;
+ CustomDataMask cd_used, cd_needed;
/* settings to determine if cache is invalid */
bool is_dirty;
@@ -998,10 +998,10 @@ void DRW_curve_batch_cache_create_requested(Object *ob)
if (cache->mat_len > 1) {
DRW_ibo_request(cache->surf_per_mat[i], &cache->surf_per_mat_tris[i]);
}
- if (cache->cd_used & CD_MLOOPUV) {
+ if (cache->cd_used & CD_MASK_MLOOPUV) {
DRW_vbo_request(cache->surf_per_mat[i], &cache->ordered.loop_uv);
}
- if (cache->cd_used & CD_TANGENT) {
+ if (cache->cd_used & CD_MASK_TANGENT) {
DRW_vbo_request(cache->surf_per_mat[i], &cache->ordered.loop_tan);
}
DRW_vbo_request(cache->surf_per_mat[i], &cache->ordered.loop_pos_nor);
diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c
index 0e2be993787..e8a712b6881 100644
--- a/source/blender/draw/intern/draw_cache_impl_mesh.c
+++ b/source/blender/draw/intern/draw_cache_impl_mesh.c
@@ -1186,7 +1186,15 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
BLI_assert(me->edit_mesh->mesh_eval_final != NULL);
}
- const bool is_editmode = (me->edit_mesh != NULL) && DRW_object_is_in_edit_mode(ob);
+ /* Don't check `DRW_object_is_in_edit_mode(ob)` here because it means the same mesh
+ * may draw with edit-mesh data and regular mesh data.
+ * In this case the custom-data layers used wont always match in `me->runtime.batch_cache`.
+ * If we want to display regular mesh data, we should have a separate cache for the edit-mesh.
+ * See T77359. */
+ const bool is_editmode = (me->edit_mesh != NULL) /* && DRW_object_is_in_edit_mode(ob) */;
+
+ /* This could be set for paint mode too, currently it's only used for edit-mode. */
+ const bool is_mode_active = is_editmode && DRW_object_is_in_edit_mode(ob);
DRWBatchFlag batch_requested = cache->batch_requested;
cache->batch_requested = 0;
@@ -1507,6 +1515,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
me,
is_editmode,
is_paint_mode,
+ is_mode_active,
ob->obmat,
false,
true,
@@ -1524,6 +1533,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
me,
is_editmode,
is_paint_mode,
+ is_mode_active,
ob->obmat,
false,
false,
@@ -1540,6 +1550,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
me,
is_editmode,
is_paint_mode,
+ is_mode_active,
ob->obmat,
true,
false,
@@ -1548,6 +1559,9 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
scene,
ts,
use_hide);
+ /* TODO(jbakker): Work-around for threading issues in 2.90. See T79533, T79038. Needs to be
+ * solved or made permanent in 2.91. Underlying issue still needs to be researched. */
+ BLI_task_graph_work_and_wait(task_graph);
#ifdef DEBUG
drw_mesh_batch_cache_check_available(task_graph, me);
#endif
diff --git a/source/blender/draw/intern/draw_common.c b/source/blender/draw/intern/draw_common.c
index aac9af088de..f80b5bd71fd 100644
--- a/source/blender/draw/intern/draw_common.c
+++ b/source/blender/draw/intern/draw_common.c
@@ -214,10 +214,11 @@ void DRW_globals_update(void)
}
if (G_draw.block_ubo == NULL) {
- G_draw.block_ubo = DRW_uniformbuffer_create(sizeof(GlobalsUboStorage), gb);
+ G_draw.block_ubo = GPU_uniformbuf_create_ex(
+ sizeof(GlobalsUboStorage), gb, "GlobalsUboStorage");
}
- DRW_uniformbuffer_update(G_draw.block_ubo, gb);
+ GPU_uniformbuf_update(G_draw.block_ubo, gb);
if (!G_draw.ramp) {
ColorBand ramp = {0};
diff --git a/source/blender/draw/intern/draw_common.h b/source/blender/draw/intern/draw_common.h
index d6402127e5e..645848e7fe0 100644
--- a/source/blender/draw/intern/draw_common.h
+++ b/source/blender/draw/intern/draw_common.h
@@ -205,11 +205,11 @@ struct DRW_Global {
* Not needed for constant color. */
GlobalsUboStorage block;
/** Define "globalsBlock" uniform for 'block'. */
- struct GPUUniformBuffer *block_ubo;
+ struct GPUUniformBuf *block_ubo;
struct GPUTexture *ramp;
struct GPUTexture *weight_ramp;
- struct GPUUniformBuffer *view_ubo;
+ struct GPUUniformBuf *view_ubo;
};
extern struct DRW_Global G_draw;
diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c
index d90d7d36ebc..da11dacefbd 100644
--- a/source/blender/draw/intern/draw_manager.c
+++ b/source/blender/draw/intern/draw_manager.c
@@ -69,7 +69,7 @@
#include "GPU_immediate.h"
#include "GPU_matrix.h"
#include "GPU_state.h"
-#include "GPU_uniformbuffer.h"
+#include "GPU_uniform_buffer.h"
#include "GPU_viewport.h"
#include "IMB_colormanagement.h"
@@ -84,6 +84,7 @@
#include "draw_color_management.h"
#include "draw_manager_profiling.h"
+#include "draw_manager_testing.h"
#include "draw_manager_text.h"
/* only for callbacks */
@@ -600,7 +601,7 @@ static void drw_viewport_var_init(void)
}
if (G_draw.view_ubo == NULL) {
- G_draw.view_ubo = DRW_uniformbuffer_create(sizeof(DRWViewUboStorage), NULL);
+ G_draw.view_ubo = GPU_uniformbuf_create_ex(sizeof(DRWViewUboStorage), NULL, "G_draw.view_ubo");
}
if (DST.draw_list == NULL) {
@@ -909,7 +910,7 @@ void DRW_cache_free_old_batches(Main *bmain)
for (scene = bmain->scenes.first; scene; scene = scene->id.next) {
for (view_layer = scene->view_layers.first; view_layer; view_layer = view_layer->next) {
- Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, false);
+ Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer);
if (depsgraph == NULL) {
continue;
}
@@ -1314,15 +1315,15 @@ void DRW_draw_callbacks_post_scene(void)
/* annotations - temporary drawing buffer (3d space) */
/* XXX: Or should we use a proper draw/overlay engine for this case? */
if (do_annotations) {
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
/* XXX: as scene->gpd is not copied for COW yet */
ED_annotation_draw_view3d(DEG_get_input_scene(depsgraph), depsgraph, v3d, region, true);
- GPU_depth_test(true);
+ GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
}
drw_debug_draw();
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
ED_region_draw_cb_draw(DST.draw_ctx.evil_C, DST.draw_ctx.region, REGION_DRAW_POST_VIEW);
/* Callback can be nasty and do whatever they want with the state.
@@ -1331,11 +1332,11 @@ void DRW_draw_callbacks_post_scene(void)
/* needed so gizmo isn't obscured */
if ((v3d->gizmo_flag & V3D_GIZMO_HIDE) == 0) {
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
DRW_draw_gizmo_3d();
}
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
drw_engines_draw_text();
DRW_draw_region_info();
@@ -1343,7 +1344,7 @@ void DRW_draw_callbacks_post_scene(void)
/* annotations - temporary drawing buffer (screenspace) */
/* XXX: Or should we use a proper draw/overlay engine for this case? */
if (((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) && (do_annotations)) {
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
/* XXX: as scene->gpd is not copied for COW yet */
ED_annotation_draw_view3d(DEG_get_input_scene(depsgraph), depsgraph, v3d, region, false);
}
@@ -1351,18 +1352,18 @@ void DRW_draw_callbacks_post_scene(void)
if ((v3d->gizmo_flag & V3D_GIZMO_HIDE) == 0) {
/* Draw 2D after region info so we can draw on top of the camera passepartout overlay.
* 'DRW_draw_region_info' sets the projection in pixel-space. */
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
DRW_draw_gizmo_2d();
}
if (G.debug_value > 20 && G.debug_value < 30) {
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
/* local coordinate visible rect inside region, to accommodate overlapping ui */
const rcti *rect = ED_region_visible_rect(DST.draw_ctx.region);
DRW_stats_draw(rect);
}
- GPU_depth_test(true);
+ GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
}
}
@@ -1593,7 +1594,6 @@ void DRW_draw_render_loop_offscreen(struct Depsgraph *depsgraph,
* be to do that in the colormanagmeent shader. */
GPU_offscreen_bind(ofs, false);
GPU_clear_color(0.0f, 0.0f, 0.0f, 1.0f);
- GPU_clear(GPU_COLOR_BIT);
/* Premult Alpha over black background. */
GPU_blend(GPU_BLEND_ALPHA_PREMULT);
}
@@ -1703,7 +1703,7 @@ void DRW_render_gpencil(struct RenderEngine *engine, struct Depsgraph *depsgraph
GPU_viewport_free(DST.viewport);
DRW_state_reset();
- glDisable(GL_DEPTH_TEST);
+ GPU_depth_test(GPU_DEPTH_NONE);
/* Restore Drawing area. */
GPU_framebuffer_restore();
@@ -2438,7 +2438,7 @@ void DRW_draw_depth_object(
GPU_framebuffer_bind(fbl->depth_only_fb);
GPU_framebuffer_clear_depth(fbl->depth_only_fb, 1.0f);
- GPU_depth_test(true);
+ GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
const float(*world_clip_planes)[4] = NULL;
if (RV3D_CLIPPING_ENABLED(v3d, rv3d)) {
@@ -2485,7 +2485,7 @@ void DRW_draw_depth_object(
}
GPU_matrix_set(rv3d->viewmat);
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
GPU_framebuffer_restore();
DRW_opengl_context_disable();
}
@@ -2888,6 +2888,8 @@ void DRW_gpu_render_context_disable(void *UNUSED(re_gpu_context))
GPU_context_active_set(NULL);
}
+/** \} */
+
#ifdef WITH_XR_OPENXR
/* XXX
@@ -2923,4 +2925,17 @@ void DRW_xr_drawing_end(void)
}
#endif
+
+/** \name Internal testing API for gtests
+ * \{ */
+
+#ifdef WITH_OPENGL_DRAW_TESTS
+
+void DRW_draw_state_init_gtests(eGPUShaderConfig sh_cfg)
+{
+ DST.draw_ctx.sh_cfg = sh_cfg;
+}
+
+#endif
+
/** \} */
diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h
index d15a55e7bef..c0bcb0e679f 100644
--- a/source/blender/draw/intern/draw_manager.h
+++ b/source/blender/draw/intern/draw_manager.h
@@ -38,7 +38,7 @@
#include "GPU_drawlist.h"
#include "GPU_framebuffer.h"
#include "GPU_shader.h"
-#include "GPU_uniformbuffer.h"
+#include "GPU_uniform_buffer.h"
#include "GPU_viewport.h"
#include "draw_instance_data.h"
@@ -308,8 +308,8 @@ struct DRWUniform {
};
/* DRW_UNIFORM_BLOCK */
union {
- GPUUniformBuffer *block;
- GPUUniformBuffer **block_ref;
+ GPUUniformBuf *block;
+ GPUUniformBuf **block_ref;
};
/* DRW_UNIFORM_FLOAT_COPY */
float fvalue[4];
diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c
index c12b4a96488..afea820b057 100644
--- a/source/blender/draw/intern/draw_manager_data.c
+++ b/source/blender/draw/intern/draw_manager_data.c
@@ -48,6 +48,7 @@
#include "GPU_buffers.h"
#include "GPU_material.h"
+#include "GPU_uniform_buffer.h"
#include "intern/gpu_codegen.h"
@@ -85,27 +86,12 @@ static void draw_call_sort(DRWCommand *array, DRWCommand *array_tmp, int array_l
memcpy(array, array_tmp, sizeof(*array) * array_len);
}
-GPUUniformBuffer *DRW_uniformbuffer_create(int size, const void *data)
-{
- return GPU_uniformbuffer_create(size, data, NULL);
-}
-
-void DRW_uniformbuffer_update(GPUUniformBuffer *ubo, const void *data)
-{
- GPU_uniformbuffer_update(ubo, data);
-}
-
-void DRW_uniformbuffer_free(GPUUniformBuffer *ubo)
-{
- GPU_uniformbuffer_free(ubo);
-}
-
void drw_resource_buffer_finish(ViewportMemoryPool *vmempool)
{
int chunk_id = DRW_handle_chunk_get(&DST.resource_handle);
int elem_id = DRW_handle_id_get(&DST.resource_handle);
int ubo_len = 1 + chunk_id - ((elem_id == 0) ? 1 : 0);
- size_t list_size = sizeof(GPUUniformBuffer *) * ubo_len;
+ size_t list_size = sizeof(GPUUniformBuf *) * ubo_len;
/* TODO find a better system. currently a lot of obinfos UBO are going to be unused
* if not rendering with Eevee. */
@@ -118,8 +104,8 @@ void drw_resource_buffer_finish(ViewportMemoryPool *vmempool)
/* Remove unnecessary buffers */
for (int i = ubo_len; i < vmempool->ubo_len; i++) {
- GPU_uniformbuffer_free(vmempool->matrices_ubo[i]);
- GPU_uniformbuffer_free(vmempool->obinfos_ubo[i]);
+ GPU_uniformbuf_free(vmempool->matrices_ubo[i]);
+ GPU_uniformbuf_free(vmempool->obinfos_ubo[i]);
}
if (ubo_len != vmempool->ubo_len) {
@@ -133,15 +119,13 @@ void drw_resource_buffer_finish(ViewportMemoryPool *vmempool)
void *data_obmat = BLI_memblock_elem_get(vmempool->obmats, i, 0);
void *data_infos = BLI_memblock_elem_get(vmempool->obinfos, i, 0);
if (vmempool->matrices_ubo[i] == NULL) {
- vmempool->matrices_ubo[i] = GPU_uniformbuffer_create(
- sizeof(DRWObjectMatrix) * DRW_RESOURCE_CHUNK_LEN, data_obmat, NULL);
- vmempool->obinfos_ubo[i] = GPU_uniformbuffer_create(
- sizeof(DRWObjectInfos) * DRW_RESOURCE_CHUNK_LEN, data_infos, NULL);
- }
- else {
- GPU_uniformbuffer_update(vmempool->matrices_ubo[i], data_obmat);
- GPU_uniformbuffer_update(vmempool->obinfos_ubo[i], data_infos);
+ vmempool->matrices_ubo[i] = GPU_uniformbuf_create(sizeof(DRWObjectMatrix) *
+ DRW_RESOURCE_CHUNK_LEN);
+ vmempool->obinfos_ubo[i] = GPU_uniformbuf_create(sizeof(DRWObjectInfos) *
+ DRW_RESOURCE_CHUNK_LEN);
}
+ GPU_uniformbuf_update(vmempool->matrices_ubo[i], data_obmat);
+ GPU_uniformbuf_update(vmempool->obinfos_ubo[i], data_infos);
}
/* Aligned alloc to avoid unaligned memcpy. */
@@ -210,10 +194,10 @@ static void drw_shgroup_uniform_create_ex(DRWShadingGroup *shgroup,
memcpy(uni->fvalue, value, sizeof(float) * length);
break;
case DRW_UNIFORM_BLOCK:
- uni->block = (GPUUniformBuffer *)value;
+ uni->block = (GPUUniformBuf *)value;
break;
case DRW_UNIFORM_BLOCK_REF:
- uni->block_ref = (GPUUniformBuffer **)value;
+ uni->block_ref = (GPUUniformBuf **)value;
break;
case DRW_UNIFORM_TEXTURE:
uni->texture = (GPUTexture *)value;
@@ -279,16 +263,14 @@ void DRW_shgroup_uniform_texture_ref(DRWShadingGroup *shgroup, const char *name,
void DRW_shgroup_uniform_block(DRWShadingGroup *shgroup,
const char *name,
- const GPUUniformBuffer *ubo)
+ const GPUUniformBuf *ubo)
{
BLI_assert(ubo != NULL);
int loc = GPU_shader_get_uniform_block_binding(shgroup->shader, name);
drw_shgroup_uniform_create_ex(shgroup, loc, DRW_UNIFORM_BLOCK, ubo, 0, 0, 1);
}
-void DRW_shgroup_uniform_block_ref(DRWShadingGroup *shgroup,
- const char *name,
- GPUUniformBuffer **ubo)
+void DRW_shgroup_uniform_block_ref(DRWShadingGroup *shgroup, const char *name, GPUUniformBuf **ubo)
{
BLI_assert(ubo != NULL);
int loc = GPU_shader_get_uniform_block_binding(shgroup->shader, name);
@@ -1327,7 +1309,7 @@ void DRW_shgroup_add_material_resources(DRWShadingGroup *grp, struct GPUMaterial
}
}
- GPUUniformBuffer *ubo = GPU_material_uniform_buffer_get(material);
+ GPUUniformBuf *ubo = GPU_material_uniform_buffer_get(material);
if (ubo != NULL) {
DRW_shgroup_uniform_block(grp, GPU_UBO_BLOCK_NAME, ubo);
}
diff --git a/source/blender/draw/intern/draw_manager_exec.c b/source/blender/draw/intern/draw_manager_exec.c
index 44e2eec04d9..8a81b3db7d8 100644
--- a/source/blender/draw/intern/draw_manager_exec.c
+++ b/source/blender/draw/intern/draw_manager_exec.c
@@ -78,6 +78,9 @@ typedef struct DRWCommandsState {
void drw_state_set(DRWState state)
{
+ /* Mask locked state. */
+ state = (~DST.state_lock & state) | (DST.state_lock & DST.state);
+
if (DST.state == state) {
return;
}
@@ -96,6 +99,9 @@ void drw_state_set(DRWState state)
if (state & DRW_STATE_WRITE_COLOR) {
write_mask |= GPU_WRITE_COLOR;
}
+ if (state & DRW_STATE_WRITE_STENCIL_ENABLED) {
+ write_mask |= GPU_WRITE_STENCIL;
+ }
switch (state & (DRW_STATE_CULL_BACK | DRW_STATE_CULL_FRONT)) {
case DRW_STATE_CULL_BACK:
@@ -296,16 +302,13 @@ void DRW_state_reset(void)
DRW_state_reset_ex(DRW_STATE_DEFAULT);
GPU_texture_unbind_all();
- GPU_uniformbuffer_unbind_all();
+ GPU_uniformbuf_unbind_all();
/* Should stay constant during the whole rendering. */
GPU_point_size(5);
GPU_line_smooth(false);
- /* Bypass U.pixelsize factor. */
- glLineWidth(1.0f);
-
- /* Reset blending function */
- glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ /* Bypass U.pixelsize factor by using a factor of 0.0f. Will be clamped to 1.0f. */
+ GPU_line_width(0.0f);
}
/** \} */
@@ -608,11 +611,7 @@ static bool ubo_bindings_validate(DRWShadingGroup *shgroup)
DRWPass *parent_pass = DRW_memblock_elem_from_handle(DST.vmempool->passes,
&shgroup->pass_handle);
- printf("Pass : %s, Shader : %s, Block : %s, Binding %d\n",
- parent_pass->name,
- shgroup->shader->name,
- blockname,
- binding);
+ printf("Pass : %s, Block : %s, Binding %d\n", parent_pass->name, blockname, binding);
}
}
# endif
@@ -651,18 +650,18 @@ static void draw_update_uniforms(DRWShadingGroup *shgroup,
GPU_texture_bind_ex(*uni->texture_ref, uni->sampler_state, uni->location, false);
break;
case DRW_UNIFORM_BLOCK:
- GPU_uniformbuffer_bind(uni->block, uni->location);
+ GPU_uniformbuf_bind(uni->block, uni->location);
break;
case DRW_UNIFORM_BLOCK_REF:
- GPU_uniformbuffer_bind(*uni->block_ref, uni->location);
+ GPU_uniformbuf_bind(*uni->block_ref, uni->location);
break;
case DRW_UNIFORM_BLOCK_OBMATS:
state->obmats_loc = uni->location;
- GPU_uniformbuffer_bind(DST.vmempool->matrices_ubo[0], uni->location);
+ GPU_uniformbuf_bind(DST.vmempool->matrices_ubo[0], uni->location);
break;
case DRW_UNIFORM_BLOCK_OBINFOS:
state->obinfos_loc = uni->location;
- GPU_uniformbuffer_bind(DST.vmempool->obinfos_ubo[0], uni->location);
+ GPU_uniformbuf_bind(DST.vmempool->obinfos_ubo[0], uni->location);
break;
case DRW_UNIFORM_RESOURCE_CHUNK:
state->chunkid_loc = uni->location;
@@ -762,13 +761,8 @@ static void draw_call_resource_bind(DRWCommandsState *state, const DRWResourceHa
/* Front face is not a resource but it is inside the resource handle. */
bool neg_scale = DRW_handle_negative_scale_get(handle);
if (neg_scale != state->neg_scale) {
- if (DST.view_active->is_inverted) {
- glFrontFace(neg_scale ? GL_CCW : GL_CW);
- }
- else {
- glFrontFace(neg_scale ? GL_CW : GL_CCW);
- }
state->neg_scale = neg_scale;
+ GPU_front_facing(neg_scale != DST.view_active->is_inverted);
}
int chunk = DRW_handle_chunk_get(handle);
@@ -777,12 +771,12 @@ static void draw_call_resource_bind(DRWCommandsState *state, const DRWResourceHa
GPU_shader_uniform_int(DST.shader, state->chunkid_loc, chunk);
}
if (state->obmats_loc != -1) {
- GPU_uniformbuffer_unbind(DST.vmempool->matrices_ubo[state->resource_chunk]);
- GPU_uniformbuffer_bind(DST.vmempool->matrices_ubo[chunk], state->obmats_loc);
+ GPU_uniformbuf_unbind(DST.vmempool->matrices_ubo[state->resource_chunk]);
+ GPU_uniformbuf_bind(DST.vmempool->matrices_ubo[chunk], state->obmats_loc);
}
if (state->obinfos_loc != -1) {
- GPU_uniformbuffer_unbind(DST.vmempool->obinfos_ubo[state->resource_chunk]);
- GPU_uniformbuffer_bind(DST.vmempool->obinfos_ubo[chunk], state->obinfos_loc);
+ GPU_uniformbuf_unbind(DST.vmempool->obinfos_ubo[state->resource_chunk]);
+ GPU_uniformbuf_bind(DST.vmempool->obinfos_ubo[chunk], state->obinfos_loc);
}
state->resource_chunk = chunk;
}
@@ -898,13 +892,13 @@ static void draw_call_batching_finish(DRWShadingGroup *shgroup, DRWCommandsState
/* Reset state */
if (state->neg_scale) {
- glFrontFace(DST.view_active->is_inverted ? GL_CW : GL_CCW);
+ GPU_front_facing(DST.view_active->is_inverted);
}
if (state->obmats_loc != -1) {
- GPU_uniformbuffer_unbind(DST.vmempool->matrices_ubo[state->resource_chunk]);
+ GPU_uniformbuf_unbind(DST.vmempool->matrices_ubo[state->resource_chunk]);
}
if (state->obinfos_loc != -1) {
- GPU_uniformbuffer_unbind(DST.vmempool->obinfos_ubo[state->resource_chunk]);
+ GPU_uniformbuf_unbind(DST.vmempool->obinfos_ubo[state->resource_chunk]);
}
}
@@ -934,7 +928,7 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state)
/* Unbinding can be costly. Skip in normal condition. */
if (G.debug & G_DEBUG_GPU) {
GPU_texture_unbind_all();
- GPU_uniformbuffer_unbind_all();
+ GPU_uniformbuf_unbind_all();
}
}
GPU_shader_bind(shgroup->shader);
@@ -1070,7 +1064,7 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state)
static void drw_update_view(void)
{
/* TODO(fclem) update a big UBO and only bind ranges here. */
- DRW_uniformbuffer_update(G_draw.view_ubo, &DST.view_active->storage);
+ GPU_uniformbuf_update(G_draw.view_ubo, &DST.view_active->storage);
/* TODO get rid of this. */
DST.view_storage_cpy = DST.view_active->storage;
@@ -1110,7 +1104,7 @@ static void drw_draw_pass_ex(DRWPass *pass,
drw_state_validate();
if (DST.view_active->is_inverted) {
- glFrontFace(GL_CW);
+ GPU_front_facing(true);
}
DRW_stats_query_start(pass->name);
@@ -1147,7 +1141,7 @@ static void drw_draw_pass_ex(DRWPass *pass,
/* Reset default. */
if (DST.view_active->is_inverted) {
- glFrontFace(GL_CCW);
+ GPU_front_facing(false);
}
DRW_stats_query_end();
diff --git a/source/blender/draw/intern/draw_manager_testing.h b/source/blender/draw/intern/draw_manager_testing.h
new file mode 100644
index 00000000000..f8b5dd5af46
--- /dev/null
+++ b/source/blender/draw/intern/draw_manager_testing.h
@@ -0,0 +1,39 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2016, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup draw
+ */
+
+/* Internal API only for test cases. */
+
+#pragma once
+
+#include "GPU_shader.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef WITH_OPENGL_DRAW_TESTS
+void DRW_draw_state_init_gtests(eGPUShaderConfig sh_cfg);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/draw/intern/draw_view.c b/source/blender/draw/intern/draw_view.c
index 0dc35d44788..d01e1a51080 100644
--- a/source/blender/draw/intern/draw_view.c
+++ b/source/blender/draw/intern/draw_view.c
@@ -105,7 +105,7 @@ void DRW_draw_cursor(void)
GPU_color_mask(true, true, true, true);
GPU_depth_mask(false);
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
if (is_cursor_visible(draw_ctx, scene, view_layer)) {
int co[2];
diff --git a/source/blender/draw/tests/shaders_test.cc b/source/blender/draw/tests/shaders_test.cc
new file mode 100644
index 00000000000..b2f1020ed98
--- /dev/null
+++ b/source/blender/draw/tests/shaders_test.cc
@@ -0,0 +1,270 @@
+/* Apache License, Version 2.0 */
+
+#include "testing/testing.h"
+
+#include "intern/draw_manager_testing.h"
+
+#include "GPU_context.h"
+#include "GPU_init_exit.h"
+#include "GPU_shader.h"
+
+#include "GHOST_C-api.h"
+
+#include "engines/eevee/eevee_private.h"
+#include "engines/gpencil/gpencil_engine.h"
+#include "engines/overlay/overlay_private.h"
+#include "engines/workbench/workbench_private.h"
+
+/* Base class for draw test cases. It will setup and tear down the GPU part around each test. */
+class DrawTest : public ::testing::Test {
+ private:
+ GHOST_SystemHandle ghost_system;
+ GHOST_ContextHandle ghost_context;
+ GPUContext *context;
+
+ void SetUp() override
+ {
+ ghost_system = GHOST_CreateSystem();
+ ghost_context = GHOST_CreateOpenGLContext(ghost_system);
+ context = GPU_context_create(0);
+ GPU_init();
+ DRW_draw_state_init_gtests(GPU_SHADER_CFG_DEFAULT);
+ }
+
+ void TearDown() override
+ {
+ GPU_exit();
+ GPU_context_discard(context);
+ GHOST_DisposeOpenGLContext(ghost_system, ghost_context);
+ GHOST_DisposeSystem(ghost_system);
+ }
+};
+
+TEST_F(DrawTest, workbench_glsl_shaders)
+{
+ workbench_shader_library_ensure();
+ DRW_draw_state_init_gtests(GPU_SHADER_CFG_DEFAULT);
+
+ const int MAX_WPD = 6;
+ WORKBENCH_PrivateData wpds[MAX_WPD];
+
+ wpds[0].sh_cfg = GPU_SHADER_CFG_DEFAULT;
+ wpds[0].shading.light = V3D_LIGHTING_FLAT;
+ wpds[1].sh_cfg = GPU_SHADER_CFG_DEFAULT;
+ wpds[1].shading.light = V3D_LIGHTING_MATCAP;
+ wpds[2].sh_cfg = GPU_SHADER_CFG_DEFAULT;
+ wpds[2].shading.light = V3D_LIGHTING_STUDIO;
+ wpds[3].sh_cfg = GPU_SHADER_CFG_CLIPPED;
+ wpds[3].shading.light = V3D_LIGHTING_FLAT;
+ wpds[4].sh_cfg = GPU_SHADER_CFG_CLIPPED;
+ wpds[4].shading.light = V3D_LIGHTING_MATCAP;
+ wpds[5].sh_cfg = GPU_SHADER_CFG_CLIPPED;
+ wpds[5].shading.light = V3D_LIGHTING_STUDIO;
+
+ for (int wpd_index = 0; wpd_index < MAX_WPD; wpd_index++) {
+ WORKBENCH_PrivateData *wpd = &wpds[wpd_index];
+ EXPECT_NE(workbench_shader_opaque_get(wpd, WORKBENCH_DATATYPE_MESH), nullptr);
+ EXPECT_NE(workbench_shader_opaque_get(wpd, WORKBENCH_DATATYPE_HAIR), nullptr);
+ EXPECT_NE(workbench_shader_opaque_get(wpd, WORKBENCH_DATATYPE_POINTCLOUD), nullptr);
+ EXPECT_NE(workbench_shader_opaque_image_get(wpd, WORKBENCH_DATATYPE_MESH, false), nullptr);
+ EXPECT_NE(workbench_shader_opaque_image_get(wpd, WORKBENCH_DATATYPE_MESH, true), nullptr);
+ EXPECT_NE(workbench_shader_opaque_image_get(wpd, WORKBENCH_DATATYPE_HAIR, false), nullptr);
+ EXPECT_NE(workbench_shader_opaque_image_get(wpd, WORKBENCH_DATATYPE_HAIR, true), nullptr);
+ EXPECT_NE(workbench_shader_opaque_image_get(wpd, WORKBENCH_DATATYPE_POINTCLOUD, false),
+ nullptr);
+ EXPECT_NE(workbench_shader_opaque_image_get(wpd, WORKBENCH_DATATYPE_POINTCLOUD, true),
+ nullptr);
+ EXPECT_NE(workbench_shader_composite_get(wpd), nullptr);
+ EXPECT_NE(workbench_shader_merge_infront_get(wpd), nullptr);
+
+ EXPECT_NE(workbench_shader_transparent_get(wpd, WORKBENCH_DATATYPE_MESH), nullptr);
+ EXPECT_NE(workbench_shader_transparent_get(wpd, WORKBENCH_DATATYPE_HAIR), nullptr);
+ EXPECT_NE(workbench_shader_transparent_get(wpd, WORKBENCH_DATATYPE_POINTCLOUD), nullptr);
+ EXPECT_NE(workbench_shader_transparent_image_get(wpd, WORKBENCH_DATATYPE_MESH, false),
+ nullptr);
+ EXPECT_NE(workbench_shader_transparent_image_get(wpd, WORKBENCH_DATATYPE_MESH, true), nullptr);
+ EXPECT_NE(workbench_shader_transparent_image_get(wpd, WORKBENCH_DATATYPE_HAIR, false),
+ nullptr);
+ EXPECT_NE(workbench_shader_transparent_image_get(wpd, WORKBENCH_DATATYPE_HAIR, true), nullptr);
+ EXPECT_NE(workbench_shader_transparent_image_get(wpd, WORKBENCH_DATATYPE_POINTCLOUD, false),
+ nullptr);
+ EXPECT_NE(workbench_shader_transparent_image_get(wpd, WORKBENCH_DATATYPE_POINTCLOUD, true),
+ nullptr);
+ EXPECT_NE(workbench_shader_transparent_resolve_get(wpd), nullptr);
+ }
+
+ EXPECT_NE(workbench_shader_shadow_pass_get(false), nullptr);
+ EXPECT_NE(workbench_shader_shadow_pass_get(true), nullptr);
+ EXPECT_NE(workbench_shader_shadow_fail_get(false, false), nullptr);
+ EXPECT_NE(workbench_shader_shadow_fail_get(false, true), nullptr);
+ EXPECT_NE(workbench_shader_shadow_fail_get(true, false), nullptr);
+ EXPECT_NE(workbench_shader_shadow_fail_get(true, true), nullptr);
+
+ /* NOTE: workbench_shader_cavity_get(false, false) isn't a valid option. */
+ EXPECT_NE(workbench_shader_cavity_get(false, true), nullptr);
+ EXPECT_NE(workbench_shader_cavity_get(true, false), nullptr);
+ EXPECT_NE(workbench_shader_cavity_get(true, true), nullptr);
+ EXPECT_NE(workbench_shader_outline_get(), nullptr);
+
+ EXPECT_NE(workbench_shader_antialiasing_accumulation_get(), nullptr);
+ EXPECT_NE(workbench_shader_antialiasing_get(0), nullptr);
+ EXPECT_NE(workbench_shader_antialiasing_get(1), nullptr);
+ EXPECT_NE(workbench_shader_antialiasing_get(2), nullptr);
+
+ EXPECT_NE(workbench_shader_volume_get(false, false, false, false), nullptr);
+ EXPECT_NE(workbench_shader_volume_get(false, false, false, true), nullptr);
+ EXPECT_NE(workbench_shader_volume_get(false, false, true, false), nullptr);
+ EXPECT_NE(workbench_shader_volume_get(false, false, true, true), nullptr);
+ EXPECT_NE(workbench_shader_volume_get(false, true, false, false), nullptr);
+ EXPECT_NE(workbench_shader_volume_get(false, true, false, true), nullptr);
+ EXPECT_NE(workbench_shader_volume_get(false, true, true, false), nullptr);
+ EXPECT_NE(workbench_shader_volume_get(false, true, true, true), nullptr);
+ EXPECT_NE(workbench_shader_volume_get(true, false, false, false), nullptr);
+ EXPECT_NE(workbench_shader_volume_get(true, false, false, true), nullptr);
+ EXPECT_NE(workbench_shader_volume_get(true, false, true, false), nullptr);
+ EXPECT_NE(workbench_shader_volume_get(true, false, true, true), nullptr);
+ EXPECT_NE(workbench_shader_volume_get(true, true, false, false), nullptr);
+ EXPECT_NE(workbench_shader_volume_get(true, true, false, true), nullptr);
+ EXPECT_NE(workbench_shader_volume_get(true, true, true, false), nullptr);
+ EXPECT_NE(workbench_shader_volume_get(true, true, true, true), nullptr);
+
+ GPUShader *dof_prepare_sh;
+ GPUShader *dof_downsample_sh;
+ GPUShader *dof_blur1_sh;
+ GPUShader *dof_blur2_sh;
+ GPUShader *dof_resolve_sh;
+ workbench_shader_depth_of_field_get(
+ &dof_prepare_sh, &dof_downsample_sh, &dof_blur1_sh, &dof_blur2_sh, &dof_resolve_sh);
+ EXPECT_NE(dof_prepare_sh, nullptr);
+ EXPECT_NE(dof_downsample_sh, nullptr);
+ EXPECT_NE(dof_blur1_sh, nullptr);
+ EXPECT_NE(dof_blur2_sh, nullptr);
+ EXPECT_NE(dof_resolve_sh, nullptr);
+
+ workbench_shader_free();
+}
+
+TEST_F(DrawTest, gpencil_glsl_shaders)
+{
+ EXPECT_NE(GPENCIL_shader_antialiasing(0), nullptr);
+ EXPECT_NE(GPENCIL_shader_antialiasing(1), nullptr);
+ EXPECT_NE(GPENCIL_shader_antialiasing(2), nullptr);
+
+ EXPECT_NE(GPENCIL_shader_geometry_get(), nullptr);
+ EXPECT_NE(GPENCIL_shader_layer_blend_get(), nullptr);
+ EXPECT_NE(GPENCIL_shader_mask_invert_get(), nullptr);
+ EXPECT_NE(GPENCIL_shader_depth_merge_get(), nullptr);
+ EXPECT_NE(GPENCIL_shader_fx_blur_get(), nullptr);
+ EXPECT_NE(GPENCIL_shader_fx_colorize_get(), nullptr);
+ EXPECT_NE(GPENCIL_shader_fx_composite_get(), nullptr);
+ EXPECT_NE(GPENCIL_shader_fx_transform_get(), nullptr);
+ EXPECT_NE(GPENCIL_shader_fx_glow_get(), nullptr);
+ EXPECT_NE(GPENCIL_shader_fx_pixelize_get(), nullptr);
+ EXPECT_NE(GPENCIL_shader_fx_rim_get(), nullptr);
+ EXPECT_NE(GPENCIL_shader_fx_shadow_get(), nullptr);
+
+ GPENCIL_shader_free();
+}
+
+TEST_F(DrawTest, overlay_glsl_shaders)
+{
+ for (int i = 0; i < 2; i++) {
+ eGPUShaderConfig sh_cfg = i == 0 ? GPU_SHADER_CFG_DEFAULT : GPU_SHADER_CFG_CLIPPED;
+ DRW_draw_state_init_gtests(sh_cfg);
+ EXPECT_NE(OVERLAY_shader_antialiasing(), nullptr);
+ EXPECT_NE(OVERLAY_shader_armature_degrees_of_freedom_wire(), nullptr);
+ EXPECT_NE(OVERLAY_shader_armature_degrees_of_freedom_solid(), nullptr);
+ EXPECT_NE(OVERLAY_shader_armature_envelope(false), nullptr);
+ EXPECT_NE(OVERLAY_shader_armature_envelope(true), nullptr);
+ EXPECT_NE(OVERLAY_shader_armature_shape(false), nullptr);
+ EXPECT_NE(OVERLAY_shader_armature_shape(true), nullptr);
+ EXPECT_NE(OVERLAY_shader_armature_shape_wire(), nullptr);
+ EXPECT_NE(OVERLAY_shader_armature_sphere(false), nullptr);
+ EXPECT_NE(OVERLAY_shader_armature_sphere(true), nullptr);
+ EXPECT_NE(OVERLAY_shader_armature_stick(), nullptr);
+ EXPECT_NE(OVERLAY_shader_armature_wire(), nullptr);
+ EXPECT_NE(OVERLAY_shader_background(), nullptr);
+ EXPECT_NE(OVERLAY_shader_clipbound(), nullptr);
+ EXPECT_NE(OVERLAY_shader_depth_only(), nullptr);
+ EXPECT_NE(OVERLAY_shader_edit_curve_handle(), nullptr);
+ EXPECT_NE(OVERLAY_shader_edit_curve_point(), nullptr);
+ EXPECT_NE(OVERLAY_shader_edit_curve_wire(), nullptr);
+ EXPECT_NE(OVERLAY_shader_edit_gpencil_guide_point(), nullptr);
+ EXPECT_NE(OVERLAY_shader_edit_gpencil_point(), nullptr);
+ EXPECT_NE(OVERLAY_shader_edit_gpencil_wire(), nullptr);
+ EXPECT_NE(OVERLAY_shader_edit_lattice_point(), nullptr);
+ EXPECT_NE(OVERLAY_shader_edit_lattice_wire(), nullptr);
+ EXPECT_NE(OVERLAY_shader_edit_mesh_analysis(), nullptr);
+ EXPECT_NE(OVERLAY_shader_edit_mesh_edge(false), nullptr);
+ EXPECT_NE(OVERLAY_shader_edit_mesh_edge(true), nullptr);
+ EXPECT_NE(OVERLAY_shader_edit_mesh_face(), nullptr);
+ EXPECT_NE(OVERLAY_shader_edit_mesh_facedot(), nullptr);
+ EXPECT_NE(OVERLAY_shader_edit_mesh_normal(), nullptr);
+ EXPECT_NE(OVERLAY_shader_edit_mesh_skin_root(), nullptr);
+ EXPECT_NE(OVERLAY_shader_edit_mesh_vert(), nullptr);
+ EXPECT_NE(OVERLAY_shader_edit_particle_strand(), nullptr);
+ EXPECT_NE(OVERLAY_shader_edit_particle_point(), nullptr);
+ EXPECT_NE(OVERLAY_shader_extra(false), nullptr);
+ EXPECT_NE(OVERLAY_shader_extra(true), nullptr);
+ EXPECT_NE(OVERLAY_shader_extra_groundline(), nullptr);
+ EXPECT_NE(OVERLAY_shader_extra_wire(false, false), nullptr);
+ EXPECT_NE(OVERLAY_shader_extra_wire(false, true), nullptr);
+ EXPECT_NE(OVERLAY_shader_extra_wire(true, false), nullptr);
+ EXPECT_NE(OVERLAY_shader_extra_wire(true, true), nullptr);
+ EXPECT_NE(OVERLAY_shader_extra_loose_point(), nullptr);
+ EXPECT_NE(OVERLAY_shader_extra_point(), nullptr);
+ EXPECT_NE(OVERLAY_shader_facing(), nullptr);
+ EXPECT_NE(OVERLAY_shader_gpencil_canvas(), nullptr);
+ EXPECT_NE(OVERLAY_shader_grid(), nullptr);
+ EXPECT_NE(OVERLAY_shader_image(), nullptr);
+ EXPECT_NE(OVERLAY_shader_motion_path_line(), nullptr);
+ EXPECT_NE(OVERLAY_shader_motion_path_vert(), nullptr);
+ EXPECT_NE(OVERLAY_shader_uniform_color(), nullptr);
+ EXPECT_NE(OVERLAY_shader_outline_prepass(false), nullptr);
+ EXPECT_NE(OVERLAY_shader_outline_prepass(true), nullptr);
+ EXPECT_NE(OVERLAY_shader_outline_prepass_gpencil(), nullptr);
+ EXPECT_NE(OVERLAY_shader_outline_prepass_pointcloud(), nullptr);
+ EXPECT_NE(OVERLAY_shader_extra_grid(), nullptr);
+ EXPECT_NE(OVERLAY_shader_outline_detect(), nullptr);
+ EXPECT_NE(OVERLAY_shader_paint_face(), nullptr);
+ EXPECT_NE(OVERLAY_shader_paint_point(), nullptr);
+ EXPECT_NE(OVERLAY_shader_paint_texture(), nullptr);
+ EXPECT_NE(OVERLAY_shader_paint_vertcol(), nullptr);
+ EXPECT_NE(OVERLAY_shader_paint_weight(), nullptr);
+ EXPECT_NE(OVERLAY_shader_paint_wire(), nullptr);
+ EXPECT_NE(OVERLAY_shader_particle_dot(), nullptr);
+ EXPECT_NE(OVERLAY_shader_particle_shape(), nullptr);
+ EXPECT_NE(OVERLAY_shader_sculpt_mask(), nullptr);
+ EXPECT_NE(OVERLAY_shader_volume_velocity(false), nullptr);
+ EXPECT_NE(OVERLAY_shader_volume_velocity(true), nullptr);
+ EXPECT_NE(OVERLAY_shader_wireframe(false), nullptr);
+ EXPECT_NE(OVERLAY_shader_wireframe(true), nullptr);
+ EXPECT_NE(OVERLAY_shader_wireframe_select(), nullptr);
+ EXPECT_NE(OVERLAY_shader_xray_fade(), nullptr);
+ }
+
+ OVERLAY_shader_free();
+}
+
+TEST_F(DrawTest, eevee_glsl_shaders_static)
+{
+ EEVEE_shaders_lightprobe_shaders_init();
+ EEVEE_shaders_material_shaders_init();
+
+ EXPECT_NE(EEVEE_shaders_probe_filter_glossy_sh_get(), nullptr);
+ EXPECT_NE(EEVEE_shaders_probe_filter_diffuse_sh_get(), nullptr);
+ EXPECT_NE(EEVEE_shaders_probe_filter_visibility_sh_get(), nullptr);
+ EXPECT_NE(EEVEE_shaders_probe_grid_fill_sh_get(), nullptr);
+ EXPECT_NE(EEVEE_shaders_probe_planar_downsample_sh_get(), nullptr);
+ EXPECT_NE(EEVEE_shaders_studiolight_probe_sh_get(), nullptr);
+ EXPECT_NE(EEVEE_shaders_studiolight_background_sh_get(), nullptr);
+ EXPECT_NE(EEVEE_shaders_probe_cube_display_sh_get(), nullptr);
+ EXPECT_NE(EEVEE_shaders_probe_grid_display_sh_get(), nullptr);
+ EXPECT_NE(EEVEE_shaders_probe_planar_display_sh_get(), nullptr);
+ EXPECT_NE(EEVEE_shaders_update_noise_sh_get(), nullptr);
+ EXPECT_NE(EEVEE_shaders_velocity_resolve_sh_get(), nullptr);
+ EXPECT_NE(EEVEE_shaders_taa_resolve_sh_get(EFFECT_TAA), nullptr);
+ EXPECT_NE(EEVEE_shaders_taa_resolve_sh_get(EFFECT_TAA_REPROJECT), nullptr);
+
+ EEVEE_shaders_free();
+} \ No newline at end of file
diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c
index 8280b58c21a..ddd389a5348 100644
--- a/source/blender/editors/animation/anim_filter.c
+++ b/source/blender/editors/animation/anim_filter.c
@@ -1555,7 +1555,7 @@ static size_t animfilter_nla(bAnimContext *UNUSED(ac),
next = nlt->next;
}
- /* if we're in NLA-tweakmode, don't show this track if it was disabled
+ /* If we're in NLA-tweak-mode, don't show this track if it was disabled
* (due to tweaking) for now:
* - active track should still get shown though (even though it has disabled flag set)
*/
diff --git a/source/blender/editors/armature/armature_relations.c b/source/blender/editors/armature/armature_relations.c
index a737916e9a2..2efb7315b89 100644
--- a/source/blender/editors/armature/armature_relations.c
+++ b/source/blender/editors/armature/armature_relations.c
@@ -304,6 +304,10 @@ int ED_armature_join_objects_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
+ /* Inverse transform for all selected armatures in this object,
+ * See #object_join_exec for detailed comment on why the safe version is used. */
+ invert_m4_m4_safe_ortho(oimat, ob_active->obmat);
+
/* Get edit-bones of active armature to add edit-bones to */
ED_armature_to_edit(arm);
@@ -334,7 +338,6 @@ int ED_armature_join_objects_exec(bContext *C, wmOperator *op)
// BASACT->flag &= ~OB_MODE_POSE;
/* Find the difference matrix */
- invert_m4_m4(oimat, ob_active->obmat);
mul_m4_m4m4(mat, oimat, ob_iter->obmat);
/* Copy bones and posechannels from the object to the edit armature */
@@ -435,6 +438,7 @@ int ED_armature_join_objects_exec(bContext *C, wmOperator *op)
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
+ WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene);
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/armature/meshlaplacian.c b/source/blender/editors/armature/meshlaplacian.c
index 75ffd31854a..ceacd3333d5 100644
--- a/source/blender/editors/armature/meshlaplacian.c
+++ b/source/blender/editors/armature/meshlaplacian.c
@@ -37,6 +37,7 @@
#include "BKE_bvhutils.h"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
+#include "BKE_mesh_wrapper.h"
#include "BKE_modifier.h"
#include "ED_armature.h"
@@ -1761,6 +1762,9 @@ void ED_mesh_deform_bind_callback(MeshDeformModifierData *mmd,
memset(&mdb, 0, sizeof(MeshDeformBind));
+ /* No need to support other kinds of mesh data as binding is a one-off action. */
+ BKE_mesh_wrapper_ensure_mdata(cagemesh);
+
/* get mesh and cage mesh */
mdb.vertexcos = MEM_callocN(sizeof(float[3]) * totvert, "MeshDeformCos");
mdb.totvert = totvert;
diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c
index d113e0693a4..fb102574a85 100644
--- a/source/blender/editors/curve/editcurve.c
+++ b/source/blender/editors/curve/editcurve.c
@@ -2172,12 +2172,22 @@ bool ed_editnurb_extrude_flag(EditNurb *editnurb, const short flag)
return ok;
}
+static void calc_duplicate_actnurb(const ListBase *editnurb, const ListBase *newnurb, Curve *cu)
+{
+ cu->actnu = BLI_listbase_count(editnurb) + BLI_listbase_count(newnurb);
+}
+
static bool calc_duplicate_actvert(
const ListBase *editnurb, const ListBase *newnurb, Curve *cu, int start, int end, int vert)
{
+ if (cu->actvert == -1) {
+ calc_duplicate_actnurb(editnurb, newnurb, cu);
+ return true;
+ }
+
if ((start <= cu->actvert) && (end > cu->actvert)) {
+ calc_duplicate_actnurb(editnurb, newnurb, cu);
cu->actvert = vert;
- cu->actnu = BLI_listbase_count(editnurb) + BLI_listbase_count(newnurb);
return true;
}
return false;
@@ -2427,26 +2437,31 @@ static void adduplicateflagNurb(
}
if (cu->actnu == i) {
- for (b = 0, diffa = 0; b < newv; b++, diffa += nu->pntsu - newu) {
- starta = b * nu->pntsu + a;
- if (calc_duplicate_actvert(editnurb,
- newnurb,
- cu,
- cu->actvert,
- starta,
- cu->actvert % nu->pntsu + newu +
- b * newnu->pntsu)) {
- /* actvert in cyclicu selection */
- break;
- }
- if (calc_duplicate_actvert(editnurb,
- newnurb,
- cu,
- starta,
- starta + newu,
- cu->actvert - starta + b * newnu->pntsu)) {
- /* actvert in 'current' iteration selection */
- break;
+ if (cu->actvert == -1) {
+ calc_duplicate_actnurb(editnurb, newnurb, cu);
+ }
+ else {
+ for (b = 0, diffa = 0; b < newv; b++, diffa += nu->pntsu - newu) {
+ starta = b * nu->pntsu + a;
+ if (calc_duplicate_actvert(editnurb,
+ newnurb,
+ cu,
+ cu->actvert,
+ starta,
+ cu->actvert % nu->pntsu + newu +
+ b * newnu->pntsu)) {
+ /* actvert in cyclicu selection */
+ break;
+ }
+ if (calc_duplicate_actvert(editnurb,
+ newnurb,
+ cu,
+ starta,
+ starta + newu,
+ cu->actvert - starta + b * newnu->pntsu)) {
+ /* actvert in 'current' iteration selection */
+ break;
+ }
}
}
}
@@ -2474,16 +2489,21 @@ static void adduplicateflagNurb(
/* general case if not handled by cyclicu or cyclicv */
if (cu->actnu == i) {
- for (b = 0, diffa = 0; b < newv; b++, diffa += nu->pntsu - newu) {
- starta = b * nu->pntsu + a;
- if (calc_duplicate_actvert(editnurb,
- newnurb,
- cu,
- starta,
- starta + newu,
- cu->actvert - (a / nu->pntsu * nu->pntsu + diffa +
- (starta % nu->pntsu)))) {
- break;
+ if (cu->actvert == -1) {
+ calc_duplicate_actnurb(editnurb, newnurb, cu);
+ }
+ else {
+ for (b = 0, diffa = 0; b < newv; b++, diffa += nu->pntsu - newu) {
+ starta = b * nu->pntsu + a;
+ if (calc_duplicate_actvert(editnurb,
+ newnurb,
+ cu,
+ starta,
+ starta + newu,
+ cu->actvert - (a / nu->pntsu * nu->pntsu + diffa +
+ (starta % nu->pntsu)))) {
+ break;
+ }
}
}
}
@@ -2510,15 +2530,20 @@ static void adduplicateflagNurb(
/* check for actvert in the unused cyclicuv selection */
if (cu->actnu == i) {
- for (b = 0, diffa = 0; b < newv; b++, diffa += nu->pntsu - newu) {
- starta = b * nu->pntsu;
- if (calc_duplicate_actvert(editnurb,
- newnurb,
- cu,
- starta,
- starta + newu,
- cu->actvert - (diffa + (starta % nu->pntsu)))) {
- break;
+ if (cu->actvert == -1) {
+ calc_duplicate_actnurb(editnurb, newnurb, cu);
+ }
+ else {
+ for (b = 0, diffa = 0; b < newv; b++, diffa += nu->pntsu - newu) {
+ starta = b * nu->pntsu;
+ if (calc_duplicate_actvert(editnurb,
+ newnurb,
+ cu,
+ starta,
+ starta + newu,
+ cu->actvert - (diffa + (starta % nu->pntsu)))) {
+ break;
+ }
}
}
}
@@ -6923,8 +6948,9 @@ int ED_curve_join_objects_exec(bContext *C, wmOperator *op)
BLI_listbase_clear(&tempbase);
- /* trasnform all selected curves inverse in obact */
- invert_m4_m4(imat, ob_active->obmat);
+ /* Inverse transform for all selected curves in this object,
+ * See #object_join_exec for detailed comment on why the safe version is used. */
+ invert_m4_m4_safe_ortho(imat, ob_active->obmat);
CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) {
if (ob_iter->type == ob_active->type) {
@@ -6988,6 +7014,7 @@ int ED_curve_join_objects_exec(bContext *C, wmOperator *op)
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
+ WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene);
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/curve/editcurve_paint.c b/source/blender/editors/curve/editcurve_paint.c
index 2dac273501d..889041daacf 100644
--- a/source/blender/editors/curve/editcurve_paint.c
+++ b/source/blender/editors/curve/editcurve_paint.c
@@ -419,7 +419,7 @@ static void curve_draw_stroke_3d(const struct bContext *UNUSED(C),
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
GPU_blend(GPU_BLEND_ALPHA);
GPU_line_smooth(true);
GPU_line_width(3.0f);
@@ -441,7 +441,7 @@ static void curve_draw_stroke_3d(const struct bContext *UNUSED(C),
immEnd();
/* Reset defaults */
- GPU_depth_test(true);
+ GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
GPU_blend(GPU_BLEND_NONE);
GPU_line_smooth(false);
diff --git a/source/blender/editors/gizmo_library/gizmo_draw_utils.c b/source/blender/editors/gizmo_library/gizmo_draw_utils.c
index 033673a99a8..2896aa25930 100644
--- a/source/blender/editors/gizmo_library/gizmo_draw_utils.c
+++ b/source/blender/editors/gizmo_library/gizmo_draw_utils.c
@@ -84,13 +84,13 @@ void wm_gizmo_geometryinfo_draw(const GizmoGeomInfo *info,
* since it causes issues leaving the GL state modified. */
#if 0
GPU_face_culling(GPU_CULL_BACK);
- GPU_depth_test(true);
+ GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
#endif
GPU_batch_draw(batch);
#if 0
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
GPU_face_culling(GPU_CULL_NONE);
#endif
diff --git a/source/blender/editors/gpencil/annotate_draw.c b/source/blender/editors/gpencil/annotate_draw.c
index 654d1b87918..b6cbbe7712b 100644
--- a/source/blender/editors/gpencil/annotate_draw.c
+++ b/source/blender/editors/gpencil/annotate_draw.c
@@ -556,7 +556,7 @@ static void annotation_draw_strokes(const bGPDframe *gpf,
const int no_xray = (dflag & GP_DRAWDATA_NO_XRAY);
if (no_xray) {
- GPU_depth_test(true);
+ GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
/* first arg is normally rv3d->dist, but this isn't
* available here and seems to work quite well without */
@@ -574,7 +574,7 @@ static void annotation_draw_strokes(const bGPDframe *gpf,
}
if (no_xray) {
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
GPU_polygon_offset(0.0f, 0.0f);
}
diff --git a/source/blender/editors/gpencil/drawgpencil.c b/source/blender/editors/gpencil/drawgpencil.c
index 9d11c1c2a25..93767127cc7 100644
--- a/source/blender/editors/gpencil/drawgpencil.c
+++ b/source/blender/editors/gpencil/drawgpencil.c
@@ -348,7 +348,7 @@ static void gpencil_draw_strokes(tGPDdraw *tgpw)
const int no_xray = (tgpw->dflag & GP_DRAWDATA_NO_XRAY);
if (no_xray) {
- GPU_depth_test(true);
+ GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
/* first arg is normally rv3d->dist, but this isn't
* available here and seems to work quite well without */
@@ -393,7 +393,7 @@ static void gpencil_draw_strokes(tGPDdraw *tgpw)
}
}
if (no_xray) {
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
GPU_polygon_offset(0.0f, 0.0f);
}
diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c
index 348fb614977..6c003b85edd 100644
--- a/source/blender/editors/gpencil/gpencil_data.c
+++ b/source/blender/editors/gpencil/gpencil_data.c
@@ -2819,7 +2819,10 @@ int ED_gpencil_join_objects_exec(bContext *C, wmOperator *op)
sub_v3_v3v3(offset_global, ob_active->loc, ob_iter->obmat[3]);
copy_m3_m4(bmat, ob_active->obmat);
- invert_m3_m3(imat, bmat);
+
+ /* Inverse transform for all selected curves in this object,
+ * See #object_join_exec for detailed comment on why the safe version is used. */
+ invert_m3_m3_safe_ortho(imat, bmat);
mul_m3_v3(imat, offset_global);
mul_v3_m3v3(offset_local, imat, offset_global);
@@ -2830,7 +2833,7 @@ int ED_gpencil_join_objects_exec(bContext *C, wmOperator *op)
/* recalculate all stroke points */
BKE_gpencil_parent_matrix_get(depsgraph, ob_iter, gpl_src, diff_mat);
- invert_m4_m4(inverse_diff_mat, diff_mat);
+ invert_m4_m4_safe_ortho(inverse_diff_mat, diff_mat);
Material *ma_src = NULL;
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl_new->frames) {
@@ -2910,6 +2913,7 @@ int ED_gpencil_join_objects_exec(bContext *C, wmOperator *op)
DEG_relations_tag_update(bmain); /* because we removed object(s) */
WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
+ WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene);
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c
index ae9146b3a6a..247cc218c2f 100644
--- a/source/blender/editors/gpencil/gpencil_fill.c
+++ b/source/blender/editors/gpencil/gpencil_fill.c
@@ -454,8 +454,9 @@ static bool gpencil_render_offscreen(tGPDfill *tgpf)
GPU_matrix_push();
GPU_matrix_identity_set();
+ GPU_depth_mask(true);
GPU_clear_color(0.0f, 0.0f, 0.0f, 0.0f);
- GPU_clear(GPU_COLOR_BIT | GPU_DEPTH_BIT);
+ GPU_clear_depth(1.0f);
ED_view3d_update_viewmat(
tgpf->depsgraph, tgpf->scene, tgpf->v3d, tgpf->region, NULL, winmat, NULL, true);
@@ -467,6 +468,8 @@ static bool gpencil_render_offscreen(tGPDfill *tgpf)
const float ink[4] = {1.0f, 0.0f, 0.0f, 1.0f};
gpencil_draw_datablock(tgpf, ink);
+ GPU_depth_mask(false);
+
GPU_matrix_pop_projection();
GPU_matrix_pop();
diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h
index ad46dada0c9..dd7ca5c65a4 100644
--- a/source/blender/editors/include/ED_screen.h
+++ b/source/blender/editors/include/ED_screen.h
@@ -81,18 +81,12 @@ void ED_region_tag_refresh_ui(struct ARegion *region);
void ED_region_tag_redraw_editor_overlays(struct ARegion *region);
void ED_region_panels_init(struct wmWindowManager *wm, struct ARegion *region);
-void ED_region_panels_ex(const struct bContext *C,
- struct ARegion *region,
- const char *contexts[],
- int contextnr,
- const bool vertical);
+void ED_region_panels_ex(const struct bContext *C, struct ARegion *region, const char *contexts[]);
void ED_region_panels(const struct bContext *C, struct ARegion *region);
void ED_region_panels_layout_ex(const struct bContext *C,
struct ARegion *region,
struct ListBase *paneltypes,
const char *contexts[],
- int contextnr,
- const bool vertical,
const char *category_override);
void ED_region_panels_layout(const struct bContext *C, struct ARegion *region);
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index 5d936cdfaa3..1fa9ed0b2c0 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -1667,19 +1667,13 @@ void UI_panels_end(const struct bContext *C, struct ARegion *region, int *r_x, i
void UI_panels_draw(const struct bContext *C, struct ARegion *region);
struct Panel *UI_panel_find_by_type(struct ListBase *lb, struct PanelType *pt);
-struct Panel *UI_panel_begin(struct ScrArea *area,
- struct ARegion *region,
+struct Panel *UI_panel_begin(struct ARegion *region,
struct ListBase *lb,
uiBlock *block,
struct PanelType *pt,
struct Panel *panel,
bool *r_open);
-void UI_panel_end(const struct ScrArea *area,
- const struct ARegion *region,
- uiBlock *block,
- int width,
- int height,
- bool open);
+void UI_panel_end(const struct ARegion *region, uiBlock *block, int width, int height, bool open);
void UI_panels_scale(struct ARegion *region, float new_width);
void UI_panel_label_offset(struct uiBlock *block, int *r_x, int *r_y);
@@ -1709,8 +1703,7 @@ struct PointerRNA *UI_region_panel_custom_data_under_cursor(const struct bContex
void UI_panel_custom_data_set(struct Panel *panel, struct PointerRNA *custom_data);
/* Polyinstantiated panels for representing a list of data. */
-struct Panel *UI_panel_add_instanced(struct ScrArea *area,
- struct ARegion *region,
+struct Panel *UI_panel_add_instanced(struct ARegion *region,
struct ListBase *panels,
char *panel_idname,
int list_index,
diff --git a/source/blender/editors/include/UI_resources.h b/source/blender/editors/include/UI_resources.h
index 3f548f98e97..31cb62117c5 100644
--- a/source/blender/editors/include/UI_resources.h
+++ b/source/blender/editors/include/UI_resources.h
@@ -262,7 +262,6 @@ typedef enum ThemeColorID {
TH_PAINT_CURVE_PIVOT,
TH_UV_SHADOW,
- TH_UV_OTHERS,
TH_FREESTYLE_EDGE_MARK,
TH_FREESTYLE_FACE_MARK,
@@ -431,12 +430,9 @@ void UI_GetColorPtrBlendShade3ubv(const unsigned char cp1[3],
// (for anything fancy use UI_GetThemeColor[Fancy] then BLF_color)
void UI_FontThemeColor(int fontid, int colorid);
-// clear the openGL ClearColor using the input colorid
+// clear the framebuffer using the input colorid
void UI_ThemeClearColor(int colorid);
-// clear the openGL ClearColor using the input colorid using optional transparency
-void UI_ThemeClearColorAlpha(int colorid, float alpha);
-
// internal (blender) usage only, for init and set active
void UI_SetTheme(int spacetype, int regionid);
diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c
index e04531bb1dd..9cede126890 100644
--- a/source/blender/editors/interface/interface.c
+++ b/source/blender/editors/interface/interface.c
@@ -304,11 +304,11 @@ static void ui_update_flexible_spacing(const ARegion *region, uiBlock *block)
/* How much the next block overlap with the current segment */
int overlap = ((i == sepr_flex_len - 1) ? buttons_width - spacers_pos[i] :
(spacers_pos[i + 1] - spacers_pos[i]) / 2);
- int segment_end = segment_width * (i + 1);
- int spacer_end = segment_end - overlap;
- int spacer_sta = spacers_pos[i] + offset;
+ const int segment_end = segment_width * (i + 1);
+ const int spacer_end = segment_end - overlap;
+ const int spacer_sta = spacers_pos[i] + offset;
if (spacer_end > spacer_sta) {
- float step = min_ff(remaining_space, spacer_end - spacer_sta);
+ const float step = min_ff(remaining_space, spacer_end - spacer_sta);
remaining_space -= step;
offset += step;
}
@@ -329,9 +329,9 @@ static void ui_update_window_matrix(const wmWindow *window, const ARegion *regio
else {
/* No subwindow created yet, for menus for example, so we use the main
* window instead, since buttons are created there anyway. */
- int width = WM_window_pixels_x(window);
- int height = WM_window_pixels_y(window);
- rcti winrct = {0, width - 1, 0, height - 1};
+ const int width = WM_window_pixels_x(window);
+ const int height = WM_window_pixels_y(window);
+ const rcti winrct = {0, width - 1, 0, height - 1};
wmGetProjectionMatrix(block->winmat, &winrct);
block->aspect = 2.0f / fabsf(width * block->winmat[0][0]);
@@ -1130,7 +1130,7 @@ static bool ui_but_event_operator_string_from_menu(const bContext *C,
IDProperty *prop_menu;
/* annoying, create a property */
- IDPropertyTemplate val = {0};
+ const IDPropertyTemplate val = {0};
prop_menu = IDP_New(IDP_GROUP, &val, __func__); /* dummy, name is unimportant */
IDP_AddToGroup(prop_menu, IDP_NewString(mt->idname, "name", sizeof(mt->idname)));
@@ -1156,7 +1156,7 @@ static bool ui_but_event_operator_string_from_panel(const bContext *C,
IDProperty *prop_panel;
/* annoying, create a property */
- IDPropertyTemplate val = {0};
+ const IDPropertyTemplate val = {0};
prop_panel = IDP_New(IDP_GROUP, &val, __func__); /* dummy, name is unimportant */
IDP_AddToGroup(prop_panel, IDP_NewString(pt->idname, "name", sizeof(pt->idname)));
IDP_AddToGroup(prop_panel,
@@ -1361,7 +1361,7 @@ static bool ui_but_event_property_operator_string(const bContext *C,
/* create a property to host the "datapath" property we're sending to the operators */
IDProperty *prop_path;
- IDPropertyTemplate val = {0};
+ const IDPropertyTemplate val = {0};
prop_path = IDP_New(IDP_GROUP, &val, __func__);
if (data_path) {
IDP_AddToGroup(prop_path, IDP_NewString(data_path, "data_path", strlen(data_path) + 1));
@@ -1370,11 +1370,11 @@ static bool ui_but_event_property_operator_string(const bContext *C,
const EnumPropertyItem *item;
bool free;
RNA_property_enum_items((bContext *)C, ptr, prop, &item, NULL, &free);
- int index = RNA_enum_from_value(item, prop_enum_value);
+ const int index = RNA_enum_from_value(item, prop_enum_value);
if (index != -1) {
IDProperty *prop_value;
if (prop_enum_value_is_int) {
- int value = item[index].value;
+ const int value = item[index].value;
prop_value = IDP_New(IDP_INT,
&(IDPropertyTemplate){
.i = value,
@@ -1656,7 +1656,7 @@ static PredefinedExtraOpIconType ui_but_icon_extra_get(uiBut *but)
*/
static void ui_but_predefined_extra_operator_icons_add(uiBut *but)
{
- PredefinedExtraOpIconType extra_icon = ui_but_icon_extra_get(but);
+ const PredefinedExtraOpIconType extra_icon = ui_but_icon_extra_get(but);
wmOperatorType *optype = NULL;
BIFIconID icon = ICON_NONE;
@@ -2047,7 +2047,7 @@ int ui_but_is_pushed_ex(uiBut *but, double *value)
/* uiBut.custom_data points to data this tab represents (e.g. workspace).
* uiBut.rnapoin/prop store an active value (e.g. active workspace). */
if (RNA_property_type(but->rnaprop) == PROP_POINTER) {
- PointerRNA active_ptr = RNA_property_pointer_get(&but->rnapoin, but->rnaprop);
+ const PointerRNA active_ptr = RNA_property_pointer_get(&but->rnapoin, but->rnaprop);
if (active_ptr.data == but->custom_data) {
is_push = true;
}
@@ -2518,7 +2518,7 @@ uiBut *ui_but_drag_multi_edit_get(uiBut *but)
static double ui_get_but_scale_unit(uiBut *but, double value)
{
UnitSettings *unit = but->block->unit;
- int unit_type = UI_but_unit_type_get(but);
+ const int unit_type = UI_but_unit_type_get(but);
/* Time unit is a bit special, not handled by BKE_scene_unit_scale() for now. */
if (unit_type == PROP_UNIT_TIME) { /* WARNING - using evil_C :| */
@@ -2533,7 +2533,7 @@ void ui_but_convert_to_unit_alt_name(uiBut *but, char *str, size_t maxlen)
{
if (ui_but_is_unit(but)) {
UnitSettings *unit = but->block->unit;
- int unit_type = UI_but_unit_type_get(but);
+ const int unit_type = UI_but_unit_type_get(but);
char *orig_str;
orig_str = BLI_strdup(str);
@@ -2551,7 +2551,7 @@ static void ui_get_but_string_unit(
uiBut *but, char *str, int len_max, double value, bool pad, int float_precision)
{
UnitSettings *unit = but->block->unit;
- int unit_type = UI_but_unit_type_get(but);
+ const int unit_type = UI_but_unit_type_get(but);
int precision;
if (unit->scale_length < 0.0001f) {
@@ -2584,7 +2584,7 @@ static void ui_get_but_string_unit(
static float ui_get_but_step_unit(uiBut *but, float step_default)
{
- int unit_type = RNA_SUBTYPE_UNIT_VALUE(UI_but_unit_type_get(but));
+ const int unit_type = RNA_SUBTYPE_UNIT_VALUE(UI_but_unit_type_get(but));
const double step_orig = step_default * UI_PRECISION_FLOAT_SCALE;
/* Scaling up 'step_origg ' here is a bit arbitrary,
* its just giving better scales from user POV */
@@ -2655,7 +2655,7 @@ void ui_but_string_get_ex(uiBut *but,
}
else if (type == PROP_ENUM) {
/* RNA enum */
- int value = RNA_property_enum_get(&but->rnapoin, but->rnaprop);
+ const int value = RNA_property_enum_get(&but->rnapoin, but->rnaprop);
if (RNA_property_enum_name(but->block->evil_C, &but->rnapoin, but->rnaprop, value, &buf)) {
BLI_strncpy(str, buf, maxlen);
buf = str;
@@ -2774,7 +2774,7 @@ char *ui_but_string_get_dynamic(uiBut *but, int *r_str_size)
}
else if (type == PROP_ENUM) {
/* RNA enum */
- int value = RNA_property_enum_get(&but->rnapoin, but->rnaprop);
+ const int value = RNA_property_enum_get(&but->rnapoin, but->rnaprop);
const char *value_id;
if (!RNA_property_enum_name(
but->block->evil_C, &but->rnapoin, but->rnaprop, value, &value_id)) {
@@ -2845,10 +2845,10 @@ static bool ui_number_from_string(bContext *C, const char *str, double *r_value)
static bool ui_number_from_string_factor(bContext *C, const char *str, double *r_value)
{
- int len = strlen(str);
+ const int len = strlen(str);
if (BLI_strn_endswith(str, "%", len)) {
char *str_new = BLI_strdupn(str, len - 1);
- bool success = ui_number_from_string(C, str_new, r_value);
+ const bool success = ui_number_from_string(C, str_new, r_value);
MEM_freeN(str_new);
*r_value /= 100.0;
return success;
@@ -2864,10 +2864,10 @@ static bool ui_number_from_string_factor(bContext *C, const char *str, double *r
static bool ui_number_from_string_percentage(bContext *C, const char *str, double *r_value)
{
- int len = strlen(str);
+ const int len = strlen(str);
if (BLI_strn_endswith(str, "%", len)) {
char *str_new = BLI_strdupn(str, len - 1);
- bool success = ui_number_from_string(C, str_new, r_value);
+ const bool success = ui_number_from_string(C, str_new, r_value);
MEM_freeN(str_new);
return success;
}
@@ -3078,7 +3078,7 @@ static double soft_range_round_up(double value, double max)
{
/* round up to .., 0.1, 0.2, 0.5, 1, 2, 5, 10, 20, 50, ..
* checking for 0.0 prevents floating point exceptions */
- double newmax = (value != 0.0) ? pow(10.0, ceil(log(value) / M_LN10)) : 0.0;
+ const double newmax = (value != 0.0) ? pow(10.0, ceil(log(value) / M_LN10)) : 0.0;
if (newmax * 0.2 >= max && newmax * 0.2 >= value) {
return newmax * 0.2;
@@ -3093,7 +3093,7 @@ static double soft_range_round_down(double value, double max)
{
/* round down to .., 0.1, 0.2, 0.5, 1, 2, 5, 10, 20, 50, ..
* checking for 0.0 prevents floating point exceptions */
- double newmax = (value != 0.0) ? pow(10.0, floor(log(value) / M_LN10)) : 0.0;
+ const double newmax = (value != 0.0) ? pow(10.0, floor(log(value) / M_LN10)) : 0.0;
if (newmax * 5.0 <= max && newmax * 5.0 <= value) {
return newmax * 5.0;
@@ -3481,6 +3481,9 @@ static void ui_but_build_drawstr_float(uiBut *but, double value)
subtype = RNA_property_subtype(but->rnaprop);
}
+ /* Change negative zero to regular zero, without altering anything else. */
+ value += +0.0f;
+
if (value == (double)FLT_MAX) {
STR_CONCAT(but->drawstr, slen, "inf");
}
@@ -3488,15 +3491,15 @@ static void ui_but_build_drawstr_float(uiBut *but, double value)
STR_CONCAT(but->drawstr, slen, "-inf");
}
else if (subtype == PROP_PERCENTAGE) {
- int prec = ui_but_calc_float_precision(but, value);
+ const int prec = ui_but_calc_float_precision(but, value);
STR_CONCATF(but->drawstr, slen, "%.*f%%", prec, value);
}
else if (subtype == PROP_PIXEL) {
- int prec = ui_but_calc_float_precision(but, value);
+ const int prec = ui_but_calc_float_precision(but, value);
STR_CONCATF(but->drawstr, slen, "%.*f px", prec, value);
}
else if (subtype == PROP_FACTOR) {
- int precision = ui_but_calc_float_precision(but, value);
+ const int precision = ui_but_calc_float_precision(but, value);
if (U.factor_display_type == USER_FACTOR_AS_FACTOR) {
STR_CONCATF(but->drawstr, slen, "%.*f", precision, value);
@@ -3511,7 +3514,7 @@ static void ui_but_build_drawstr_float(uiBut *but, double value)
STR_CONCAT(but->drawstr, slen, new_str);
}
else {
- int prec = ui_but_calc_float_precision(but, value);
+ const int prec = ui_but_calc_float_precision(but, value);
STR_CONCATF(but->drawstr, slen, "%.*f", prec, value);
}
}
@@ -3601,12 +3604,12 @@ static void ui_but_update_ex(uiBut *but, const bool validate)
/* only needed for menus in popup blocks that don't recreate buttons on redraw */
if (but->block->flag & UI_BLOCK_LOOP) {
if (but->rnaprop && (RNA_property_type(but->rnaprop) == PROP_ENUM)) {
- int value_enum = RNA_property_enum_get(&but->rnapoin, but->rnaprop);
+ const int value_enum = RNA_property_enum_get(&but->rnapoin, but->rnaprop);
EnumPropertyItem item;
if (RNA_property_enum_item_from_value_gettexted(
but->block->evil_C, &but->rnapoin, but->rnaprop, value_enum, &item)) {
- size_t slen = strlen(item.name);
+ const size_t slen = strlen(item.name);
ui_but_string_free_internal(but);
ui_but_string_set_internal(but, item.name, slen);
but->icon = item.icon;
@@ -4797,7 +4800,7 @@ static uiBut *uiDefButBit(uiBlock *block,
float a2,
const char *tip)
{
- int bitIdx = findBitIndex(bit);
+ const int bitIdx = findBitIndex(bit);
if (bitIdx == -1) {
return NULL;
}
@@ -5182,7 +5185,7 @@ static uiBut *uiDefIconButBit(uiBlock *block,
float a2,
const char *tip)
{
- int bitIdx = findBitIndex(bit);
+ const int bitIdx = findBitIndex(bit);
if (bitIdx == -1) {
return NULL;
}
@@ -5568,7 +5571,7 @@ static uiBut *uiDefIconTextButBit(uiBlock *block,
float a2,
const char *tip)
{
- int bitIdx = findBitIndex(bit);
+ const int bitIdx = findBitIndex(bit);
if (bitIdx == -1) {
return NULL;
}
@@ -6127,7 +6130,7 @@ void UI_but_unit_type_set(uiBut *but, const int unit_type)
int UI_but_unit_type_get(const uiBut *but)
{
- int ownUnit = (int)but->unit_type;
+ const int ownUnit = (int)but->unit_type;
/* own unit define always takes precedence over RNA provided, allowing for overriding
* default value provided in RNA in a few special cases (i.e. Active Keyframe in Graph Edit)
diff --git a/source/blender/editors/interface/interface_anim.c b/source/blender/editors/interface/interface_anim.c
index 56df49981e0..6fb9b99632e 100644
--- a/source/blender/editors/interface/interface_anim.c
+++ b/source/blender/editors/interface/interface_anim.c
@@ -60,7 +60,7 @@ static FCurve *ui_but_get_fcurve(
{
/* for entire array buttons we check the first component, it's not perfect
* but works well enough in typical cases */
- int rnaindex = (but->rnaindex == -1) ? 0 : but->rnaindex;
+ const int rnaindex = (but->rnaindex == -1) ? 0 : but->rnaindex;
return BKE_fcurve_find_by_rna_context_ui(
but->block->evil_C, &but->rnapoin, but->rnaprop, rnaindex, adt, action, r_driven, r_special);
@@ -158,7 +158,7 @@ void ui_but_anim_decorate_update_from_flag(uiButDecorator *decorator_but)
return;
}
- int flag = but_anim->flag;
+ const int flag = but_anim->flag;
if (flag & UI_BUT_DRIVEN) {
but->icon = ICON_DECORATE_DRIVER;
diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c
index 59178e209db..9889c81ca55 100644
--- a/source/blender/editors/interface/interface_context_menu.c
+++ b/source/blender/editors/interface/interface_context_menu.c
@@ -91,7 +91,7 @@ static IDProperty *shortcut_property_from_rna(bContext *C, uiBut *but)
/* Create ID property of data path, to pass to the operator. */
IDProperty *prop;
- IDPropertyTemplate val = {0};
+ const IDPropertyTemplate val = {0};
prop = IDP_New(IDP_GROUP, &val, __func__);
IDP_AddToGroup(prop, IDP_NewString(final_data_path, "data_path", strlen(final_data_path) + 1));
@@ -517,7 +517,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but)
uiLayout *layout;
{
- uiStringInfo label = {BUT_GET_LABEL, NULL};
+ const uiStringInfo label = {BUT_GET_LABEL, NULL};
/* highly unlikely getting the label ever fails */
UI_but_string_info_get(C, but, &label, NULL);
@@ -548,9 +548,9 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but)
const PropertyType type = RNA_property_type(prop);
const PropertySubType subtype = RNA_property_subtype(prop);
bool is_anim = RNA_property_animateable(ptr, prop);
- bool is_editable = RNA_property_editable(ptr, prop);
- bool is_idprop = RNA_property_is_idprop(prop);
- bool is_set = RNA_property_is_set(ptr, prop);
+ const bool is_editable = RNA_property_editable(ptr, prop);
+ const bool is_idprop = RNA_property_is_idprop(prop);
+ const bool is_set = RNA_property_is_set(ptr, prop);
/* second slower test,
* saved people finding keyframe items in menus when its not possible */
@@ -1044,7 +1044,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but)
if (idname != NULL) {
uiBlock *block = uiLayoutGetBlock(layout);
uiBut *but2;
- int w = uiLayoutGetWidth(layout);
+ const int w = uiLayoutGetWidth(layout);
wmKeyMap *km;
/* We want to know if this op has a shortcut, be it hotkey or not. */
diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c
index ba878be3dc7..25c5e63d23e 100644
--- a/source/blender/editors/interface/interface_draw.c
+++ b/source/blender/editors/interface/interface_draw.c
@@ -622,10 +622,10 @@ void UI_draw_roundbox_shade_y(bool filled,
void UI_draw_text_underline(int pos_x, int pos_y, int len, int height, const float color[4])
{
- int ofs_y = 4 * U.pixelsize;
+ const int ofs_y = 4 * U.pixelsize;
GPUVertFormat *format = immVertexFormat();
- uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
+ const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
immUniformColor4fv(color);
@@ -644,8 +644,9 @@ void ui_draw_but_TAB_outline(const rcti *rect,
uchar highlight_fade[3])
{
GPUVertFormat *format = immVertexFormat();
- uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_U8, 3, GPU_FETCH_INT_TO_FLOAT_UNIT);
+ const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ const uint col = GPU_vertformat_attr_add(
+ format, "color", GPU_COMP_U8, 3, GPU_FETCH_INT_TO_FLOAT_UNIT);
/* add a 1px offset, looks nicer */
const int minx = rect->xmin + U.pixelsize, maxx = rect->xmax - U.pixelsize;
const int miny = rect->ymin + U.pixelsize, maxy = rect->ymax - U.pixelsize;
@@ -741,8 +742,8 @@ void ui_draw_but_IMAGE(ARegion *UNUSED(region),
return;
}
- int w = BLI_rcti_size_x(rect);
- int h = BLI_rcti_size_y(rect);
+ const int w = BLI_rcti_size_x(rect);
+ const int h = BLI_rcti_size_y(rect);
/* scissor doesn't seem to be doing the right thing...? */
# if 0
@@ -813,13 +814,13 @@ void UI_draw_safe_areas(uint pos,
for (int i = 0; i < safe_len; i++) {
if (safe_areas[i][0] || safe_areas[i][1]) {
- float margin_x = safe_areas[i][0] * size_x_half;
- float margin_y = safe_areas[i][1] * size_y_half;
+ const float margin_x = safe_areas[i][0] * size_x_half;
+ const float margin_y = safe_areas[i][1] * size_y_half;
- float minx = x1 + margin_x;
- float miny = y1 + margin_y;
- float maxx = x2 - margin_x;
- float maxy = y2 - margin_y;
+ const float minx = x1 + margin_x;
+ const float miny = y1 + margin_y;
+ const float maxx = x2 - margin_x;
+ const float maxy = y2 - margin_y;
imm_draw_box_wire_2d(pos, minx, miny, maxx, maxy);
}
@@ -868,7 +869,7 @@ static void histogram_draw_one(float r,
immBegin(GPU_PRIM_LINE_STRIP, res);
for (int i = 0; i < res; i++) {
- float x2 = x + i * (w / (float)res);
+ const float x2 = x + i * (w / (float)res);
immVertex2f(pos_attr, x2, y + (data[i] * h));
}
immEnd();
@@ -879,7 +880,7 @@ static void histogram_draw_one(float r,
immVertex2f(pos_attr, x, y);
immVertex2f(pos_attr, x, y + (data[0] * h));
for (int i = 1; i < res; i++) {
- float x2 = x + i * (w / (float)res);
+ const float x2 = x + i * (w / (float)res);
immVertex2f(pos_attr, x2, y + (data[i] * h));
immVertex2f(pos_attr, x2, y);
}
@@ -891,7 +892,7 @@ static void histogram_draw_one(float r,
GPU_blend(GPU_BLEND_ALPHA);
immBegin(GPU_PRIM_LINE_STRIP, res);
for (int i = 0; i < res; i++) {
- float x2 = x + i * (w / (float)res);
+ const float x2 = x + i * (w / (float)res);
immVertex2f(pos_attr, x2, y + (data[i] * h));
}
immEnd();
@@ -908,7 +909,7 @@ void ui_draw_but_HISTOGRAM(ARegion *UNUSED(region),
const rcti *recti)
{
Histogram *hist = (Histogram *)but->poin;
- int res = hist->x_resolution;
+ const int res = hist->x_resolution;
const bool is_line = (hist->flag & HISTO_FLAG_LINE) != 0;
rctf rect = {
@@ -918,8 +919,8 @@ void ui_draw_but_HISTOGRAM(ARegion *UNUSED(region),
.ymax = (float)recti->ymax - 1,
};
- float w = BLI_rctf_size_x(&rect);
- float h = BLI_rctf_size_y(&rect) * hist->ymax;
+ const float w = BLI_rctf_size_x(&rect);
+ const float h = BLI_rctf_size_y(&rect) * hist->ymax;
GPU_blend(GPU_BLEND_ALPHA);
@@ -938,7 +939,7 @@ void ui_draw_but_HISTOGRAM(ARegion *UNUSED(region),
(rect.ymax + 1) - (rect.ymin - 1));
GPUVertFormat *format = immVertexFormat();
- uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
@@ -1000,7 +1001,7 @@ void ui_draw_but_HISTOGRAM(ARegion *UNUSED(region),
static void waveform_draw_one(float *waveform, int nbr, const float col[3])
{
GPUVertFormat format = {0};
- uint pos_id = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ const uint pos_id = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
GPU_vertbuf_data_alloc(vbo, nbr);
@@ -1043,13 +1044,13 @@ void ui_draw_but_WAVEFORM(ARegion *UNUSED(region),
if (scopes->wavefrm_yfac < 0.5f) {
scopes->wavefrm_yfac = 0.98f;
}
- float w = BLI_rctf_size_x(&rect) - 7;
- float h = BLI_rctf_size_y(&rect) * scopes->wavefrm_yfac;
- float yofs = rect.ymin + (BLI_rctf_size_y(&rect) - h) * 0.5f;
- float w3 = w / 3.0f;
+ const float w = BLI_rctf_size_x(&rect) - 7;
+ const float h = BLI_rctf_size_y(&rect) * scopes->wavefrm_yfac;
+ const float yofs = rect.ymin + (BLI_rctf_size_y(&rect) - h) * 0.5f;
+ const float w3 = w / 3.0f;
/* log scale for alpha */
- float alpha = scopes->wavefrm_alpha * scopes->wavefrm_alpha;
+ const float alpha = scopes->wavefrm_alpha * scopes->wavefrm_alpha;
unit_m3(colors);
@@ -1093,7 +1094,7 @@ void ui_draw_but_WAVEFORM(ARegion *UNUSED(region),
GPU_blend(GPU_BLEND_ALPHA);
GPUVertFormat *format = immVertexFormat();
- uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
@@ -1200,7 +1201,7 @@ void ui_draw_but_WAVEFORM(ARegion *UNUSED(region),
SCOPES_WAVEFRM_YCC_601,
SCOPES_WAVEFRM_YCC_709,
SCOPES_WAVEFRM_YCC_JPEG)) {
- int rgb = (scopes->wavefrm_mode == SCOPES_WAVEFRM_RGB_PARADE);
+ const int rgb = (scopes->wavefrm_mode == SCOPES_WAVEFRM_RGB_PARADE);
GPU_matrix_push();
GPU_matrix_translate_2f(rect.xmin, yofs);
@@ -1384,13 +1385,13 @@ void ui_draw_but_VECTORSCOPE(ARegion *UNUSED(region),
.ymax = (float)recti->ymax - 1,
};
- float w = BLI_rctf_size_x(&rect);
- float h = BLI_rctf_size_y(&rect);
- float centerx = rect.xmin + w * 0.5f;
- float centery = rect.ymin + h * 0.5f;
- float diam = (w < h) ? w : h;
+ const float w = BLI_rctf_size_x(&rect);
+ const float h = BLI_rctf_size_y(&rect);
+ const float centerx = rect.xmin + w * 0.5f;
+ const float centery = rect.ymin + h * 0.5f;
+ const float diam = (w < h) ? w : h;
- float alpha = scopes->vecscope_alpha * scopes->vecscope_alpha * scopes->vecscope_alpha;
+ const float alpha = scopes->vecscope_alpha * scopes->vecscope_alpha * scopes->vecscope_alpha;
GPU_blend(GPU_BLEND_ALPHA);
@@ -1409,7 +1410,7 @@ void ui_draw_but_VECTORSCOPE(ARegion *UNUSED(region),
(rect.ymax + 1) - (rect.ymin - 1));
GPUVertFormat *format = immVertexFormat();
- uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
@@ -1538,11 +1539,11 @@ static void ui_draw_colorband_handle(uint shdr_pos,
const float min_width = 3.0f;
float colf[3] = {UNPACK3(rgb)};
- float half_width = floorf(sizey / 3.5f);
- float height = half_width * 1.4f;
+ const float half_width = floorf(sizey / 3.5f);
+ const float height = half_width * 1.4f;
float y1 = rect->ymin + (sizey * 0.16f);
- float y2 = rect->ymax;
+ const float y2 = rect->ymax;
/* align to pixels */
x = floorf(x + 0.5f);
@@ -1637,11 +1638,11 @@ void ui_draw_but_COLORBAND(uiBut *but, const uiWidgetColors *UNUSED(wcol), const
return;
}
- float x1 = rect->xmin;
- float sizex = rect->xmax - x1;
- float sizey = BLI_rcti_size_y(rect);
- float sizey_solid = sizey * 0.25f;
- float y1 = rect->ymin;
+ const float x1 = rect->xmin;
+ const float sizex = rect->xmax - x1;
+ const float sizey = BLI_rcti_size_y(rect);
+ const float sizey_solid = sizey * 0.25f;
+ const float y1 = rect->ymin;
/* exit early if too narrow */
if (sizex <= 0) {
@@ -1680,7 +1681,7 @@ void ui_draw_but_COLORBAND(uiBut *but, const uiWidgetColors *UNUSED(wcol), const
immBegin(GPU_PRIM_TRI_STRIP, (sizex + 1) * 2);
for (int a = 0; a <= sizex; a++) {
- float pos = ((float)a) / sizex;
+ const float pos = ((float)a) / sizex;
BKE_colorband_evaluate(coba, pos, colf);
if (display) {
IMB_colormanagement_scene_linear_to_display_v3(colf, display);
@@ -1700,7 +1701,7 @@ void ui_draw_but_COLORBAND(uiBut *but, const uiWidgetColors *UNUSED(wcol), const
immBegin(GPU_PRIM_TRI_STRIP, (sizex + 1) * 2);
for (int a = 0; a <= sizex; a++) {
- float pos = ((float)a) / sizex;
+ const float pos = ((float)a) / sizex;
BKE_colorband_evaluate(coba, pos, colf);
if (display) {
IMB_colormanagement_scene_linear_to_display_v3(colf, display);
@@ -1748,7 +1749,7 @@ void ui_draw_but_COLORBAND(uiBut *but, const uiWidgetColors *UNUSED(wcol), const
/* layer: draw handles */
for (int a = 0; a < coba->tot; a++, cbd++) {
if (a != coba->cur) {
- float pos = x1 + cbd->pos * (sizex - 1) + 1;
+ const float pos = x1 + cbd->pos * (sizex - 1) + 1;
ui_draw_colorband_handle(pos_id, rect, pos, &cbd->r, display, false);
}
}
@@ -1756,7 +1757,7 @@ void ui_draw_but_COLORBAND(uiBut *but, const uiWidgetColors *UNUSED(wcol), const
/* layer: active handle */
if (coba->tot != 0) {
cbd = &coba->data[coba->cur];
- float pos = x1 + cbd->pos * (sizex - 1) + 1;
+ const float pos = x1 + cbd->pos * (sizex - 1) + 1;
ui_draw_colorband_handle(pos_id, rect, pos, &cbd->r, display, true);
}
@@ -1783,7 +1784,7 @@ void ui_draw_but_UNITVEC(uiBut *but, const uiWidgetColors *wcol, const rcti *rec
/* transform to button */
GPU_matrix_push();
- bool use_project_matrix = (size >= -GPU_MATRIX_ORTHO_CLIP_NEAR_DEFAULT);
+ const bool use_project_matrix = (size >= -GPU_MATRIX_ORTHO_CLIP_NEAR_DEFAULT);
if (use_project_matrix) {
GPU_matrix_push_projection();
GPU_matrix_ortho_set_z(-size, size);
@@ -1804,7 +1805,7 @@ void ui_draw_but_UNITVEC(uiBut *but, const uiWidgetColors *wcol, const rcti *rec
/* AA circle */
GPUVertFormat *format = immVertexFormat();
- uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
immUniformColor3ubv(wcol->inner);
@@ -1827,13 +1828,13 @@ void ui_draw_but_UNITVEC(uiBut *but, const uiWidgetColors *wcol, const rcti *rec
static void ui_draw_but_curve_grid(
uint pos, const rcti *rect, float zoomx, float zoomy, float offsx, float offsy, float step)
{
- float dx = step * zoomx;
+ const float dx = step * zoomx;
float fx = rect->xmin + zoomx * (-offsx);
if (fx > rect->xmin) {
fx -= dx * (floorf(fx - rect->xmin));
}
- float dy = step * zoomy;
+ const float dy = step * zoomy;
float fy = rect->ymin + zoomy * (-offsy);
if (fy > rect->ymin) {
fy -= dy * (floorf(fy - rect->ymin));
@@ -1885,8 +1886,8 @@ void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol,
CurveMapping *cumap = (but_cumap->edit_cumap == NULL) ? (CurveMapping *)but->poin :
but_cumap->edit_cumap;
- float clip_size_x = BLI_rctf_size_x(&cumap->curr);
- float clip_size_y = BLI_rctf_size_y(&cumap->curr);
+ const float clip_size_x = BLI_rctf_size_x(&cumap->curr);
+ const float clip_size_y = BLI_rctf_size_y(&cumap->curr);
/* zero-sized curve */
if (clip_size_x == 0.0f || clip_size_y == 0.0f) {
@@ -1894,10 +1895,10 @@ void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol,
}
/* calculate offset and zoom */
- float zoomx = (BLI_rcti_size_x(rect) - 2.0f) / clip_size_x;
- float zoomy = (BLI_rcti_size_y(rect) - 2.0f) / clip_size_y;
- float offsx = cumap->curr.xmin - (1.0f / zoomx);
- float offsy = cumap->curr.ymin - (1.0f / zoomy);
+ const float zoomx = (BLI_rcti_size_x(rect) - 2.0f) / clip_size_x;
+ const float zoomy = (BLI_rcti_size_y(rect) - 2.0f) / clip_size_y;
+ const float offsx = cumap->curr.xmin - (1.0f / zoomx);
+ const float offsy = cumap->curr.ymin - (1.0f / zoomy);
/* exit early if too narrow */
if (zoomx == 0.0f) {
@@ -1915,7 +1916,7 @@ void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol,
.xmax = rect->xmax,
.ymax = rect->ymax,
};
- rcti scissor_region = {0, region->winx, 0, region->winy};
+ const rcti scissor_region = {0, region->winx, 0, region->winy};
BLI_rcti_isect(&scissor_new, &scissor_region, &scissor_new);
GPU_scissor(scissor_new.xmin,
scissor_new.ymin,
@@ -2014,7 +2015,7 @@ void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol,
immVertex2f(pos, rect->xmin + zoomx * (hsv[0] - offsx), rect->ymax);
}
else if (cumap->cur == 3) {
- float lum = IMB_colormanagement_get_luminance(cumap->sample);
+ const float lum = IMB_colormanagement_get_luminance(cumap->sample);
immUniformColor3ub(240, 240, 240);
immVertex2f(pos, rect->xmin + zoomx * (lum - offsx), rect->ymin);
@@ -2073,8 +2074,8 @@ void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol,
immVertex2f(pos, line_range.xmin, rect->ymin);
immVertex2f(pos, line_range.xmin, line_range.ymin);
for (int a = 0; a <= CM_TABLE; a++) {
- float fx = rect->xmin + zoomx * (cmp[a].x - offsx);
- float fy = rect->ymin + zoomy * (cmp[a].y - offsy);
+ const float fx = rect->xmin + zoomx * (cmp[a].x - offsx);
+ const float fy = rect->ymin + zoomy * (cmp[a].y - offsy);
immVertex2f(pos, fx, rect->ymin);
immVertex2f(pos, fx, fy);
}
@@ -2089,8 +2090,8 @@ void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol,
immBegin(GPU_PRIM_LINE_STRIP, (CM_TABLE + 1) + 2);
immVertex2f(pos, line_range.xmin, line_range.ymin);
for (int a = 0; a <= CM_TABLE; a++) {
- float fx = rect->xmin + zoomx * (cmp[a].x - offsx);
- float fy = rect->ymin + zoomy * (cmp[a].y - offsy);
+ const float fx = rect->xmin + zoomx * (cmp[a].x - offsx);
+ const float fy = rect->ymin + zoomy * (cmp[a].y - offsy);
immVertex2f(pos, fx, fy);
}
immVertex2f(pos, line_range.xmax, line_range.ymax);
@@ -2104,7 +2105,7 @@ void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol,
/* The points, use aspect to make them visible on edges. */
format = immVertexFormat();
pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+ const uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
/* Calculate vertex colors based on text theme. */
@@ -2123,8 +2124,8 @@ void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol,
GPU_point_size(max_ff(1.0f, min_ff(UI_DPI_FAC / but->block->aspect * 4.0f, 4.0f)));
immBegin(GPU_PRIM_POINTS, cuma->totpoint);
for (int a = 0; a < cuma->totpoint; a++) {
- float fx = rect->xmin + zoomx * (cmp[a].x - offsx);
- float fy = rect->ymin + zoomy * (cmp[a].y - offsy);
+ const float fx = rect->xmin + zoomx * (cmp[a].x - offsx);
+ const float fy = rect->ymin + zoomy * (cmp[a].y - offsy);
immAttr4fv(col, (cmp[a].flag & CUMA_SELECT) ? color_vert_select : color_vert);
immVertex2f(pos, fx, fy);
}
@@ -2171,10 +2172,10 @@ void ui_draw_but_CURVEPROFILE(ARegion *region,
but_profile->edit_profile;
/* Calculate offset and zoom. */
- float zoomx = (BLI_rcti_size_x(rect) - 2.0f) / BLI_rctf_size_x(&profile->view_rect);
- float zoomy = (BLI_rcti_size_y(rect) - 2.0f) / BLI_rctf_size_y(&profile->view_rect);
- float offsx = profile->view_rect.xmin - (1.0f / zoomx);
- float offsy = profile->view_rect.ymin - (1.0f / zoomy);
+ const float zoomx = (BLI_rcti_size_x(rect) - 2.0f) / BLI_rctf_size_x(&profile->view_rect);
+ const float zoomy = (BLI_rcti_size_y(rect) - 2.0f) / BLI_rctf_size_y(&profile->view_rect);
+ const float offsx = profile->view_rect.xmin - (1.0f / zoomx);
+ const float offsy = profile->view_rect.ymin - (1.0f / zoomy);
/* Exit early if too narrow. */
if (zoomx == 0.0f) {
@@ -2190,7 +2191,7 @@ void ui_draw_but_CURVEPROFILE(ARegion *region,
.xmax = rect->xmax,
.ymax = rect->ymax,
};
- rcti scissor_region = {0, region->winx, 0, region->winy};
+ const rcti scissor_region = {0, region->winx, 0, region->winy};
BLI_rcti_isect(&scissor_new, &scissor_region, &scissor_new);
GPU_scissor(scissor_new.xmin,
scissor_new.ymin,
@@ -2235,10 +2236,10 @@ void ui_draw_but_CURVEPROFILE(ARegion *region,
}
CurveProfilePoint *pts = profile->table;
/* Also add the last points on the right and bottom edges to close off the fill polygon. */
- bool add_left_tri = profile->view_rect.xmin < 0.0f;
- bool add_bottom_tri = profile->view_rect.ymin < 0.0f;
+ const bool add_left_tri = profile->view_rect.xmin < 0.0f;
+ const bool add_bottom_tri = profile->view_rect.ymin < 0.0f;
uint tot_points = (uint)PROF_TABLE_LEN(profile->path_len) + 1 + add_left_tri + add_bottom_tri;
- uint tot_triangles = tot_points - 2;
+ const uint tot_triangles = tot_points - 2;
/* Create array of the positions of the table's points. */
float(*table_coords)[2] = MEM_mallocN(sizeof(*table_coords) * tot_points, "table x coords");
@@ -2349,7 +2350,7 @@ void ui_draw_but_CURVEPROFILE(ARegion *region,
/* New GPU instructions for control points and sampled points. */
format = immVertexFormat();
pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+ const uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
/* Calculate vertex colors based on text theme. */
@@ -2448,8 +2449,8 @@ void ui_draw_but_TRACKPREVIEW(ARegion *UNUSED(region),
.ymax = (float)recti->ymax - 1,
};
- int width = BLI_rctf_size_x(&rect) + 1;
- int height = BLI_rctf_size_y(&rect);
+ const int width = BLI_rctf_size_x(&rect) + 1;
+ const int height = BLI_rctf_size_y(&rect);
GPU_blend(GPU_BLEND_ALPHA);
@@ -2535,8 +2536,8 @@ void ui_draw_but_TRACKPREVIEW(ARegion *UNUSED(region),
GPU_scissor(rect.xmin, rect.ymin, BLI_rctf_size_x(&rect), BLI_rctf_size_y(&rect));
GPUVertFormat *format = immVertexFormat();
- uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+ const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ const uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
UI_GetThemeColor4fv(TH_SEL_MARKER, col_sel);
@@ -2547,10 +2548,10 @@ void ui_draw_but_TRACKPREVIEW(ARegion *UNUSED(region),
const float pos_sel[8] = {-10.0f, -7.0f, -4.0f, -1.0f, 2.0f, 5.0f, 8.0f, 11.0f};
for (int axe = 0; axe < 2; axe++) {
for (int i = 0; i < 7; i++) {
- float x1 = pos_sel[i] * (1 - axe);
- float y1 = pos_sel[i] * axe;
- float x2 = pos_sel[i + 1] * (1 - axe);
- float y2 = pos_sel[i + 1] * axe;
+ const float x1 = pos_sel[i] * (1 - axe);
+ const float y1 = pos_sel[i] * axe;
+ const float x2 = pos_sel[i + 1] * (1 - axe);
+ const float y2 = pos_sel[i + 1] * axe;
if (i % 2 == 1) {
immAttr4fv(col, col_sel);
@@ -2669,7 +2670,7 @@ void UI_draw_box_shadow(uchar alpha, float minx, float miny, float maxx, float m
GPU_blend(GPU_BLEND_ALPHA);
GPUVertFormat *format = immVertexFormat();
- uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
uint color = GPU_vertformat_attr_add(
format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
diff --git a/source/blender/editors/interface/interface_eyedropper_colorband.c b/source/blender/editors/interface/interface_eyedropper_colorband.c
index b757341ae13..76b361a9e68 100644
--- a/source/blender/editors/interface/interface_eyedropper_colorband.c
+++ b/source/blender/editors/interface/interface_eyedropper_colorband.c
@@ -109,7 +109,7 @@ static bool eyedropper_colorband_init(bContext *C, wmOperator *op)
}
if (!band) {
- PointerRNA ptr = CTX_data_pointer_get_type(C, "color_ramp", &RNA_ColorRamp);
+ const PointerRNA ptr = CTX_data_pointer_get_type(C, "color_ramp", &RNA_ColorRamp);
if (ptr.data != NULL) {
band = ptr.data;
@@ -200,7 +200,7 @@ static void eyedropper_colorband_apply(bContext *C, wmOperator *op)
{
EyedropperColorband *eye = op->customdata;
/* Always filter, avoids noise in resulting color-band. */
- bool filter_samples = true;
+ const bool filter_samples = true;
BKE_colorband_init_from_table_rgba(
eye->color_band, eye->color_buffer, eye->color_buffer_len, filter_samples);
eye->is_set = true;
@@ -339,7 +339,7 @@ static bool eyedropper_colorband_poll(bContext *C)
if (but && but->type == UI_BTYPE_COLORBAND) {
return true;
}
- PointerRNA ptr = CTX_data_pointer_get_type(C, "color_ramp", &RNA_ColorRamp);
+ const PointerRNA ptr = CTX_data_pointer_get_type(C, "color_ramp", &RNA_ColorRamp);
if (ptr.data != NULL) {
return true;
}
diff --git a/source/blender/editors/interface/interface_eyedropper_datablock.c b/source/blender/editors/interface/interface_eyedropper_datablock.c
index a162cac1b02..dd55d2c364b 100644
--- a/source/blender/editors/interface/interface_eyedropper_datablock.c
+++ b/source/blender/editors/interface/interface_eyedropper_datablock.c
@@ -117,7 +117,7 @@ static int datadropper_init(bContext *C, wmOperator *op)
* because this struct has very short lifetime. */
ddr->idcode_name = TIP_(BKE_idtype_idcode_to_name(ddr->idcode));
- PointerRNA ptr = RNA_property_pointer_get(&ddr->ptr, ddr->prop);
+ const PointerRNA ptr = RNA_property_pointer_get(&ddr->ptr, ddr->prop);
ddr->init_id = ptr.owner_id;
return true;
diff --git a/source/blender/editors/interface/interface_eyedropper_driver.c b/source/blender/editors/interface/interface_eyedropper_driver.c
index 0162e205c29..d13e47624ee 100644
--- a/source/blender/editors/interface/interface_eyedropper_driver.c
+++ b/source/blender/editors/interface/interface_eyedropper_driver.c
@@ -95,8 +95,8 @@ static void driverdropper_sample(bContext *C, wmOperator *op, const wmEvent *eve
DriverDropper *ddr = (DriverDropper *)op->customdata;
uiBut *but = eyedropper_get_property_button_under_mouse(C, event);
- short mapping_type = RNA_enum_get(op->ptr, "mapping_type");
- short flag = 0;
+ const short mapping_type = RNA_enum_get(op->ptr, "mapping_type");
+ const short flag = 0;
/* we can only add a driver if we know what RNA property it corresponds to */
if (but == NULL) {
@@ -105,7 +105,7 @@ static void driverdropper_sample(bContext *C, wmOperator *op, const wmEvent *eve
/* Get paths for src... */
PointerRNA *target_ptr = &but->rnapoin;
PropertyRNA *target_prop = but->rnaprop;
- int target_index = but->rnaindex;
+ const int target_index = but->rnaindex;
char *target_path = RNA_path_from_ID_to_property(target_ptr, target_prop);
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index 77cd3e00f3c..bf88b3c0318 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -85,23 +85,45 @@
# include "wm_window.h"
#endif
-/* place the mouse at the scaled down location when un-grabbing */
+/* -------------------------------------------------------------------- */
+/** \name Feature Defines
+ *
+ * These defines allow developers to locally toggle functionality which
+ * may be useful for testing (especially conflicts in dragging).
+ * Ideally the code would be refactored to support this functionality in a less fragile way.
+ * Until then keep these defines.
+ * \{ */
+
+/** Place the mouse at the scaled down location when un-grabbing. */
#define USE_CONT_MOUSE_CORRECT
-/* support dragging toggle buttons */
+/** Support dragging toggle buttons. */
#define USE_DRAG_TOGGLE
-/* support dragging multiple number buttons at once */
+/** Support dragging multiple number buttons at once. */
#define USE_DRAG_MULTINUM
-/* allow dragging/editing all other selected items at once */
+/** Allow dragging/editing all other selected items at once. */
#define USE_ALLSELECT
-/* so we can avoid very small mouse-moves from jumping away from keyboard navigation [#34936] */
+/**
+ * Check to avoid very small mouse-moves from jumping away from keyboard navigation,
+ * while larger mouse motion will override keyboard input, see: T34936.
+ */
#define USE_KEYNAV_LIMIT
-/* drag popups by their header */
+/** Support dragging popups by their header. */
#define USE_DRAG_POPUP
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Local Defines
+ * \{ */
+
+/**
+ * The buffer side used for password strings, where the password is stored internally,
+ * but not displayed.
+ */
#define UI_MAX_PASSWORD_STR 128
/**
@@ -117,7 +139,12 @@
*/
#define UI_DRAG_MAP_SOFT_RANGE_PIXEL_MAX 1000
-/* proto */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Local Prototypes
+ * \{ */
+
static int ui_do_but_EXIT(bContext *C,
uiBut *but,
struct uiHandleButtonData *data,
@@ -131,6 +158,8 @@ static void ui_mouse_motion_keynav_init(struct uiKeyNavLock *keynav, const wmEve
static bool ui_mouse_motion_keynav_test(struct uiKeyNavLock *keynav, const wmEvent *event);
#endif
+/** \} */
+
/* -------------------------------------------------------------------- */
/** \name Structs & Defines
* \{ */
@@ -667,9 +696,7 @@ static ListBase UIAfterFuncs = {NULL, NULL};
static uiAfterFunc *ui_afterfunc_new(void)
{
- uiAfterFunc *after;
-
- after = MEM_callocN(sizeof(uiAfterFunc), "uiAfterFunc");
+ uiAfterFunc *after = MEM_callocN(sizeof(uiAfterFunc), "uiAfterFunc");
BLI_addtail(&UIAfterFuncs, after);
@@ -718,7 +745,6 @@ static bool ui_afterfunc_check(const uiBlock *block, const uiBut *but)
static void ui_apply_but_func(bContext *C, uiBut *but)
{
- uiAfterFunc *after;
uiBlock *block = but->block;
/* these functions are postponed and only executed after all other
@@ -726,7 +752,7 @@ static void ui_apply_but_func(bContext *C, uiBut *but)
* with these functions removing the buttons we are working with */
if (ui_afterfunc_check(block, but)) {
- after = ui_afterfunc_new();
+ uiAfterFunc *after = ui_afterfunc_new();
if (but->func && ELEM(but, but->func_arg1, but->func_arg2)) {
/* exception, this will crash due to removed button otherwise */
@@ -788,8 +814,6 @@ static void ui_apply_but_func(bContext *C, uiBut *but)
/* typically call ui_apply_but_undo(), ui_apply_but_autokey() */
static void ui_apply_but_undo(uiBut *but)
{
- uiAfterFunc *after;
-
if (but->flag & UI_BUT_UNDO) {
const char *str = NULL;
bool skip_undo = false;
@@ -842,7 +866,7 @@ static void ui_apply_but_undo(uiBut *but)
}
/* delayed, after all other funcs run, popups are closed, etc */
- after = ui_afterfunc_new();
+ uiAfterFunc *after = ui_afterfunc_new();
BLI_strncpy(after->undostr, str, sizeof(after->undostr));
}
}
@@ -874,16 +898,12 @@ static void ui_apply_but_autokey(bContext *C, uiBut *but)
static void ui_apply_but_funcs_after(bContext *C)
{
- uiAfterFunc after;
- PointerRNA opptr;
- ListBase funcs;
-
/* copy to avoid recursive calls */
- funcs = UIAfterFuncs;
+ ListBase funcs = UIAfterFuncs;
BLI_listbase_clear(&UIAfterFuncs);
LISTBASE_FOREACH_MUTABLE (uiAfterFunc *, afterf, &funcs) {
- after = *afterf; /* copy to avoid memleak on exit() */
+ uiAfterFunc after = *afterf; /* copy to avoid memleak on exit() */
BLI_freelinkN(&funcs, afterf);
if (after.context) {
@@ -894,6 +914,7 @@ static void ui_apply_but_funcs_after(bContext *C)
popup_check(C, after.popup_op);
}
+ PointerRNA opptr;
if (after.opptr) {
/* free in advance to avoid leak on exit */
opptr = *after.opptr;
@@ -1142,12 +1163,10 @@ static void ui_apply_but_CURVEPROFILE(bContext *C, uiBut *but, uiHandleButtonDat
/* small multi-but api */
static void ui_multibut_add(uiHandleButtonData *data, uiBut *but)
{
- uiButMultiState *mbut_state;
-
BLI_assert(but->flag & UI_BUT_DRAG_MULTI);
BLI_assert(data->multi_data.has_mbuts);
- mbut_state = MEM_callocN(sizeof(*mbut_state), __func__);
+ uiButMultiState *mbut_state = MEM_callocN(sizeof(*mbut_state), __func__);
mbut_state->but = but;
mbut_state->origvalue = ui_but_value_get(but);
# ifdef USE_ALLSELECT
@@ -1161,9 +1180,7 @@ static void ui_multibut_add(uiHandleButtonData *data, uiBut *but)
static uiButMultiState *ui_multibut_lookup(uiHandleButtonData *data, const uiBut *but)
{
- LinkNode *l;
-
- for (l = data->multi_data.mbuts; l; l = l->next) {
+ for (LinkNode *l = data->multi_data.mbuts; l; l = l->next) {
uiButMultiState *mbut_state;
mbut_state = l->link;
@@ -1458,7 +1475,7 @@ static bool ui_drag_toggle_set_xy_xy(
/* execute the button */
if (ui_drag_toggle_but_is_supported(but)) {
/* is it pressed? */
- int pushed_state_but = ui_drag_toggle_but_pushed_state(C, but);
+ const int pushed_state_but = ui_drag_toggle_but_pushed_state(C, but);
if (pushed_state_but != pushed_state) {
UI_but_execute(C, region, but);
if (do_check) {
@@ -1487,7 +1504,6 @@ static void ui_drag_toggle_set(bContext *C, uiDragToggleHandle *drag_info, const
{
ARegion *region = CTX_wm_region(C);
bool do_draw = false;
- int xy[2];
/**
* Initialize Locking:
@@ -1526,6 +1542,7 @@ static void ui_drag_toggle_set(bContext *C, uiDragToggleHandle *drag_info, const
}
/* done with axis locking */
+ int xy[2];
xy[0] = (drag_info->xy_lock[0] == false) ? xy_input[0] : drag_info->xy_last[0];
xy[1] = (drag_info->xy_lock[1] == false) ? xy_input[1] : drag_info->xy_last[1];
@@ -1599,17 +1616,16 @@ static bool ui_but_is_drag_toggle(const uiBut *but)
static bool ui_selectcontext_begin(bContext *C, uiBut *but, uiSelectContextStore *selctx_data)
{
- PointerRNA ptr, lptr, idptr;
- PropertyRNA *prop, *lprop;
+ PointerRNA lptr, idptr;
+ PropertyRNA *lprop;
bool success = false;
- int index;
char *path = NULL;
ListBase lb = {NULL};
- ptr = but->rnapoin;
- prop = but->rnaprop;
- index = but->rnaindex;
+ PointerRNA ptr = but->rnapoin;
+ PropertyRNA *prop = but->rnaprop;
+ const int index = but->rnaindex;
/* for now don't support whole colors */
if (index == -1) {
@@ -1618,9 +1634,7 @@ static bool ui_selectcontext_begin(bContext *C, uiBut *but, uiSelectContextStore
/* if there is a valid property that is editable... */
if (ptr.data && prop) {
- CollectionPointerLink *link;
bool use_path_from_id;
- int i;
/* some facts we want to know */
const bool is_array = RNA_property_array_check(prop);
@@ -1631,8 +1645,11 @@ static bool ui_selectcontext_begin(bContext *C, uiBut *but, uiSelectContextStore
selctx_data->elems_len = BLI_listbase_count(&lb);
selctx_data->elems = MEM_mallocN(sizeof(uiSelectContextElem) * selctx_data->elems_len,
__func__);
-
- for (i = 0, link = lb.first; i < selctx_data->elems_len; i++, link = link->next) {
+ int i;
+ LISTBASE_FOREACH_INDEX (CollectionPointerLink *, link, &lb, i) {
+ if (i >= selctx_data->elems_len) {
+ break;
+ }
uiSelectContextElem *other = &selctx_data->elems[i];
/* TODO,. de-duplicate copy_to_selected_button */
if (link->ptr.data != ptr.data) {
@@ -1736,8 +1753,7 @@ static void ui_selectcontext_apply(bContext *C,
if (selctx_data->elems) {
PropertyRNA *prop = but->rnaprop;
PropertyRNA *lprop = but->rnaprop;
- int index = but->rnaindex;
- int i;
+ const int index = but->rnaindex;
const bool use_delta = (selctx_data->is_copy == false);
union {
@@ -1780,7 +1796,7 @@ static void ui_selectcontext_apply(bContext *C,
# ifdef USE_ALLSELECT_LAYER_HACK
/* make up for not having 'handle_layer_buttons' */
{
- PropertySubType subtype = RNA_property_subtype(prop);
+ const PropertySubType subtype = RNA_property_subtype(prop);
if ((rna_type == PROP_BOOLEAN) && ELEM(subtype, PROP_LAYER, PROP_LAYER_MEMBER) && is_array &&
/* could check for 'handle_layer_buttons' */
@@ -1792,7 +1808,7 @@ static void ui_selectcontext_apply(bContext *C,
tmparray[index] = true;
- for (i = 0; i < selctx_data->elems_len; i++) {
+ for (int i = 0; i < selctx_data->elems_len; i++) {
uiSelectContextElem *other = &selctx_data->elems[i];
PointerRNA lptr = other->ptr;
RNA_property_boolean_set_array(&lptr, lprop, tmparray);
@@ -1807,7 +1823,7 @@ static void ui_selectcontext_apply(bContext *C,
}
# endif
- for (i = 0; i < selctx_data->elems_len; i++) {
+ for (int i = 0; i < selctx_data->elems_len; i++) {
uiSelectContextElem *other = &selctx_data->elems[i];
PointerRNA lptr = other->ptr;
@@ -2016,14 +2032,7 @@ static void ui_apply_but_TRACKPREVIEW(bContext *C, uiBut *but, uiHandleButtonDat
static void ui_apply_but(
bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const bool interactive)
{
- const int but_type = but->type; /* Store as const to quiet maybe uninitialized warning. */
-
- char *editstr;
- double *editval;
- float *editvec;
- ColorBand *editcoba;
- CurveMapping *editcumap;
- CurveProfile *editprofile;
+ const eButType but_type = but->type; /* Store as const to quiet maybe uninitialized warning. */
data->retval = 0;
@@ -2076,9 +2085,12 @@ static void ui_apply_but(
}
/* ensures we are writing actual values */
- editstr = but->editstr;
- editval = but->editval;
- editvec = but->editvec;
+ char *editstr = but->editstr;
+ double *editval = but->editval;
+ float *editvec = but->editvec;
+ ColorBand *editcoba;
+ CurveMapping *editcumap;
+ CurveProfile *editprofile;
if (but_type == UI_BTYPE_COLORBAND) {
uiButColorBand *but_coba = (uiButColorBand *)but;
editcoba = but_coba->edit_coba;
@@ -2268,10 +2280,9 @@ static void ui_but_drop(bContext *C, const wmEvent *event, uiBut *but, uiHandleB
static void ui_but_get_pasted_text_from_clipboard(char **buf_paste, int *buf_len)
{
- char *text;
- int length;
/* get only first line even if the clipboard contains multiple lines */
- text = WM_clipboard_text_get_firstline(false, &length);
+ int length;
+ char *text = WM_clipboard_text_get_firstline(false, &length);
if (text) {
*buf_paste = text;
@@ -2342,7 +2353,7 @@ static void float_array_to_string(float *values,
static void ui_but_copy_numeric_array(uiBut *but, char *output, int output_len_max)
{
- int array_length = get_but_property_array_length(but);
+ const int array_length = get_but_property_array_length(but);
float *values = alloca(array_length * sizeof(float));
RNA_property_float_get_array(&but->rnapoin, but->rnaprop, values);
float_array_to_string(values, array_length, output, output_len_max);
@@ -2354,7 +2365,8 @@ static bool parse_float_array(char *text, float *values, int expected_length)
BLI_assert(0 <= expected_length && expected_length <= 4);
float v[5];
- int actual_length = sscanf(text, "[%f, %f, %f, %f, %f]", &v[0], &v[1], &v[2], &v[3], &v[4]);
+ const int actual_length = sscanf(
+ text, "[%f, %f, %f, %f, %f]", &v[0], &v[1], &v[2], &v[3], &v[4]);
if (actual_length == expected_length) {
memcpy(values, v, sizeof(float) * expected_length);
@@ -2368,7 +2380,7 @@ static void ui_but_paste_numeric_array(bContext *C,
uiHandleButtonData *data,
char *buf_paste)
{
- int array_length = get_but_property_array_length(but);
+ const int array_length = get_but_property_array_length(but);
if (array_length > 4) {
// not supported for now
return;
@@ -2398,7 +2410,6 @@ static void ui_but_paste_numeric_value(bContext *C,
char *buf_paste)
{
double value;
-
if (ui_but_string_eval_number(C, but, buf_paste, &value)) {
button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
data->value = value;
@@ -2460,7 +2471,7 @@ static void ui_but_paste_color(bContext *C, uiBut *but, char *buf_paste)
}
/* Some color properties are RGB, not RGBA. */
- int array_len = get_but_property_array_length(but);
+ const int array_len = get_but_property_array_length(but);
BLI_assert(ELEM(array_len, 3, 4));
ui_but_set_float_array(C, but, NULL, rgba, array_len);
}
@@ -2556,8 +2567,7 @@ static void ui_but_paste_CurveProfile(bContext *C, uiBut *but)
static void ui_but_copy_operator(bContext *C, uiBut *but, char *output, int output_len_max)
{
- PointerRNA *opptr;
- opptr = UI_but_operator_ptr_get(but);
+ PointerRNA *opptr = UI_but_operator_ptr_get(but);
char *str;
str = WM_operator_pystring_ex(C, NULL, false, true, but->optype, opptr);
@@ -2598,7 +2608,7 @@ static void ui_but_copy(bContext *C, uiBut *but, const bool copy_array)
/* Left false for copying internal data (color-band for eg). */
bool is_buf_set = false;
- bool has_required_data = !(but->poin == NULL && but->rnapoin.data == NULL);
+ const bool has_required_data = !(but->poin == NULL && but->rnapoin.data == NULL);
switch (but->type) {
case UI_BTYPE_NUM:
@@ -2689,7 +2699,7 @@ static void ui_but_paste(bContext *C, uiBut *but, uiHandleButtonData *data, cons
char *buf_paste;
ui_but_get_pasted_text_from_clipboard(&buf_paste, &buf_paste_len);
- bool has_required_data = !(but->poin == NULL && but->rnapoin.data == NULL);
+ const bool has_required_data = !(but->poin == NULL && but->rnapoin.data == NULL);
switch (but->type) {
case UI_BTYPE_NUM:
@@ -2769,12 +2779,9 @@ void ui_but_clipboard_free(void)
static int ui_text_position_from_hidden(uiBut *but, int pos)
{
- const char *strpos, *butstr;
- int i;
-
- butstr = (but->editstr) ? but->editstr : but->drawstr;
-
- for (i = 0, strpos = butstr; i < pos; i++) {
+ const char *butstr = (but->editstr) ? but->editstr : but->drawstr;
+ const char *strpos = butstr;
+ for (int i = 0; i < pos; i++) {
strpos = BLI_str_find_next_char_utf8(strpos, NULL);
}
@@ -2791,13 +2798,11 @@ void ui_but_text_password_hide(char password_str[UI_MAX_PASSWORD_STR],
uiBut *but,
const bool restore)
{
- char *butstr;
-
if (!(but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_PASSWORD)) {
return;
}
- butstr = (but->editstr) ? but->editstr : but->drawstr;
+ char *butstr = (but->editstr) ? but->editstr : but->drawstr;
if (restore) {
/* restore original string */
@@ -2898,7 +2903,7 @@ static bool ui_textedit_set_cursor_pos_foreach_glyph(const char *UNUSED(str),
void *user_data)
{
int *cursor_data = user_data;
- float center = glyph_step_bounds->xmin + (BLI_rctf_size_x(glyph_bounds) / 2.0f);
+ const float center = glyph_step_bounds->xmin + (BLI_rctf_size_x(glyph_bounds) / 2.0f);
if (cursor_data[0] < center) {
cursor_data[1] = str_step_ofs;
return false;
@@ -2967,7 +2972,7 @@ static void ui_textedit_set_cursor_pos(uiBut *but, uiHandleButtonData *data, con
else {
str_last = &str[but->ofs];
const int str_last_len = strlen(str_last);
- int x_pos = (int)(x - startx);
+ const int x_pos = (int)(x - startx);
int glyph_data[2] = {
x_pos, /* horizontal position to test. */
-1, /* Write the character offset here. */
@@ -3015,7 +3020,7 @@ static bool ui_textedit_insert_buf(uiBut *but,
int buf_len)
{
int len = strlen(data->str);
- int len_new = len - (but->selend - but->selsta) + 1;
+ const int len_new = len - (but->selend - but->selsta) + 1;
bool changed = false;
if (data->is_str_dynamic) {
@@ -3167,11 +3172,9 @@ static bool ui_textedit_delete(uiBut *but,
static int ui_textedit_autocomplete(bContext *C, uiBut *but, uiHandleButtonData *data)
{
- char *str;
- int changed;
-
- str = data->str;
+ char *str = data->str;
+ int changed;
if (data->searchbox) {
changed = ui_searchbox_autocomplete(C, data->searchbox, but, data->str);
}
@@ -3194,14 +3197,13 @@ enum {
static bool ui_textedit_copypaste(uiBut *but, uiHandleButtonData *data, const int mode)
{
- char *pbuf;
bool changed = false;
- int buf_len;
/* paste */
if (mode == UI_TEXTEDIT_PASTE) {
/* extract the first line from the clipboard */
- pbuf = WM_clipboard_text_get_firstline(false, &buf_len);
+ int buf_len;
+ char *pbuf = WM_clipboard_text_get_firstline(false, &buf_len);
if (pbuf) {
if (UI_but_is_utf8(but)) {
@@ -3218,7 +3220,7 @@ static bool ui_textedit_copypaste(uiBut *but, uiHandleButtonData *data, const in
/* cut & copy */
else if (ELEM(mode, UI_TEXTEDIT_COPY, UI_TEXTEDIT_CUT)) {
/* copy the contents to the copypaste buffer */
- int sellen = but->selend - but->selsta;
+ const int sellen = but->selend - but->selsta;
char *buf = MEM_mallocN(sizeof(char) * (sellen + 1), "ui_textedit_copypaste");
BLI_strncpy(buf, data->str + but->selsta, sellen + 1);
@@ -3281,7 +3283,6 @@ wmIMEData *ui_but_ime_data_get(uiBut *but)
static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data)
{
wmWindow *win = data->window;
- int len;
const bool is_num_but = ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER);
bool no_zero_strip = false;
@@ -3336,7 +3337,7 @@ static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data)
}
/* won't change from now on */
- len = strlen(data->str);
+ const int len = strlen(data->str);
data->origstr = BLI_strdupn(data->str, len);
data->sel_pos_init = 0;
@@ -3379,7 +3380,7 @@ static void ui_textedit_end(bContext *C, uiBut *but, uiHandleButtonData *data)
if (but) {
if (UI_but_is_utf8(but)) {
- int strip = BLI_utf8_invalid_strip(but->editstr, strlen(but->editstr));
+ const int strip = BLI_utf8_invalid_strip(but->editstr, strlen(but->editstr));
/* not a file?, strip non utf-8 chars */
if (strip) {
/* wont happen often so isn't that annoying to keep it here for a while */
@@ -3424,8 +3425,6 @@ static void ui_textedit_end(bContext *C, uiBut *but, uiHandleButtonData *data)
static void ui_textedit_next_but(uiBlock *block, uiBut *actbut, uiHandleButtonData *data)
{
- uiBut *but;
-
/* label and roundbox can overlap real buttons (backdrops...) */
if (ELEM(actbut->type,
UI_BTYPE_LABEL,
@@ -3436,7 +3435,7 @@ static void ui_textedit_next_but(uiBlock *block, uiBut *actbut, uiHandleButtonDa
return;
}
- for (but = actbut->next; but; but = but->next) {
+ for (uiBut *but = actbut->next; but; but = but->next) {
if (ui_but_is_editable_as_text(but)) {
if (!(but->flag & UI_BUT_DISABLED)) {
data->postbut = but;
@@ -3445,7 +3444,7 @@ static void ui_textedit_next_but(uiBlock *block, uiBut *actbut, uiHandleButtonDa
}
}
}
- for (but = block->buttons.first; but != actbut; but = but->next) {
+ for (uiBut *but = block->buttons.first; but != actbut; but = but->next) {
if (ui_but_is_editable_as_text(but)) {
if (!(but->flag & UI_BUT_DISABLED)) {
data->postbut = but;
@@ -3458,8 +3457,6 @@ static void ui_textedit_next_but(uiBlock *block, uiBut *actbut, uiHandleButtonDa
static void ui_textedit_prev_but(uiBlock *block, uiBut *actbut, uiHandleButtonData *data)
{
- uiBut *but;
-
/* label and roundbox can overlap real buttons (backdrops...) */
if (ELEM(actbut->type,
UI_BTYPE_LABEL,
@@ -3470,7 +3467,7 @@ static void ui_textedit_prev_but(uiBlock *block, uiBut *actbut, uiHandleButtonDa
return;
}
- for (but = actbut->prev; but; but = but->prev) {
+ for (uiBut *but = actbut->prev; but; but = but->prev) {
if (ui_but_is_editable_as_text(but)) {
if (!(but->flag & UI_BUT_DISABLED)) {
data->postbut = but;
@@ -3479,7 +3476,7 @@ static void ui_textedit_prev_but(uiBlock *block, uiBut *actbut, uiHandleButtonDa
}
}
}
- for (but = block->buttons.last; but != actbut; but = but->prev) {
+ for (uiBut *but = block->buttons.last; but != actbut; but = but->prev) {
if (ui_but_is_editable_as_text(but)) {
if (!(but->flag & UI_BUT_DISABLED)) {
data->postbut = but;
@@ -3499,9 +3496,9 @@ static void ui_do_but_textedit(
#ifdef WITH_INPUT_IME
wmWindow *win = CTX_wm_window(C);
wmIMEData *ime_data = win->ime_data;
- bool is_ime_composing = ime_data && ime_data->is_ime_composing;
+ const bool is_ime_composing = ime_data && ime_data->is_ime_composing;
#else
- bool is_ime_composing = false;
+ const bool is_ime_composing = false;
#endif
switch (event->type) {
@@ -3517,7 +3514,7 @@ static void ui_do_but_textedit(
ui_searchbox_event(C, data->searchbox, but, data->region, event);
}
#else
- ui_searchbox_event(C, data->searchbox, but, event);
+ ui_searchbox_event(C, data->searchbox, but, data->region, event);
#endif
}
@@ -3548,7 +3545,7 @@ static void ui_do_but_textedit(
}
break;
case LEFTMOUSE: {
- bool had_selection = but->selsta != but->selend;
+ const bool had_selection = but->selsta != but->selend;
/* exit on LMB only on RELEASE for searchbox, to mimic other popups,
* and allow multiple menu levels */
@@ -3559,10 +3556,8 @@ static void ui_do_but_textedit(
/* for double click: we do a press again for when you first click on button
* (selects all text, no cursor pos) */
if (event->val == KM_PRESS || event->val == KM_DBL_CLICK) {
- float mx, my;
-
- mx = event->x;
- my = event->y;
+ float mx = event->x;
+ float my = event->y;
ui_window_to_block_fl(data->region, block, &mx, &my);
if (ui_but_contains_pt(but, mx, my)) {
@@ -3708,7 +3703,7 @@ static void ui_do_but_textedit(
case EVT_TABKEY:
/* there is a key conflict here, we can't tab with autocomplete */
if (but->autocomplete_func || data->searchbox) {
- int autocomplete = ui_textedit_autocomplete(C, but, data);
+ const int autocomplete = ui_textedit_autocomplete(C, but, data);
changed = autocomplete != AUTOCOMPLETE_NO_MATCH;
if (autocomplete == AUTOCOMPLETE_FULL_MATCH) {
@@ -3772,7 +3767,7 @@ static void ui_do_but_textedit(
}
if (utf8_buf && utf8_buf[0]) {
- int utf8_buf_len = BLI_str_utf8_size(utf8_buf);
+ const int utf8_buf_len = BLI_str_utf8_size(utf8_buf);
BLI_assert(utf8_buf_len != -1);
changed = ui_textedit_insert_buf(but, data, event->utf8_buf, utf8_buf_len);
}
@@ -3832,12 +3827,12 @@ static void ui_do_but_textedit(
static void ui_do_but_textedit_select(
bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
{
- int mx, my, retval = WM_UI_HANDLER_CONTINUE;
+ int retval = WM_UI_HANDLER_CONTINUE;
switch (event->type) {
case MOUSEMOVE: {
- mx = event->x;
- my = event->y;
+ int mx = event->x;
+ int my = event->y;
ui_window_to_block(data->region, block, &mx, &my);
ui_textedit_set_cursor_select(but, data, event->x);
@@ -4297,7 +4292,7 @@ static int ui_do_but_HOTKEYEVT(bContext *C,
if (event->type == LEFTMOUSE && event->val == KM_PRESS) {
/* only cancel if click outside the button */
- if (ui_but_contains_point_px(but, but->active->region, event->x, event->y) == 0) {
+ if (ui_but_contains_point_px(but, but->active->region, event->x, event->y) == false) {
/* data->cancel doesn't work, this button opens immediate */
if (but->flag & UI_BUT_IMMEDIATE) {
ui_but_value_set(but, 0);
@@ -4407,7 +4402,7 @@ static int ui_do_but_TAB(
return WM_UI_HANDLER_BREAK;
}
if (ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY)) {
- int event_val = (is_property) ? KM_PRESS : KM_CLICK;
+ const int event_val = (is_property) ? KM_PRESS : KM_CLICK;
if (event->val == event_val) {
button_activate_state(C, but, BUTTON_STATE_EXIT);
return WM_UI_HANDLER_BREAK;
@@ -4546,7 +4541,6 @@ static int ui_do_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data, cons
static int ui_do_but_EXIT(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
{
-
if (data->state == BUTTON_STATE_HIGHLIGHT) {
/* first handle click on icondrag type button */
@@ -4616,7 +4610,7 @@ static float ui_numedit_apply_snapf(
if (ui_but_is_unit(but)) {
UnitSettings *unit = but->block->unit;
- int unit_type = RNA_SUBTYPE_UNIT_VALUE(UI_but_unit_type_get(but));
+ const int unit_type = RNA_SUBTYPE_UNIT_VALUE(UI_but_unit_type_get(but));
if (bUnit_IsValid(unit->system, unit_type)) {
fac = (float)bUnit_BaseScalar(unit->system, unit_type);
@@ -4639,7 +4633,7 @@ static float ui_numedit_apply_snapf(
* but allow for rotations */
if (softrange >= 21.0f) {
UnitSettings *unit = but->block->unit;
- int unit_type = UI_but_unit_type_get(but);
+ const int unit_type = UI_but_unit_type_get(but);
if ((unit_type == PROP_UNIT_ROTATION) && (unit->system_rotation != USER_UNIT_ROT_RADIANS)) {
/* pass (degrees)*/
}
@@ -4883,7 +4877,7 @@ static bool ui_numedit_but_NUM(uiBut *but,
static void ui_numedit_set_active(uiBut *but)
{
- int oldflag = but->drawflag;
+ const int oldflag = but->drawflag;
but->drawflag &= ~(UI_BUT_ACTIVE_LEFT | UI_BUT_ACTIVE_RIGHT);
uiHandleButtonData *data = but->active;
@@ -4933,13 +4927,14 @@ static void ui_numedit_set_active(uiBut *but)
static int ui_do_but_NUM(
bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
{
- int mx, my; /* mouse location scaled to fit the UI */
- int screen_mx, screen_my; /* mouse location kept at screen pixel coords */
int click = 0;
int retval = WM_UI_HANDLER_CONTINUE;
- mx = screen_mx = event->x;
- my = screen_my = event->y;
+ /* mouse location scaled to fit the UI */
+ int mx = event->x;
+ int my = event->y;
+ /* mouse location kept at screen pixel coords */
+ const int screen_mx = event->x;
ui_window_to_block(data->region, block, &mx, &my);
ui_numedit_set_active(but);
@@ -5154,7 +5149,7 @@ static bool ui_numedit_but_SLI(uiBut *but,
(but->softmax - but->softmin + but->a1);
}
else {
- float offs = (BLI_rctf_size_y(&but->rect) / 2.0f);
+ const float offs = (BLI_rctf_size_y(&but->rect) / 2.0f);
cursor_x_range = (BLI_rctf_size_x(&but->rect) - offs);
}
@@ -5245,11 +5240,11 @@ static bool ui_numedit_but_SLI(uiBut *but,
static int ui_do_but_SLI(
bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
{
- int mx, my, click = 0;
+ int click = 0;
int retval = WM_UI_HANDLER_CONTINUE;
- mx = event->x;
- my = event->y;
+ int mx = event->x;
+ int my = event->y;
ui_window_to_block(data->region, block, &mx, &my);
if (data->state == BUTTON_STATE_HIGHLIGHT) {
@@ -5448,12 +5443,11 @@ static int ui_do_but_SLI(
static int ui_do_but_SCROLL(
bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
{
- int mx, my /*, click = 0 */;
int retval = WM_UI_HANDLER_CONTINUE;
- bool horizontal = (BLI_rctf_size_x(&but->rect) > BLI_rctf_size_y(&but->rect));
+ const bool horizontal = (BLI_rctf_size_x(&but->rect) > BLI_rctf_size_y(&but->rect));
- mx = event->x;
- my = event->y;
+ int mx = event->x;
+ int my = event->y;
ui_window_to_block(data->region, block, &mx, &my);
if (data->state == BUTTON_STATE_HIGHLIGHT) {
@@ -5470,12 +5464,6 @@ static int ui_do_but_SCROLL(
button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
retval = WM_UI_HANDLER_BREAK;
}
- /* UNUSED - otherwise code is ok, add back if needed */
-#if 0
- else if (ELEM(event->type, PADENTER, RETKEY) && event->val == KM_PRESS) {
- click = 1;
- }
-#endif
}
}
else if (data->state == BUTTON_STATE_NUM_EDITING) {
@@ -5506,7 +5494,6 @@ static int ui_do_but_SCROLL(
static int ui_do_but_GRIP(
bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
{
- int mx, my;
int retval = WM_UI_HANDLER_CONTINUE;
const bool horizontal = (BLI_rctf_size_x(&but->rect) < BLI_rctf_size_y(&but->rect));
@@ -5516,8 +5503,8 @@ static int ui_do_but_GRIP(
* See T37739.
*/
- mx = event->x;
- my = event->y;
+ int mx = event->x;
+ int my = event->y;
ui_window_to_block(data->region, block, &mx, &my);
if (data->state == BUTTON_STATE_HIGHLIGHT) {
@@ -5581,7 +5568,6 @@ static int ui_do_but_LISTROW(bContext *C,
static int ui_do_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
{
-
if (data->state == BUTTON_STATE_HIGHLIGHT) {
/* first handle click on icondrag type button */
@@ -5666,8 +5652,7 @@ static int ui_do_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data, co
static bool ui_numedit_but_UNITVEC(
uiBut *but, uiHandleButtonData *data, int mx, int my, const enum eSnapType snap)
{
- float dx, dy, rad, radsq, mrad, *fp;
- int mdx, mdy;
+ float mrad;
bool changed = true;
/* button is presumed square */
@@ -5676,10 +5661,11 @@ static bool ui_numedit_but_UNITVEC(
/* note that both data->vec and data->origvec should be normalized
* else we'll get a harmless but annoying jump when first clicking */
- fp = data->origvec;
- rad = BLI_rctf_size_x(&but->rect);
- radsq = rad * rad;
+ float *fp = data->origvec;
+ const float rad = BLI_rctf_size_x(&but->rect);
+ const float radsq = rad * rad;
+ int mdx, mdy;
if (fp[2] > 0.0f) {
mdx = (rad * fp[0]);
mdy = (rad * fp[1]);
@@ -5694,8 +5680,8 @@ static bool ui_numedit_but_UNITVEC(
mdx = mdy = 0;
}
- dx = (float)(mx + mdx - data->dragstartx);
- dy = (float)(my + mdy - data->dragstarty);
+ float dx = (float)(mx + mdx - data->dragstartx);
+ float dy = (float)(my + mdy - data->dragstarty);
fp = data->vec;
mrad = dx * dx + dy * dy;
@@ -5724,11 +5710,10 @@ static bool ui_numedit_but_UNITVEC(
const int snap_steps = (snap == SNAP_ON) ? 4 : 12; /* 45 or 15 degree increments */
const float snap_steps_angle = M_PI / snap_steps;
float angle, angle_snap;
- int i;
/* round each axis of 'fp' to the next increment
* do this in "angle" space - this gives increments of same size */
- for (i = 0; i < 3; i++) {
+ for (int i = 0; i < 3; i++) {
angle = asinf(fp[i]);
angle_snap = roundf((angle / snap_steps_angle)) * snap_steps_angle;
fp[i] = sinf(angle_snap);
@@ -5799,7 +5784,7 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co
hsv[2] = clamp_f(hsv[2] + 0.05f, 0.0f, 1.0f);
}
else {
- float fac = 0.005 * (event->y - event->prevy);
+ const float fac = 0.005 * (event->y - event->prevy);
hsv[2] = clamp_f(hsv[2] + fac, 0.0f, 1.0f);
}
@@ -5903,10 +5888,8 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co
static int ui_do_but_UNITVEC(
bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
{
- int mx, my;
-
- mx = event->x;
- my = event->y;
+ int mx = event->x;
+ int my = event->y;
ui_window_to_block(data->region, block, &mx, &my);
if (data->state == BUTTON_STATE_HIGHLIGHT) {
@@ -6020,7 +6003,7 @@ static bool ui_numedit_but_HSVCUBE(uiBut *but,
float rgb[3];
float x, y;
float mx_fl, my_fl;
- bool changed = true;
+ const bool changed = true;
ui_mouse_scale_warp(data, mx, my, &mx_fl, &my_fl, shift);
@@ -6093,7 +6076,7 @@ static bool ui_numedit_but_HSVCUBE(uiBut *but,
break;
case UI_GRAD_V_ALT: {
/* vertical 'value' strip */
- float min = but->softmin, max = but->softmax;
+ const float min = but->softmin, max = but->softmax;
/* exception only for value strip - use the range set in but->min/max */
hsv[2] = y * (max - min) + min;
break;
@@ -6136,7 +6119,7 @@ static void ui_ndofedit_but_HSVCUBE(uiButHSVCube *hsv_but,
float *hsv = cpicker->color_data;
const float hsv_v_max = max_ff(hsv[2], hsv_but->but.softmax);
float rgb[3];
- float sensitivity = (shift ? 0.15f : 0.3f) * ndof->dt;
+ const float sensitivity = (shift ? 0.15f : 0.3f) * ndof->dt;
ui_but_v3_get(&hsv_but->but, rgb);
ui_scene_linear_to_color_picker_space(&hsv_but->but, rgb);
@@ -6199,10 +6182,8 @@ static int ui_do_but_HSVCUBE(
bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
{
uiButHSVCube *hsv_but = (uiButHSVCube *)but;
- int mx, my;
-
- mx = event->x;
- my = event->y;
+ int mx = event->x;
+ int my = event->y;
ui_window_to_block(data->region, block, &mx, &my);
if (data->state == BUTTON_STATE_HIGHLIGHT) {
@@ -6301,13 +6282,11 @@ static bool ui_numedit_but_HSVCIRCLE(uiBut *but,
const enum eSnapType snap,
const bool shift)
{
- rcti rect;
- bool changed = true;
- float mx_fl, my_fl;
- float rgb[3];
+ const bool changed = true;
ColorPicker *cpicker = but->custom_data;
float *hsv = cpicker->color_data;
+ float mx_fl, my_fl;
ui_mouse_scale_warp(data, mx, my, &mx_fl, &my_fl, shift);
#ifdef USE_CONT_MOUSE_CORRECT
@@ -6326,8 +6305,10 @@ static bool ui_numedit_but_HSVCIRCLE(uiBut *but,
}
#endif
+ rcti rect;
BLI_rcti_rctf_copy(&rect, &but->rect);
+ float rgb[3];
ui_but_v3_get(but, rgb);
ui_scene_linear_to_color_picker_space(but, rgb);
ui_rgb_to_color_picker_compat_v(rgb, hsv);
@@ -6405,7 +6386,7 @@ static void ui_ndofedit_but_HSVCIRCLE(uiBut *but,
float *hsv = cpicker->color_data;
float rgb[3];
float phi, r /*, sqr */ /* UNUSED */, v[2];
- float sensitivity = (shift ? 0.06f : 0.3f) * ndof->dt;
+ const float sensitivity = (shift ? 0.06f : 0.3f) * ndof->dt;
ui_but_v3_get(but, rgb);
ui_scene_linear_to_color_picker_space(but, rgb);
@@ -6477,9 +6458,8 @@ static int ui_do_but_HSVCIRCLE(
{
ColorPicker *cpicker = but->custom_data;
float *hsv = cpicker->color_data;
- int mx, my;
- mx = event->x;
- my = event->y;
+ int mx = event->x;
+ int my = event->y;
ui_window_to_block(data->region, block, &mx, &my);
if (data->state == BUTTON_STATE_HIGHLIGHT) {
@@ -6581,7 +6561,6 @@ static int ui_do_but_HSVCIRCLE(
static bool ui_numedit_but_COLORBAND(uiBut *but, uiHandleButtonData *data, int mx)
{
- float dx;
bool changed = false;
if (data->draglastx == mx) {
@@ -6592,7 +6571,7 @@ static bool ui_numedit_but_COLORBAND(uiBut *but, uiHandleButtonData *data, int m
return changed;
}
- dx = ((float)(mx - data->draglastx)) / BLI_rctf_size_x(&but->rect);
+ const float dx = ((float)(mx - data->draglastx)) / BLI_rctf_size_x(&but->rect);
data->dragcbd->pos += dx;
CLAMP(data->dragcbd->pos, 0.0f, 1.0f);
@@ -6608,33 +6587,32 @@ static bool ui_numedit_but_COLORBAND(uiBut *but, uiHandleButtonData *data, int m
static int ui_do_but_COLORBAND(
bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
{
- ColorBand *coba;
- CBData *cbd;
- /* ignore zoom-level for mindist */
- int mindist = (50 * UI_DPI_FAC) * block->aspect;
- int mx, my, a, xco;
-
- mx = event->x;
- my = event->y;
+ int mx = event->x;
+ int my = event->y;
ui_window_to_block(data->region, block, &mx, &my);
if (data->state == BUTTON_STATE_HIGHLIGHT) {
if (event->type == LEFTMOUSE && event->val == KM_PRESS) {
- coba = (ColorBand *)but->poin;
+ ColorBand *coba = (ColorBand *)but->poin;
if (event->ctrl) {
/* insert new key on mouse location */
- float pos = ((float)(mx - but->rect.xmin)) / BLI_rctf_size_x(&but->rect);
+ const float pos = ((float)(mx - but->rect.xmin)) / BLI_rctf_size_x(&but->rect);
BKE_colorband_element_add(coba, pos);
button_activate_state(C, but, BUTTON_STATE_EXIT);
}
else {
+ CBData *cbd;
+ /* ignore zoom-level for mindist */
+ int mindist = (50 * UI_DPI_FAC) * block->aspect;
+ int xco;
data->dragstartx = mx;
data->dragstarty = my;
data->draglastx = mx;
data->draglasty = my;
/* activate new key when mouse is close */
+ int a;
for (a = 0, cbd = coba->data; a < coba->tot; a++, cbd++) {
xco = but->rect.xmin + (cbd->pos * BLI_rctf_size_x(&but->rect));
xco = abs(xco - mx);
@@ -6693,22 +6671,19 @@ static bool ui_numedit_but_CURVE(uiBlock *block,
CurveMapping *cumap = (CurveMapping *)but->poin;
CurveMap *cuma = cumap->cm + cumap->cur;
CurveMapPoint *cmp = cuma->curve;
- float fx, fy, zoomx, zoomy;
- int mx, my, dragx, dragy;
- int a;
bool changed = false;
/* evtx evty and drag coords are absolute mousecoords,
* prevents errors when editing when layout changes */
- mx = evtx;
- my = evty;
+ int mx = evtx;
+ int my = evty;
ui_window_to_block(data->region, block, &mx, &my);
- dragx = data->draglastx;
- dragy = data->draglasty;
+ int dragx = data->draglastx;
+ int dragy = data->draglasty;
ui_window_to_block(data->region, block, &dragx, &dragy);
- zoomx = BLI_rctf_size_x(&but->rect) / BLI_rctf_size_x(&cumap->curr);
- zoomy = BLI_rctf_size_y(&but->rect) / BLI_rctf_size_y(&cumap->curr);
+ const float zoomx = BLI_rctf_size_x(&but->rect) / BLI_rctf_size_x(&cumap->curr);
+ const float zoomy = BLI_rctf_size_y(&but->rect) / BLI_rctf_size_y(&cumap->curr);
if (snap) {
float d[2];
@@ -6721,20 +6696,20 @@ static bool ui_numedit_but_CURVE(uiBlock *block,
}
}
+ float fx = (mx - dragx) / zoomx;
+ float fy = (my - dragy) / zoomy;
+
if (data->dragsel != -1) {
CurveMapPoint *cmp_last = NULL;
const float mval_factor = ui_mouse_scale_warp_factor(shift);
bool moved_point = false; /* for ctrl grid, can't use orig coords because of sorting */
- fx = (mx - dragx) / zoomx;
- fy = (my - dragy) / zoomy;
-
fx *= mval_factor;
fy *= mval_factor;
- for (a = 0; a < cuma->totpoint; a++) {
+ for (int a = 0; a < cuma->totpoint; a++) {
if (cmp[a].flag & CUMA_SELECT) {
- float origx = cmp[a].x, origy = cmp[a].y;
+ const float origx = cmp[a].x, origy = cmp[a].y;
cmp[a].x += fx;
cmp[a].y += fy;
if (snap) {
@@ -6771,9 +6746,6 @@ static bool ui_numedit_but_CURVE(uiBlock *block,
data->dragchange = true; /* mark for selection */
}
else {
- fx = (mx - dragx) / zoomx;
- fy = (my - dragy) / zoomy;
-
/* clamp for clip */
if (cumap->flag & CUMA_DO_CLIP) {
if (cumap->curr.xmin - fx < cumap->clipr.xmin) {
@@ -6807,20 +6779,18 @@ static bool ui_numedit_but_CURVE(uiBlock *block,
static int ui_do_but_CURVE(
bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
{
- int mx, my, a;
bool changed = false;
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- mx = event->x;
- my = event->y;
+ int mx = event->x;
+ int my = event->y;
ui_window_to_block(data->region, block, &mx, &my);
if (data->state == BUTTON_STATE_HIGHLIGHT) {
if (event->type == LEFTMOUSE && event->val == KM_PRESS) {
CurveMapping *cumap = (CurveMapping *)but->poin;
CurveMap *cuma = cumap->cm + cumap->cur;
- CurveMapPoint *cmp;
const float m_xy[2] = {mx, my};
float dist_min_sq = square_f(U.dpi_fac * 14.0f); /* 14 pixels radius */
int sel = -1;
@@ -6835,8 +6805,8 @@ static int ui_do_but_CURVE(
}
/* check for selecting of a point */
- cmp = cuma->curve; /* ctrl adds point, new malloc */
- for (a = 0; a < cuma->totpoint; a++) {
+ CurveMapPoint *cmp = cuma->curve; /* ctrl adds point, new malloc */
+ for (int a = 0; a < cuma->totpoint; a++) {
float f_xy[2];
BLI_rctf_transform_pt_v(&but->rect, &cumap->curr, f_xy, &cmp[a].x);
const float dist_sq = len_squared_v2v2(m_xy, f_xy);
@@ -6847,7 +6817,6 @@ static int ui_do_but_CURVE(
}
if (sel == -1) {
- int i;
float f_xy[2], f_xy_prev[2];
/* if the click didn't select anything, check if it's clicked on the
@@ -6860,7 +6829,7 @@ static int ui_do_but_CURVE(
dist_min_sq = square_f(U.dpi_fac * 8.0f);
/* loop through the curve segment table and find what's near the mouse. */
- for (i = 1; i <= CM_TABLE; i++) {
+ for (int i = 1; i <= CM_TABLE; i++) {
copy_v2_v2(f_xy_prev, f_xy);
BLI_rctf_transform_pt_v(&but->rect, &cumap->curr, f_xy, &cmp[i].x);
@@ -6877,7 +6846,7 @@ static int ui_do_but_CURVE(
cmp = cuma->curve;
/* find newly added point and make it 'sel' */
- for (a = 0; a < cuma->totpoint; a++) {
+ for (int a = 0; a < cuma->totpoint; a++) {
if (cmp[a].x == f_xy[0]) {
sel = a;
}
@@ -6891,7 +6860,7 @@ static int ui_do_but_CURVE(
/* ok, we move a point */
/* deselect all if this one is deselect. except if we hold shift */
if (!event->shift) {
- for (a = 0; a < cuma->totpoint; a++) {
+ for (int a = 0; a < cuma->totpoint; a++) {
cmp[a].flag &= ~CUMA_SELECT;
}
cmp[sel].flag |= CUMA_SELECT;
@@ -6935,7 +6904,7 @@ static int ui_do_but_CURVE(
if (data->dragchange == false) {
/* deselect all, select one */
if (!event->shift) {
- for (a = 0; a < cuma->totpoint; a++) {
+ for (int a = 0; a < cuma->totpoint; a++) {
cmp[a].flag &= ~CUMA_SELECT;
}
cmp[data->dragsel].flag |= CUMA_SELECT;
@@ -6970,22 +6939,19 @@ static bool ui_numedit_but_CURVEPROFILE(uiBlock *block,
{
CurveProfile *profile = (CurveProfile *)but->poin;
CurveProfilePoint *pts = profile->path;
- float fx, fy, zoomx, zoomy;
- int mx, my, dragx, dragy;
- int a;
bool changed = false;
/* evtx evty and drag coords are absolute mousecoords,
* prevents errors when editing when layout changes */
- mx = evtx;
- my = evty;
+ int mx = evtx;
+ int my = evty;
ui_window_to_block(data->region, block, &mx, &my);
- dragx = data->draglastx;
- dragy = data->draglasty;
+ int dragx = data->draglastx;
+ int dragy = data->draglasty;
ui_window_to_block(data->region, block, &dragx, &dragy);
- zoomx = BLI_rctf_size_x(&but->rect) / BLI_rctf_size_x(&profile->view_rect);
- zoomy = BLI_rctf_size_y(&but->rect) / BLI_rctf_size_y(&profile->view_rect);
+ const float zoomx = BLI_rctf_size_x(&but->rect) / BLI_rctf_size_x(&profile->view_rect);
+ const float zoomy = BLI_rctf_size_y(&but->rect) / BLI_rctf_size_y(&profile->view_rect);
if (snap) {
float d[2];
@@ -6998,8 +6964,8 @@ static bool ui_numedit_but_CURVEPROFILE(uiBlock *block,
}
}
- fx = (mx - dragx) / zoomx;
- fy = (my - dragy) / zoomy;
+ float fx = (mx - dragx) / zoomx;
+ float fy = (my - dragy) / zoomy;
if (data->dragsel != -1) {
float last_x, last_y;
@@ -7011,7 +6977,7 @@ static bool ui_numedit_but_CURVEPROFILE(uiBlock *block,
/* Move all selected points. */
const float delta[2] = {fx, fy};
- for (a = 0; a < profile->path_len; a++) {
+ for (int a = 0; a < profile->path_len; a++) {
/* Don't move the last and first control points. */
if ((pts[a].flag & PROF_SELECT) && (a != 0) && (a != profile->path_len)) {
moved_point |= BKE_curveprofile_move_point(profile, &pts[a], snap, delta);
@@ -7265,8 +7231,8 @@ static int ui_do_but_CURVEPROFILE(
static bool ui_numedit_but_HISTOGRAM(uiBut *but, uiHandleButtonData *data, int mx, int my)
{
Histogram *hist = (Histogram *)but->poin;
- bool changed = true;
- float dy = my - data->draglasty;
+ const bool changed = true;
+ const float dy = my - data->draglasty;
/* scale histogram values (dy / 10 for better control) */
const float yfac = min_ff(pow2f(hist->ymax), 1.0f) * 0.5f;
@@ -7284,10 +7250,8 @@ static bool ui_numedit_but_HISTOGRAM(uiBut *but, uiHandleButtonData *data, int m
static int ui_do_but_HISTOGRAM(
bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
{
- int mx, my;
-
- mx = event->x;
- my = event->y;
+ int mx = event->x;
+ int my = event->y;
ui_window_to_block(data->region, block, &mx, &my);
if (data->state == BUTTON_STATE_HIGHLIGHT) {
@@ -7341,10 +7305,9 @@ static int ui_do_but_HISTOGRAM(
static bool ui_numedit_but_WAVEFORM(uiBut *but, uiHandleButtonData *data, int mx, int my)
{
Scopes *scopes = (Scopes *)but->poin;
- bool changed = true;
- float dy;
+ const bool changed = true;
- dy = my - data->draglasty;
+ const float dy = my - data->draglasty;
/* scale waveform values */
scopes->wavefrm_yfac += dy / 200.0f;
@@ -7360,10 +7323,8 @@ static bool ui_numedit_but_WAVEFORM(uiBut *but, uiHandleButtonData *data, int mx
static int ui_do_but_WAVEFORM(
bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
{
- int mx, my;
-
- mx = event->x;
- my = event->y;
+ int mx = event->x;
+ int my = event->y;
ui_window_to_block(data->region, block, &mx, &my);
if (data->state == BUTTON_STATE_HIGHLIGHT) {
@@ -7418,11 +7379,10 @@ static bool ui_numedit_but_TRACKPREVIEW(
bContext *C, uiBut *but, uiHandleButtonData *data, int mx, int my, const bool shift)
{
MovieClipScopes *scopes = (MovieClipScopes *)but->poin;
- bool changed = true;
- float dx, dy;
+ const bool changed = true;
- dx = mx - data->draglastx;
- dy = my - data->draglasty;
+ float dx = mx - data->draglastx;
+ float dy = my - data->draglasty;
if (shift) {
dx /= 5.0f;
@@ -7431,7 +7391,7 @@ static bool ui_numedit_but_TRACKPREVIEW(
if (!scopes->track_locked) {
const MovieClip *clip = CTX_data_edit_movieclip(C);
- int clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(clip, scopes->framenr);
+ const int clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(clip, scopes->framenr);
if (scopes->marker->framenr != clip_framenr) {
scopes->marker = BKE_tracking_marker_ensure(scopes->track, clip_framenr);
}
@@ -7454,10 +7414,8 @@ static bool ui_numedit_but_TRACKPREVIEW(
static int ui_do_but_TRACKPREVIEW(
bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
{
- int mx, my;
-
- mx = event->x;
- my = event->y;
+ int mx = event->x;
+ int my = event->y;
ui_window_to_block(data->region, block, &mx, &my);
if (data->state == BUTTON_STATE_HIGHLIGHT) {
@@ -7502,13 +7460,10 @@ static int ui_do_but_TRACKPREVIEW(
static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *event)
{
- uiHandleButtonData *data;
- int retval;
-
- data = but->active;
- retval = WM_UI_HANDLER_CONTINUE;
+ uiHandleButtonData *data = but->active;
+ int retval = WM_UI_HANDLER_CONTINUE;
- bool is_disabled = but->flag & UI_BUT_DISABLED;
+ const bool is_disabled = but->flag & UI_BUT_DISABLED;
/* if but->pointype is set, but->poin should be too */
BLI_assert(!but->pointype || but->poin);
@@ -7521,8 +7476,8 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *
/* handle copy and paste */
bool is_press_ctrl_but_no_shift = event->val == KM_PRESS && IS_EVENT_MOD(event, ctrl, oskey) &&
!event->shift;
- bool do_copy = event->type == EVT_CKEY && is_press_ctrl_but_no_shift;
- bool do_paste = event->type == EVT_VKEY && is_press_ctrl_but_no_shift;
+ const bool do_copy = event->type == EVT_CKEY && is_press_ctrl_but_no_shift;
+ const bool do_paste = event->type == EVT_VKEY && is_press_ctrl_but_no_shift;
/* Specific handling for listrows, we try to find their overlapping tex button. */
if ((do_copy || do_paste) && but->type == UI_BTYPE_LISTROW) {
@@ -7798,9 +7753,7 @@ void UI_but_tooltip_refresh(bContext *C, uiBut *but)
*/
void UI_but_tooltip_timer_remove(bContext *C, uiBut *but)
{
- uiHandleButtonData *data;
-
- data = but->active;
+ uiHandleButtonData *data = but->active;
if (data) {
if (data->autoopentimer) {
WM_event_remove_timer(data->wm, data->window, data->autoopentimer);
@@ -7841,8 +7794,8 @@ static void button_tooltip_timer_reset(bContext *C, uiBut *but)
if ((U.flag & USER_TOOLTIPS) || (data->tooltip_force)) {
if (!but->block->tooltipdisabled) {
if (!wm->drags.first) {
- bool is_label = UI_but_has_tooltip_label(but);
- double delay = is_label ? UI_TOOLTIP_DELAY_LABEL : UI_TOOLTIP_DELAY;
+ const bool is_label = UI_but_has_tooltip_label(but);
+ const double delay = is_label ? UI_TOOLTIP_DELAY_LABEL : UI_TOOLTIP_DELAY;
WM_tooltip_timer_init_ex(
C, data->window, data->area, data->region, ui_but_tooltip_init, delay);
if (is_label) {
@@ -7875,9 +7828,7 @@ static bool button_modal_state(uiHandleButtonState state)
static void button_activate_state(bContext *C, uiBut *but, uiHandleButtonState state)
{
- uiHandleButtonData *data;
-
- data = but->active;
+ uiHandleButtonData *data = but->active;
if (data->state == state) {
return;
}
@@ -8047,13 +7998,11 @@ static void button_activate_init(bContext *C,
uiBut *but,
uiButtonActivateType type)
{
- uiHandleButtonData *data;
-
/* Only ever one active button! */
BLI_assert(ui_region_find_active_but(region) == NULL);
/* setup struct */
- data = MEM_callocN(sizeof(uiHandleButtonData), "uiHandleButtonData");
+ uiHandleButtonData *data = MEM_callocN(sizeof(uiHandleButtonData), "uiHandleButtonData");
data->wm = CTX_wm_manager(C);
data->window = CTX_wm_window(C);
data->area = CTX_wm_area(C);
@@ -8277,13 +8226,11 @@ static void button_activate_exit(
void ui_but_active_free(const bContext *C, uiBut *but)
{
- uiHandleButtonData *data;
-
/* this gets called when the button somehow disappears while it is still
* active, this is bad for user interaction, but we need to handle this
* case cleanly anyway in case it happens */
if (but->active) {
- data = but->active;
+ uiHandleButtonData *data = but->active;
data->cancel = true;
button_activate_exit((bContext *)C, but, data, false, true);
}
@@ -8462,11 +8409,10 @@ void UI_context_update_anim_flag(const bContext *C)
struct Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(
depsgraph, (scene) ? scene->r.cfra : 0.0f);
- uiBut *activebut;
while (region) {
/* find active button */
- activebut = NULL;
+ uiBut *activebut = NULL;
LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
@@ -8524,10 +8470,8 @@ static uiBut *ui_but_find_open_event(ARegion *region, const wmEvent *event)
static int ui_handle_button_over(bContext *C, const wmEvent *event, ARegion *region)
{
- uiBut *but;
-
if (event->type == MOUSEMOVE) {
- but = ui_but_find_mouse_over(region, event);
+ uiBut *but = ui_but_find_mouse_over(region, event);
if (but) {
button_activate_init(C, region, but, BUTTON_ACTIVATE_OVER);
@@ -8538,7 +8482,7 @@ static int ui_handle_button_over(bContext *C, const wmEvent *event, ARegion *reg
}
}
else if (event->type == EVT_BUT_OPEN) {
- but = ui_but_find_open_event(region, event);
+ uiBut *but = ui_but_find_open_event(region, event);
if (but) {
button_activate_init(C, region, but, BUTTON_ACTIVATE_OVER);
ui_do_button(C, but->block, but, event);
@@ -8556,10 +8500,10 @@ static int ui_handle_button_over(bContext *C, const wmEvent *event, ARegion *reg
void ui_but_activate_event(bContext *C, ARegion *region, uiBut *but)
{
wmWindow *win = CTX_wm_window(C);
- wmEvent event;
button_activate_init(C, region, but, BUTTON_ACTIVATE_OVER);
+ wmEvent event;
wm_event_init_from_window(win, &event);
event.type = EVT_BUT_OPEN;
event.val = KM_PRESS;
@@ -8617,12 +8561,9 @@ static void ui_handle_button_activate(bContext *C,
uiBut *but,
uiButtonActivateType type)
{
- uiBut *oldbut;
- uiHandleButtonData *data;
-
- oldbut = ui_region_find_active_but(region);
+ uiBut *oldbut = ui_region_find_active_but(region);
if (oldbut) {
- data = oldbut->active;
+ uiHandleButtonData *data = oldbut->active;
data->cancel = true;
button_activate_exit(C, oldbut, data, false, false);
}
@@ -8665,7 +8606,7 @@ static bool ui_handle_button_activate_by_type(bContext *C, ARegion *region, uiBu
static bool ui_button_value_default(uiBut *but, double *r_value)
{
if (but->rnaprop != NULL && ui_but_is_rna_valid(but)) {
- int type = RNA_property_type(but->rnaprop);
+ const int type = RNA_property_type(but->rnaprop);
if (ELEM(type, PROP_FLOAT, PROP_INT)) {
double default_value;
switch (type) {
@@ -8699,14 +8640,11 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but)
{
uiHandleButtonData *data = but->active;
const uiHandleButtonState state_orig = data->state;
- uiBlock *block;
- ARegion *region;
- int retval;
- block = but->block;
- region = data->region;
+ uiBlock *block = but->block;
+ ARegion *region = data->region;
- retval = WM_UI_HANDLER_CONTINUE;
+ int retval = WM_UI_HANDLER_CONTINUE;
if (data->state == BUTTON_STATE_HIGHLIGHT) {
switch (event->type) {
@@ -8918,7 +8856,7 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but)
data = but->active;
if (data && data->state == BUTTON_STATE_EXIT) {
uiBut *post_but = data->postbut;
- uiButtonActivateType post_type = data->posttype;
+ const uiButtonActivateType post_type = data->posttype;
/* Reset the button value when empty text is typed. */
if ((data->cancel == false) && (data->str != NULL) && (data->str[0] == '\0') &&
@@ -8973,21 +8911,18 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but)
static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *region, uiBut *listbox)
{
- uiList *ui_list;
- uiListDyn *dyn_data;
int retval = WM_UI_HANDLER_CONTINUE;
int type = event->type, val = event->val;
bool redraw = false;
- int mx, my;
- ui_list = listbox->custom_data;
+ uiList *ui_list = listbox->custom_data;
if (!ui_list || !ui_list->dyn_data) {
return retval;
}
- dyn_data = ui_list->dyn_data;
+ uiListDyn *dyn_data = ui_list->dyn_data;
- mx = event->x;
- my = event->y;
+ int mx = event->x;
+ int my = event->y;
ui_window_to_block(region, listbox->block, &mx, &my);
/* Convert pan to scroll-wheel. */
@@ -9023,11 +8958,11 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *regi
* collection order, we have some work! */
int *org_order = MEM_mallocN(dyn_data->items_shown * sizeof(int), __func__);
const int *new_order = dyn_data->items_filter_neworder;
- int i, org_idx = -1, len = dyn_data->items_len;
+ int org_idx = -1, len = dyn_data->items_len;
int current_idx = -1;
- int filter_exclude = ui_list->filter_flag & UILST_FLT_EXCLUDE;
+ const int filter_exclude = ui_list->filter_flag & UILST_FLT_EXCLUDE;
- for (i = 0; i < len; i++) {
+ for (int i = 0; i < len; i++) {
if (!dyn_data->items_filter_flags ||
((dyn_data->items_filter_flags[i] & UILST_FLT_ITEM) ^ filter_exclude)) {
org_order[new_order ? new_order[++org_idx] : ++org_idx] = i;
@@ -9106,11 +9041,8 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *regi
static void ui_handle_button_return_submenu(bContext *C, const wmEvent *event, uiBut *but)
{
- uiHandleButtonData *data;
- uiPopupBlockHandle *menu;
-
- data = but->active;
- menu = data->menu;
+ uiHandleButtonData *data = but->active;
+ uiPopupBlockHandle *menu = data->menu;
/* copy over return values from the closing menu */
if ((menu->menuretval & UI_RETURN_OK) || (menu->menuretval & UI_RETURN_UPDATE)) {
@@ -9212,13 +9144,6 @@ static bool ui_mouse_motion_towards_check(uiBlock *block,
const int xy[2],
const bool use_wiggle_room)
{
- float p1[2], p2[2], p3[2], p4[2];
- float oldp[2] = {menu->towards_xy[0], menu->towards_xy[1]};
- const float newp[2] = {xy[0], xy[1]};
- bool closer;
- const float margin = MENU_TOWARDS_MARGIN;
- rctf rect_px;
-
BLI_assert(block->flag & (UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_POPOVER));
/* annoying fix for [#36269], this is a bit odd but in fact works quite well
@@ -9240,6 +9165,8 @@ static bool ui_mouse_motion_towards_check(uiBlock *block,
return false;
}
+ float oldp[2] = {menu->towards_xy[0], menu->towards_xy[1]};
+ const float newp[2] = {xy[0], xy[1]};
if (len_squared_v2v2(oldp, newp) < (4.0f * 4.0f)) {
return menu->dotowards;
}
@@ -9247,19 +9174,15 @@ static bool ui_mouse_motion_towards_check(uiBlock *block,
/* verify that we are moving towards one of the edges of the
* menu block, in other words, in the triangle formed by the
* initial mouse location and two edge points. */
+ rctf rect_px;
ui_block_to_window_rctf(menu->region, block, &rect_px, &block->rect);
- p1[0] = rect_px.xmin - margin;
- p1[1] = rect_px.ymin - margin;
-
- p2[0] = rect_px.xmax + margin;
- p2[1] = rect_px.ymin - margin;
-
- p3[0] = rect_px.xmax + margin;
- p3[1] = rect_px.ymax + margin;
+ const float margin = MENU_TOWARDS_MARGIN;
- p4[0] = rect_px.xmin - margin;
- p4[1] = rect_px.ymax + margin;
+ const float p1[2] = {rect_px.xmin - margin, rect_px.ymin - margin};
+ const float p2[2] = {rect_px.xmax + margin, rect_px.ymin - margin};
+ const float p3[2] = {rect_px.xmax + margin, rect_px.ymax + margin};
+ const float p4[2] = {rect_px.xmin - margin, rect_px.ymax + margin};
/* allow for some wiggle room, if the user moves a few pixels away,
* don't immediately quit (only for top level menus) */
@@ -9272,8 +9195,9 @@ static bool ui_mouse_motion_towards_check(uiBlock *block,
add_v2_v2(oldp, delta);
}
- closer = (isect_point_tri_v2(newp, oldp, p1, p2) || isect_point_tri_v2(newp, oldp, p2, p3) ||
- isect_point_tri_v2(newp, oldp, p3, p4) || isect_point_tri_v2(newp, oldp, p4, p1));
+ bool closer = (isect_point_tri_v2(newp, oldp, p1, p2) ||
+ isect_point_tri_v2(newp, oldp, p2, p3) ||
+ isect_point_tri_v2(newp, oldp, p3, p4) || isect_point_tri_v2(newp, oldp, p4, p1));
if (!closer) {
menu->dotowards = false;
@@ -9475,7 +9399,6 @@ static int ui_handle_menu_button(bContext *C, const wmEvent *event, uiPopupBlock
{
ARegion *region = menu->region;
uiBut *but = ui_region_find_active_but(region);
- int retval;
if (but) {
/* Its possible there is an active menu item NOT under the mouse,
@@ -9503,6 +9426,7 @@ static int ui_handle_menu_button(bContext *C, const wmEvent *event, uiPopupBlock
}
}
+ int retval;
if (but) {
ScrArea *ctx_area = CTX_wm_area(C);
ARegion *ctx_region = CTX_wm_region(C);
@@ -9533,8 +9457,6 @@ static int ui_handle_menu_button(bContext *C, const wmEvent *event, uiPopupBlock
float ui_block_calc_pie_segment(uiBlock *block, const float event_xy[2])
{
float seg1[2];
- float seg2[2];
- float len;
if (block->pie_data.flags & UI_PIE_INITIAL_DIRECTION) {
copy_v2_v2(seg1, block->pie_data.pie_center_init);
@@ -9543,9 +9465,10 @@ float ui_block_calc_pie_segment(uiBlock *block, const float event_xy[2])
copy_v2_v2(seg1, block->pie_data.pie_center_spawned);
}
+ float seg2[2];
sub_v2_v2v2(seg2, event_xy, seg1);
- len = normalize_v2_v2(block->pie_data.pie_dir, seg2);
+ const float len = normalize_v2_v2(block->pie_data.pie_dir, seg2);
if (len < U.pie_menu_threshold * U.dpi_fac) {
block->pie_data.flags |= UI_PIE_INVALID_DIR;
@@ -9565,25 +9488,20 @@ static int ui_handle_menu_event(bContext *C,
const bool is_parent_menu,
const bool is_floating)
{
- ARegion *region;
- uiBlock *block;
uiBut *but;
- int mx, my, retval;
- bool inside;
- bool inside_title; /* check for title dragging */
-
- region = menu->region;
- block = region->uiblocks.first;
+ ARegion *region = menu->region;
+ uiBlock *block = region->uiblocks.first;
- retval = WM_UI_HANDLER_CONTINUE;
+ int retval = WM_UI_HANDLER_CONTINUE;
- mx = event->x;
- my = event->y;
+ int mx = event->x;
+ int my = event->y;
ui_window_to_block(region, block, &mx, &my);
/* check if mouse is inside block */
- inside = BLI_rctf_isect_pt(&block->rect, mx, my);
- inside_title = inside && ((my + (UI_UNIT_Y * 1.5f)) > block->rect.ymax);
+ const bool inside = BLI_rctf_isect_pt(&block->rect, mx, my);
+ /* check for title dragging */
+ const bool inside_title = inside && ((my + (UI_UNIT_Y * 1.5f)) > block->rect.ymax);
/* if there's an active modal button, don't check events or outside, except for search menu */
but = ui_region_find_active_but(region);
@@ -9762,7 +9680,7 @@ static int ui_handle_menu_event(bContext *C,
if (val == KM_PRESS) {
/* Determine scroll operation. */
uiMenuScrollType scrolltype;
- bool ui_block_flipped = (block->flag & UI_BLOCK_IS_FLIP) != 0;
+ const bool ui_block_flipped = (block->flag & UI_BLOCK_IS_FLIP) != 0;
if (ELEM(type, EVT_PAGEUPKEY, EVT_HOMEKEY)) {
scrolltype = ui_block_flipped ? MENU_SCROLL_TOP : MENU_SCROLL_BOTTOM;
@@ -10007,7 +9925,7 @@ static int ui_handle_menu_event(bContext *C,
* Events handled above may have already set the return value,
* don't overwrite them, see: T61015.
*/
- if ((inside == 0) && (menu->menuretval == 0)) {
+ if ((inside == false) && (menu->menuretval == 0)) {
uiSafetyRct *saferct = block->saferct.first;
if (ELEM(event->type, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE)) {
@@ -10094,7 +10012,7 @@ static int ui_handle_menu_event(bContext *C,
else {
/* check mouse moving outside of the menu */
- if (inside == 0 && (block->flag & (UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_POPOVER))) {
+ if (inside == false && (block->flag & (UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_POPOVER))) {
uiSafetyRct *saferct;
ui_mouse_motion_towards_check(block, menu, &event->x, is_parent_inside == false);
@@ -10173,21 +10091,15 @@ static int ui_handle_menu_return_submenu(bContext *C,
const wmEvent *event,
uiPopupBlockHandle *menu)
{
- ARegion *region;
- uiBut *but;
- uiBlock *block;
- uiHandleButtonData *data;
- uiPopupBlockHandle *submenu;
-
- region = menu->region;
- block = region->uiblocks.first;
+ ARegion *region = menu->region;
+ uiBlock *block = region->uiblocks.first;
- but = ui_region_find_active_but(region);
+ uiBut *but = ui_region_find_active_but(region);
BLI_assert(but);
- data = but->active;
- submenu = data->menu;
+ uiHandleButtonData *data = but->active;
+ uiPopupBlockHandle *submenu = data->menu;
if (submenu->menuretval) {
bool update;
@@ -10234,7 +10146,7 @@ static int ui_but_pie_menu_apply(bContext *C,
uiBut *but,
bool force_close)
{
- int retval = WM_UI_HANDLER_BREAK;
+ const int retval = WM_UI_HANDLER_BREAK;
if (but && ui_but_pie_menu_supported_apply(but)) {
if (but->type == UI_BTYPE_MENU) {
@@ -10282,13 +10194,11 @@ static uiBut *ui_block_pie_dir_activate(uiBlock *block, const wmEvent *event, Ra
static int ui_but_pie_button_activate(bContext *C, uiBut *but, uiPopupBlockHandle *menu)
{
- uiBut *active_but;
-
if (but == NULL) {
return WM_UI_HANDLER_BREAK;
}
- active_but = ui_region_find_active_but(menu->region);
+ uiBut *active_but = ui_region_find_active_but(menu->region);
if (active_but) {
button_activate_exit(C, active_but, active_but->active, false, false);
@@ -10300,13 +10210,6 @@ static int ui_but_pie_button_activate(bContext *C, uiBut *but, uiPopupBlockHandl
static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle *menu)
{
- ARegion *region;
- uiBlock *block;
- float event_xy[2];
- double duration;
- bool is_click_style;
- float dist;
-
/* we block all events, this is modal interaction,
* except for drop events which is described below */
int retval = WM_UI_HANDLER_BREAK;
@@ -10317,10 +10220,10 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle
retval = WM_UI_HANDLER_CONTINUE;
}
- region = menu->region;
- block = region->uiblocks.first;
+ ARegion *region = menu->region;
+ uiBlock *block = region->uiblocks.first;
- is_click_style = (block->pie_data.flags & UI_PIE_CLICK_STYLE);
+ const bool is_click_style = (block->pie_data.flags & UI_PIE_CLICK_STYLE);
/* if there's an active modal button, don't check events or outside, except for search menu */
uiBut *but_active = ui_region_find_active_but(region);
@@ -10331,15 +10234,14 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle
menu->scrolltimer->duration = 0.0;
}
- duration = menu->scrolltimer->duration;
+ const double duration = menu->scrolltimer->duration;
- event_xy[0] = event->x;
- event_xy[1] = event->y;
+ float event_xy[2] = {event->x, event->y};
ui_window_to_block_fl(region, block, &event_xy[0], &event_xy[1]);
/* Distance from initial point. */
- dist = ui_block_calc_pie_segment(block, event_xy);
+ const float dist = ui_block_calc_pie_segment(block, event_xy);
if (but_active && button_modal_state(but_active->active->state)) {
retval = ui_handle_menu_button(C, event, menu);
@@ -10354,9 +10256,9 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle
/* handle animation */
if (!(block->pie_data.flags & UI_PIE_ANIMATION_FINISHED)) {
- double final_time = 0.01 * U.pie_animation_timeout;
+ const double final_time = 0.01 * U.pie_animation_timeout;
float fac = duration / final_time;
- float pie_radius = U.pie_menu_radius * UI_DPI_FAC;
+ const float pie_radius = U.pie_menu_radius * UI_DPI_FAC;
if (fac > 1.0f) {
fac = 1.0f;
@@ -10446,7 +10348,7 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle
switch (event->type) {
case MOUSEMOVE:
if (!is_click_style) {
- float len_sq = len_squared_v2v2(event_xy, block->pie_data.pie_center_init);
+ const float len_sq = len_squared_v2v2(event_xy, block->pie_data.pie_center_init);
/* here we use the initial position explicitly */
if (len_sq > PIE_CLICK_THRESHOLD_SQ) {
@@ -10569,16 +10471,13 @@ static int ui_handle_menus_recursive(bContext *C,
const bool is_parent_menu,
const bool is_floating)
{
- uiBut *but;
- uiHandleButtonData *data;
- uiPopupBlockHandle *submenu;
int retval = WM_UI_HANDLER_CONTINUE;
bool do_towards_reinit = false;
/* check if we have a submenu, and handle events for it first */
- but = ui_region_find_active_but(menu->region);
- data = (but) ? but->active : NULL;
- submenu = (data) ? data->menu : NULL;
+ uiBut *but = ui_region_find_active_but(menu->region);
+ uiHandleButtonData *data = (but) ? but->active : NULL;
+ uiPopupBlockHandle *submenu = (data) ? data->menu : NULL;
if (submenu) {
uiBlock *block = menu->region->uiblocks.first;
@@ -10586,14 +10485,13 @@ static int ui_handle_menus_recursive(bContext *C,
bool inside = false;
/* root pie menus accept the key that spawned
* them as double click to improve responsiveness */
- bool do_recursion = (!(block->flag & UI_BLOCK_RADIAL) || event->type != block->pie_data.event);
+ const bool do_recursion = (!(block->flag & UI_BLOCK_RADIAL) ||
+ event->type != block->pie_data.event);
if (do_recursion) {
if (is_parent_inside == false) {
- int mx, my;
-
- mx = event->x;
- my = event->y;
+ int mx = event->x;
+ int my = event->y;
ui_window_to_block(menu->region, block, &mx, &my);
inside = BLI_rctf_isect_pt(&block->rect, mx, my);
}
@@ -10613,7 +10511,7 @@ static int ui_handle_menus_recursive(bContext *C,
(void)submenu;
/* we may want to quit the submenu and handle the even in this menu,
* if its important to use it, check 'data->menu' first */
- if (((retval == WM_UI_HANDLER_BREAK) && do_ret_out_parent) == 0) {
+ if (((retval == WM_UI_HANDLER_BREAK) && do_ret_out_parent) == false) {
/* skip applying the event */
return retval;
}
@@ -10643,7 +10541,7 @@ static int ui_handle_menus_recursive(bContext *C,
bool handled = false;
if (listbox) {
- int retval_test = ui_handle_list_event(C, event, menu->region, listbox);
+ const int retval_test = ui_handle_list_event(C, event, menu->region, listbox);
if (retval_test != WM_UI_HANDLER_CONTINUE) {
retval = retval_test;
handled = true;
@@ -10685,21 +10583,17 @@ void UI_popup_menu_retval_set(const uiBlock *block, const int retval, const bool
static int ui_region_handler(bContext *C, const wmEvent *event, void *UNUSED(userdata))
{
- ARegion *region;
- uiBut *but, *listbox;
- int retval;
-
/* here we handle buttons at the region level, non-modal */
- region = CTX_wm_region(C);
- retval = WM_UI_HANDLER_CONTINUE;
+ ARegion *region = CTX_wm_region(C);
+ int retval = WM_UI_HANDLER_CONTINUE;
if (region == NULL || BLI_listbase_is_empty(&region->uiblocks)) {
return retval;
}
/* either handle events for already activated button or try to activate */
- but = ui_region_find_active_but(region);
- listbox = ui_list_find_mouse_over(region, event);
+ uiBut *but = ui_region_find_active_but(region);
+ uiBut *listbox = ui_list_find_mouse_over(region, event);
retval = ui_handler_panel_region(C, event, region, listbox ? listbox : but);
@@ -10736,17 +10630,14 @@ static int ui_region_handler(bContext *C, const wmEvent *event, void *UNUSED(use
static void ui_region_handler_remove(bContext *C, void *UNUSED(userdata))
{
- bScreen *screen;
- ARegion *region;
-
- region = CTX_wm_region(C);
+ ARegion *region = CTX_wm_region(C);
if (region == NULL) {
return;
}
UI_blocklist_free(C, &region->uiblocks);
- screen = CTX_wm_screen(C);
+ bScreen *screen = CTX_wm_screen(C);
if (screen == NULL) {
return;
}
@@ -10765,18 +10656,16 @@ static int ui_handler_region_menu(bContext *C, const wmEvent *event, void *UNUSE
{
ARegion *menu_region = CTX_wm_menu(C);
ARegion *region = menu_region ? menu_region : CTX_wm_region(C);
- uiBut *but;
int retval = WM_UI_HANDLER_CONTINUE;
- but = ui_region_find_active_but(region);
+ uiBut *but = ui_region_find_active_but(region);
if (but) {
bScreen *screen = CTX_wm_screen(C);
uiBut *but_other;
- uiHandleButtonData *data;
/* handle activated button events */
- data = but->active;
+ uiHandleButtonData *data = but->active;
if ((data->state == BUTTON_STATE_MENU_OPEN) &&
/* Make sure this popup isn't dragging a button.
@@ -10858,13 +10747,12 @@ static int ui_handler_region_menu(bContext *C, const wmEvent *event, void *UNUSE
static int ui_popup_handler(bContext *C, const wmEvent *event, void *userdata)
{
uiPopupBlockHandle *menu = userdata;
- struct ARegion *menu_region;
/* we block all events, this is modal interaction,
* except for drop events which is described below */
int retval = WM_UI_HANDLER_BREAK;
bool reset_pie = false;
- menu_region = CTX_wm_menu(C);
+ ARegion *menu_region = CTX_wm_menu(C);
CTX_wm_menu_set(C, menu->region);
if (event->type == EVT_DROP || event->val == KM_DBL_CLICK) {
@@ -10886,7 +10774,7 @@ static int ui_popup_handler(bContext *C, const wmEvent *event, void *userdata)
if (menu->menuretval) {
wmWindow *win = CTX_wm_window(C);
/* copy values, we have to free first (closes region) */
- uiPopupBlockHandle temp = *menu;
+ const uiPopupBlockHandle temp = *menu;
uiBlock *block = menu->region->uiblocks.first;
/* set last pie event to allow chained pie spawning */
diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c
index edf66d82cbc..887f149ee12 100644
--- a/source/blender/editors/interface/interface_icons.c
+++ b/source/blender/editors/interface/interface_icons.c
@@ -264,9 +264,9 @@ static void viconutil_set_point(GLint pt[2], int x, int y)
static void vicon_small_tri_right_draw(int x, int y, int w, int UNUSED(h), float alpha)
{
GLint pts[3][2];
- int cx = x + w / 2 - 4;
- int cy = y + w / 2;
- int d = w / 5, d2 = w / 7;
+ const int cx = x + w / 2 - 4;
+ const int cy = y + w / 2;
+ const int d = w / 5, d2 = w / 7;
viconutil_set_point(pts[0], cx - d2, cy + d);
viconutil_set_point(pts[1], cx - d2, cy - d);
@@ -301,17 +301,17 @@ static void vicon_keytype_draw_wrapper(
* while the draw_keyframe_shape() function needs the midpoint for
* the keyframe
*/
- float xco = x + w / 2 + 0.5f;
- float yco = y + h / 2 + 0.5f;
+ const float xco = x + w / 2 + 0.5f;
+ const float yco = y + h / 2 + 0.5f;
GPUVertFormat *format = immVertexFormat();
- uint pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- uint size_id = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
+ const uint pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ const uint size_id = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
uint color_id = GPU_vertformat_attr_add(
format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
uint outline_color_id = GPU_vertformat_attr_add(
format, "outlineColor", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
- uint flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT);
+ const uint flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT);
GPU_program_point_size(true);
immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND);
@@ -323,7 +323,7 @@ static void vicon_keytype_draw_wrapper(
* - size: (default icon size == 16, default dopesheet icon size == 10)
* - sel: true unless in handletype icons (so that "keyframe" state shows the iconic yellow icon)
*/
- bool sel = (handle_type == KEYFRAME_HANDLE_NONE);
+ const bool sel = (handle_type == KEYFRAME_HANDLE_NONE);
draw_keyframe_shape(xco,
yco,
@@ -490,7 +490,7 @@ static void init_brush_icons(void)
# define INIT_BRUSH_ICON(icon_id, name) \
{ \
uchar *rect = (uchar *)datatoc_##name##_png; \
- int size = datatoc_##name##_png_size; \
+ const int size = datatoc_##name##_png_size; \
DrawInfo *di; \
\
di = def_internal_icon(NULL, icon_id, 0, 0, w, ICON_TYPE_BUFFER, 0); \
@@ -732,7 +732,7 @@ static ImBuf *create_mono_icon_with_border(ImBuf *buf,
for (int y = 0; y < ICON_GRID_ROWS; y++) {
for (int x = 0; x < ICON_GRID_COLS; x++) {
- IconType icontype = icontypes[y * ICON_GRID_COLS + x];
+ const IconType icontype = icontypes[y * ICON_GRID_COLS + x];
if (icontype.type != ICON_TYPE_MONO_TEXTURE) {
continue;
}
@@ -743,7 +743,7 @@ static ImBuf *create_mono_icon_with_border(ImBuf *buf,
sy = sy / resolution_divider;
/* blur the alpha channel and store it in blurred_alpha_buffer */
- int blur_size = 2 / resolution_divider;
+ const int blur_size = 2 / resolution_divider;
for (int bx = 0; bx < icon_width; bx++) {
const int asx = MAX2(bx - blur_size, 0);
const int aex = MIN2(bx + blur_size + 1, icon_width);
@@ -758,7 +758,7 @@ static ImBuf *create_mono_icon_with_border(ImBuf *buf,
for (int ax = asx; ax < aex; ax++) {
for (int ay = asy; ay < aey; ay++) {
const int offset_read = (sy + ay) * buf->x + (sx + ax);
- uint color_read = buf->rect[offset_read];
+ const uint color_read = buf->rect[offset_read];
const float alpha_read = ((color_read & 0xff000000) >> 24) / 255.0;
alpha_accum += alpha_read;
alpha_samples += 1;
@@ -790,8 +790,8 @@ static ImBuf *create_mono_icon_with_border(ImBuf *buf,
blend_color_interpolate_float(dest_rgba, orig_rgba, border_rgba, 1.0 - orig_rgba[3]);
linearrgb_to_srgb_v4(dest_srgb, dest_rgba);
- uint alpha_mask = ((uint)(dest_srgb[3] * 255)) << 24;
- uint cpack = rgb_to_cpack(dest_srgb[0], dest_srgb[1], dest_srgb[2]) | alpha_mask;
+ const uint alpha_mask = ((uint)(dest_srgb[3] * 255)) << 24;
+ const uint cpack = rgb_to_cpack(dest_srgb[0], dest_srgb[1], dest_srgb[2]) | alpha_mask;
result->rect[offset_write] = cpack;
}
}
@@ -820,7 +820,7 @@ void UI_icons_reload_internal_textures(void)
bTheme *btheme = UI_GetTheme();
ImBuf *b16buf = NULL, *b32buf = NULL, *b16buf_border = NULL, *b32buf_border = NULL;
const float icon_border_intensity = btheme->tui.icon_border_intensity;
- bool need_icons_with_border = icon_border_intensity > 0.0f;
+ const bool need_icons_with_border = icon_border_intensity > 0.0f;
if (b16buf == NULL) {
b16buf = IMB_ibImageFromMemory((const uchar *)datatoc_blender_icons16_png,
@@ -936,7 +936,7 @@ static void init_internal_icons(void)
for (y = 0; y < ICON_GRID_ROWS; y++) {
/* Row W has monochrome icons. */
for (x = 0; x < ICON_GRID_COLS; x++) {
- IconType icontype = icontypes[y * ICON_GRID_COLS + x];
+ const IconType icontype = icontypes[y * ICON_GRID_COLS + x];
if (!ELEM(icontype.type, ICON_TYPE_COLOR_TEXTURE, ICON_TYPE_MONO_TEXTURE)) {
continue;
}
@@ -1130,7 +1130,7 @@ void UI_icons_free_drawinfo(void *drawinfo)
*/
static DrawInfo *icon_create_drawinfo(Icon *icon)
{
- int icon_data_type = icon->obj_type;
+ const int icon_data_type = icon->obj_type;
DrawInfo *di = NULL;
di = MEM_callocN(sizeof(DrawInfo), "di_icon");
@@ -1246,7 +1246,7 @@ int UI_preview_render_size(enum eIconSizes size)
*/
static void icon_create_rect(struct PreviewImage *prv_img, enum eIconSizes size)
{
- uint render_size = UI_preview_render_size(size);
+ const uint render_size = UI_preview_render_size(size);
if (!prv_img) {
if (G.debug & G_DEBUG) {
@@ -1351,7 +1351,7 @@ void ui_icon_ensure_deferred(const bContext *C, const int icon_id, const bool bi
img->w = STUDIOLIGHT_ICON_SIZE;
img->h = STUDIOLIGHT_ICON_SIZE;
- size_t size = STUDIOLIGHT_ICON_SIZE * STUDIOLIGHT_ICON_SIZE * sizeof(uint);
+ const size_t size = STUDIOLIGHT_ICON_SIZE * STUDIOLIGHT_ICON_SIZE * sizeof(uint);
img->rect = MEM_mallocN(size, __func__);
memset(img->rect, 0, size);
di->data.buffer.image = img;
@@ -1479,7 +1479,7 @@ static void icon_draw_rect(float x,
return;
}
/* modulate color */
- float col[4] = {alpha, alpha, alpha, alpha};
+ const float col[4] = {alpha, alpha, alpha, alpha};
/* rect contains image in 'rendersize', we only scale if needed */
if (rw != w || rh != h) {
@@ -1565,8 +1565,8 @@ static void icon_draw_cache_texture_flush_ex(GPUTexture *texture,
GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_2D_IMAGE_MULTI_RECT_COLOR);
GPU_shader_bind(shader);
- int img_binding = GPU_shader_get_texture_binding(shader, "image");
- int data_loc = GPU_shader_get_uniform(shader, "calls_data");
+ const int img_binding = GPU_shader_get_texture_binding(shader, "image");
+ const int data_loc = GPU_shader_get_uniform(shader, "calls_data");
GPU_texture_bind(texture, img_binding);
GPU_sampler_icon_bind(img_binding);
@@ -1703,10 +1703,10 @@ static void icon_draw_texture(float x,
GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_2D_IMAGE_RECT_COLOR);
GPU_shader_bind(shader);
- int img_binding = GPU_shader_get_texture_binding(shader, "image");
- int color_loc = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_COLOR);
- int rect_tex_loc = GPU_shader_get_uniform(shader, "rect_icon");
- int rect_geom_loc = GPU_shader_get_uniform(shader, "rect_geom");
+ const int img_binding = GPU_shader_get_texture_binding(shader, "image");
+ const int color_loc = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_COLOR);
+ const int rect_tex_loc = GPU_shader_get_uniform(shader, "rect_icon");
+ const int rect_geom_loc = GPU_shader_get_uniform(shader, "rect_geom");
if (rgb) {
GPU_shader_uniform_vector(shader, color_loc, 4, 1, (float[4]){UNPACK3(rgb), alpha});
@@ -1838,7 +1838,7 @@ static void icon_draw_size(float x,
}
else if (di->type == ICON_TYPE_MONO_TEXTURE) {
/* Monochrome icon that uses text or theme color. */
- bool with_border = mono_border && (btheme->tui.icon_border_intensity > 0.0f);
+ const bool with_border = mono_border && (btheme->tui.icon_border_intensity > 0.0f);
float color[4];
if (mono_rgba) {
rgba_uchar_to_float(color, (const uchar *)mono_rgba);
@@ -2222,7 +2222,7 @@ int UI_rnaptr_icon_get(bContext *C, PointerRNA *ptr, int rnaicon, const bool big
/* get icon from ID */
if (id) {
- int icon = ui_id_icon_get(C, id, big);
+ const int icon = ui_id_icon_get(C, id, big);
return icon ? icon : rnaicon;
}
@@ -2334,7 +2334,7 @@ void UI_icon_draw_ex(float x,
const uchar mono_color[4],
const bool mono_border)
{
- int draw_size = get_draw_size(ICON_SIZE_ICON);
+ const int draw_size = get_draw_size(ICON_SIZE_ICON);
icon_draw_size(x,
y,
icon_id,
diff --git a/source/blender/editors/interface/interface_icons_event.c b/source/blender/editors/interface/interface_icons_event.c
index 4be2dbc0b4e..223fcbfd45b 100644
--- a/source/blender/editors/interface/interface_icons_event.c
+++ b/source/blender/editors/interface/interface_icons_event.c
@@ -85,8 +85,8 @@ static void icon_draw_rect_input_text(const rctf *rect,
BLF_size(font_id, font_size * U.pixelsize, U.dpi);
float width, height;
BLF_width_and_height(font_id, str, BLF_DRAW_STR_DUMMY_MAX, &width, &height);
- float x = rect->xmin + (((rect->xmax - rect->xmin) - width) / 2.0f);
- float y = rect->ymin + (((rect->ymax - rect->ymin) - height) / 2.0f);
+ const float x = rect->xmin + (((rect->xmax - rect->xmin) - width) / 2.0f);
+ const float y = rect->ymin + (((rect->ymax - rect->ymin) - height) / 2.0f);
BLF_position(font_id, x, y, 0.0f);
BLF_draw(font_id, str, BLF_DRAW_STR_DUMMY_MAX);
BLF_batch_draw_flush();
@@ -98,8 +98,8 @@ static void icon_draw_rect_input_symbol(const rctf *rect, const float color[4],
const int font_id = blf_mono_font;
BLF_color4fv(font_id, color);
BLF_size(font_id, 19 * U.pixelsize, U.dpi);
- float x = rect->xmin + (2.0f * U.pixelsize);
- float y = rect->ymin + (1.0f * U.pixelsize);
+ const float x = rect->xmin + (2.0f * U.pixelsize);
+ const float y = rect->ymin + (1.0f * U.pixelsize);
BLF_position(font_id, x, y, 0.0f);
BLF_draw(font_id, str, BLF_DRAW_STR_DUMMY_MAX);
BLF_batch_draw_flush();
diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h
index eb1bd1ba42e..87be3745f87 100644
--- a/source/blender/editors/interface/interface_intern.h
+++ b/source/blender/editors/interface/interface_intern.h
@@ -794,8 +794,8 @@ extern int ui_handler_panel_region(struct bContext *C,
const struct wmEvent *event,
struct ARegion *region,
const uiBut *active_but);
-extern void ui_draw_aligned_panel(struct uiStyle *style,
- uiBlock *block,
+extern void ui_draw_aligned_panel(const struct uiStyle *style,
+ const uiBlock *block,
const rcti *rect,
const bool show_pin,
const bool show_background);
diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c
index ad76466b67c..8184962a54b 100644
--- a/source/blender/editors/interface/interface_layout.c
+++ b/source/blender/editors/interface/interface_layout.c
@@ -225,7 +225,7 @@ typedef struct uiLayoutItemRoot {
static const char *ui_item_name_add_colon(const char *name, char namestr[UI_MAX_NAME_STR])
{
- int len = strlen(name);
+ const int len = strlen(name);
if (len != 0 && len + 1 < UI_MAX_NAME_STR) {
memcpy(namestr, name, len);
@@ -251,7 +251,7 @@ static int ui_item_fit(
return available - pos;
}
- float width = *extra_pixel + (item * available) / (float)all;
+ const float width = *extra_pixel + (item * available) / (float)all;
*extra_pixel = width - (int)width;
return (int)width;
}
@@ -262,7 +262,7 @@ static int ui_item_fit(
return available - pos;
}
- float width = *extra_pixel + (item * available) / (float)all;
+ const float width = *extra_pixel + (item * available) / (float)all;
*extra_pixel = width - (int)width;
return (int)width;
}
@@ -457,8 +457,8 @@ static void ui_layer_but_cb(bContext *C, void *arg_but, void *arg_index)
PointerRNA *ptr = &but->rnapoin;
PropertyRNA *prop = but->rnaprop;
int i, index = POINTER_AS_INT(arg_index);
- int shift = win->eventstate->shift;
- int len = RNA_property_array_length(ptr, prop);
+ const int shift = win->eventstate->shift;
+ const int len = RNA_property_array_length(ptr, prop);
if (!shift) {
RNA_property_boolean_set_index(ptr, prop, index, true);
@@ -519,7 +519,7 @@ static void ui_item_array(uiLayout *layout,
if (type == PROP_BOOLEAN && ELEM(subtype, PROP_LAYER, PROP_LAYER_MEMBER)) {
/* special check for layer layout */
int butw, buth, unit;
- int cols = (len >= 20) ? 2 : 1;
+ const int cols = (len >= 20) ? 2 : 1;
const uint colbuts = len / (2 * cols);
uint layer_used = 0;
uint layer_active = 0;
@@ -721,7 +721,7 @@ static void ui_item_enum_expand_handle(bContext *C, void *arg1, void *arg2)
if (!win->eventstate->shift) {
uiBut *but = (uiBut *)arg1;
- int enum_value = POINTER_AS_INT(arg2);
+ const int enum_value = POINTER_AS_INT(arg2);
int current_value = RNA_property_enum_get(&but->rnapoin, but->rnaprop);
if (!(current_value & enum_value)) {
@@ -814,7 +814,7 @@ static void ui_item_enum_expand_exec(uiLayout *layout,
BLI_assert(RNA_property_type(prop) == PROP_ENUM);
uiLayout *layout_radial = NULL;
- bool radial = (layout->root->type == UI_LAYOUT_PIEMENU);
+ const bool radial = (layout->root->type == UI_LAYOUT_PIEMENU);
if (radial) {
RNA_property_enum_items_gettexted_all(block->evil_C, ptr, prop, &item_array, NULL, &free);
}
@@ -1179,7 +1179,7 @@ static uiBut *uiItemFullO_ptr_ex(uiLayout *layout,
w = ui_text_icon_width(layout, name, icon, 0);
- int prev_emboss = layout->emboss;
+ const int prev_emboss = layout->emboss;
if (flag & UI_ITEM_R_NO_BG) {
layout->emboss = UI_EMBOSS_NONE;
}
@@ -1223,7 +1223,7 @@ static uiBut *uiItemFullO_ptr_ex(uiLayout *layout,
opptr->data = properties;
}
else {
- IDPropertyTemplate val = {0};
+ const IDPropertyTemplate val = {0};
opptr->data = IDP_New(IDP_GROUP, &val, "wmOperatorProperties");
}
if (r_opptr) {
@@ -2043,7 +2043,7 @@ void uiItemFullR(uiLayout *layout,
if ((layout->root->type == UI_LAYOUT_MENU) ||
/* Use checkboxes only as a fallback in pie-menu's, when no icon is defined. */
((layout->root->type == UI_LAYOUT_PIEMENU) && (icon == ICON_NONE))) {
- int prop_flag = RNA_property_flag(prop);
+ const int prop_flag = RNA_property_flag(prop);
if (type == PROP_BOOLEAN) {
if ((is_array == false) || (index != RNA_NO_INDEX)) {
if (prop_flag & PROP_ICONS_CONSECUTIVE) {
@@ -2060,7 +2060,7 @@ void uiItemFullR(uiLayout *layout,
}
else if (type == PROP_ENUM) {
if (index == RNA_ENUM_VALUE) {
- int enum_value = RNA_property_enum_get(ptr, prop);
+ const int enum_value = RNA_property_enum_get(ptr, prop);
if (prop_flag & PROP_ICONS_CONSECUTIVE) {
icon = ICON_CHECKBOX_DEHLT; /* but->iconadd will set to correct icon */
}
@@ -2099,7 +2099,7 @@ void uiItemFullR(uiLayout *layout,
int w, h;
ui_item_rna_size(layout, name, icon, ptr, prop, index, icon_only, compact, &w, &h);
- int prev_emboss = layout->emboss;
+ const int prev_emboss = layout->emboss;
if (no_bg) {
layout->emboss = UI_EMBOSS_NONE;
}
@@ -3193,7 +3193,7 @@ uiLayout *uiItemL_respect_property_split(uiLayout *layout, const char *text, int
{
if (layout->item.flag & UI_ITEM_PROP_SEP) {
uiBlock *block = uiLayoutGetBlock(layout);
- uiPropertySplitWrapper split_wrapper = uiItemPropertySplitWrapperCreate(layout);
+ const uiPropertySplitWrapper split_wrapper = uiItemPropertySplitWrapperCreate(layout);
/* Further items added to 'layout' will automatically be added to split_wrapper.property_row */
uiItemL_(split_wrapper.label_column, text, icon);
@@ -3272,7 +3272,7 @@ void uiItemV(uiLayout *layout, const char *name, int icon, int argval)
void uiItemS_ex(uiLayout *layout, float factor)
{
uiBlock *block = layout->root->block;
- bool is_menu = ui_block_is_menu(block);
+ const bool is_menu = ui_block_is_menu(block);
if (is_menu && !UI_block_can_add_separator(block)) {
return;
}
@@ -3794,7 +3794,7 @@ static void ui_litem_layout_radial(uiLayout *litem)
* also the old code at http://developer.blender.org/T5103
*/
- int pie_radius = U.pie_menu_radius * UI_DPI_FAC;
+ const int pie_radius = U.pie_menu_radius * UI_DPI_FAC;
x = litem->x;
y = litem->y;
diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c
index e47761236ca..82e6e01056c 100644
--- a/source/blender/editors/interface/interface_ops.c
+++ b/source/blender/editors/interface/interface_ops.c
@@ -201,7 +201,7 @@ static int copy_as_driver_button_exec(bContext *C, wmOperator *op)
if (ptr.owner_id && ptr.data && prop) {
ID *id;
- int dim = RNA_property_array_dimension(&ptr, prop, NULL);
+ const int dim = RNA_property_array_dimension(&ptr, prop, NULL);
char *path = RNA_path_from_real_ID_to_property_index(bmain, &ptr, prop, dim, index, &id);
if (path) {
@@ -378,7 +378,7 @@ static bool assign_default_button_poll(bContext *C)
UI_context_active_but_prop_get(C, &ptr, &prop, &index);
if (ptr.data && prop && RNA_property_editable(&ptr, prop)) {
- PropertyType type = RNA_property_type(prop);
+ const PropertyType type = RNA_property_type(prop);
return RNA_property_is_idprop(prop) && !RNA_property_array_check(prop) &&
ELEM(type, PROP_INT, PROP_FLOAT);
@@ -1174,7 +1174,7 @@ bool ui_jump_to_target_button_poll(bContext *C)
static int jump_to_target_button_exec(bContext *C, wmOperator *UNUSED(op))
{
- bool success = jump_to_target_button(C, false);
+ const bool success = jump_to_target_button(C, false);
return (success) ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
}
@@ -1474,16 +1474,16 @@ static int edittranslation_exec(bContext *C, wmOperator *op)
const char *root = U.i18ndir;
const char *uilng = BLT_lang_get();
- uiStringInfo but_label = {BUT_GET_LABEL, NULL};
- uiStringInfo rna_label = {BUT_GET_RNA_LABEL, NULL};
- uiStringInfo enum_label = {BUT_GET_RNAENUM_LABEL, NULL};
- uiStringInfo but_tip = {BUT_GET_TIP, NULL};
- uiStringInfo rna_tip = {BUT_GET_RNA_TIP, NULL};
- uiStringInfo enum_tip = {BUT_GET_RNAENUM_TIP, NULL};
- uiStringInfo rna_struct = {BUT_GET_RNASTRUCT_IDENTIFIER, NULL};
- uiStringInfo rna_prop = {BUT_GET_RNAPROP_IDENTIFIER, NULL};
- uiStringInfo rna_enum = {BUT_GET_RNAENUM_IDENTIFIER, NULL};
- uiStringInfo rna_ctxt = {BUT_GET_RNA_LABEL_CONTEXT, NULL};
+ const uiStringInfo but_label = {BUT_GET_LABEL, NULL};
+ const uiStringInfo rna_label = {BUT_GET_RNA_LABEL, NULL};
+ const uiStringInfo enum_label = {BUT_GET_RNAENUM_LABEL, NULL};
+ const uiStringInfo but_tip = {BUT_GET_TIP, NULL};
+ const uiStringInfo rna_tip = {BUT_GET_RNA_TIP, NULL};
+ const uiStringInfo enum_tip = {BUT_GET_RNAENUM_TIP, NULL};
+ const uiStringInfo rna_struct = {BUT_GET_RNASTRUCT_IDENTIFIER, NULL};
+ const uiStringInfo rna_prop = {BUT_GET_RNAPROP_IDENTIFIER, NULL};
+ const uiStringInfo rna_enum = {BUT_GET_RNAENUM_IDENTIFIER, NULL};
+ const uiStringInfo rna_ctxt = {BUT_GET_RNA_LABEL_CONTEXT, NULL};
if (!BLI_is_dir(root)) {
BKE_report(op->reports,
diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c
index a70bcd208ab..ade77e96bf9 100644
--- a/source/blender/editors/interface/interface_panel.c
+++ b/source/blender/editors/interface/interface_panel.c
@@ -64,7 +64,9 @@
#include "interface_intern.h"
-/*********************** defines and structs ************************/
+/* -------------------------------------------------------------------- */
+/** \name Defines & Structs
+ * \{ */
#define ANIMATION_TIME 0.30
#define ANIMATION_INTERVAL 0.02
@@ -76,15 +78,11 @@
#define PNL_NEW_ADDED 16
#define PNL_FIRST 32
-/* only show pin header button for pinned panels */
-#define USE_PIN_HIDDEN
-
/* the state of the mouse position relative to the panel */
typedef enum uiPanelMouseState {
- PANEL_MOUSE_OUTSIDE, /* mouse is not in the panel */
- PANEL_MOUSE_INSIDE_CONTENT, /* mouse is in the actual panel content */
- PANEL_MOUSE_INSIDE_HEADER, /* mouse is in the panel header */
- PANEL_MOUSE_INSIDE_SCALE, /* mouse is inside panel scale widget */
+ PANEL_MOUSE_OUTSIDE, /** Mouse is not in the panel. */
+ PANEL_MOUSE_INSIDE_CONTENT, /** Mouse is in the actual panel content. */
+ PANEL_MOUSE_INSIDE_HEADER, /** Mouse is in the panel header. */
} uiPanelMouseState;
typedef enum uiHandlePanelState {
@@ -121,6 +119,12 @@ static bool panel_type_context_poll(ARegion *region,
const PanelType *panel_type,
const char *context);
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Local Functions
+ * \{ */
+
static void panel_title_color_get(bool show_background, uchar color[4])
{
if (show_background) {
@@ -134,44 +138,9 @@ static void panel_title_color_get(bool show_background, uchar color[4])
}
}
-/*********************** space specific code ************************/
-/* temporary code to remove all sbuts stuff from panel code */
-
-/* SpaceProperties.align */
-typedef enum eSpaceButtons_Align {
- BUT_HORIZONTAL = 0,
- BUT_VERTICAL = 1,
- BUT_AUTO = 2,
-} eSpaceButtons_Align;
-
-static int panel_aligned(const ScrArea *area, const ARegion *region)
-{
- if (area->spacetype == SPACE_PROPERTIES && region->regiontype == RGN_TYPE_WINDOW) {
- return BUT_VERTICAL;
- }
- if (area->spacetype == SPACE_USERPREF && region->regiontype == RGN_TYPE_WINDOW) {
- return BUT_VERTICAL;
- }
- if (area->spacetype == SPACE_FILE && region->regiontype == RGN_TYPE_CHANNELS) {
- return BUT_VERTICAL;
- }
- if (area->spacetype == SPACE_IMAGE && region->regiontype == RGN_TYPE_PREVIEW) {
- return BUT_VERTICAL;
- }
- if (ELEM(region->regiontype,
- RGN_TYPE_UI,
- RGN_TYPE_TOOLS,
- RGN_TYPE_TOOL_PROPS,
- RGN_TYPE_HUD,
- RGN_TYPE_NAV_BAR,
- RGN_TYPE_EXECUTE)) {
- return BUT_VERTICAL;
- }
-
- return 0;
-}
-
-static bool panel_active_animation_changed(ListBase *lb, Panel **pa_animation, bool *no_animation)
+static bool panel_active_animation_changed(ListBase *lb,
+ Panel **r_panel_animation,
+ bool *r_no_animation)
{
LISTBASE_FOREACH (Panel *, panel, lb) {
/* Detect panel active flag changes. */
@@ -185,7 +154,7 @@ static bool panel_active_animation_changed(ListBase *lb, Panel **pa_animation, b
}
if ((panel->runtime_flag & PNL_ACTIVE) && !(panel->flag & PNL_CLOSED)) {
- if (panel_active_animation_changed(&panel->children, pa_animation, no_animation)) {
+ if (panel_active_animation_changed(&panel->children, r_panel_animation, r_no_animation)) {
return true;
}
}
@@ -194,15 +163,15 @@ static bool panel_active_animation_changed(ListBase *lb, Panel **pa_animation, b
if (panel->activedata) {
uiHandlePanelData *data = panel->activedata;
if (data->state == PANEL_STATE_ANIMATION) {
- *pa_animation = panel;
+ *r_panel_animation = panel;
}
else {
/* Don't animate while handling other interaction. */
- *no_animation = true;
+ *r_no_animation = true;
}
}
- if ((panel->runtime_flag & PNL_ANIM_ALIGN) && !(*pa_animation)) {
- *pa_animation = panel;
+ if ((panel->runtime_flag & PNL_ANIM_ALIGN) && !(*r_panel_animation)) {
+ *r_panel_animation = panel;
}
}
@@ -245,10 +214,13 @@ static bool panels_need_realign(ScrArea *area, ARegion *region, Panel **r_panel_
return false;
}
-/********* Functions for instanced panels. ***********/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Functions for Instanced Panels
+ * \{ */
-static Panel *UI_panel_add_instanced_ex(ScrArea *area,
- ARegion *region,
+static Panel *UI_panel_add_instanced_ex(ARegion *region,
ListBase *panels,
PanelType *panel_type,
int list_index,
@@ -265,7 +237,7 @@ static Panel *UI_panel_add_instanced_ex(ScrArea *area,
* function to create them, as UI_panel_begin does other things we don't need to do. */
LISTBASE_FOREACH (LinkData *, child, &panel_type->children) {
PanelType *child_type = child->data;
- UI_panel_add_instanced_ex(area, region, &panel->children, child_type, list_index, custom_data);
+ UI_panel_add_instanced_ex(region, &panel->children, child_type, list_index, custom_data);
}
/* Make sure the panel is added to the end of the display-order as well. This is needed for
@@ -288,14 +260,10 @@ static Panel *UI_panel_add_instanced_ex(ScrArea *area,
/**
* Called in situations where panels need to be added dynamically rather than having only one panel
- * corresponding to each PanelType.
+ * corresponding to each #PanelType.
*/
-Panel *UI_panel_add_instanced(ScrArea *area,
- ARegion *region,
- ListBase *panels,
- char *panel_idname,
- int list_index,
- PointerRNA *custom_data)
+Panel *UI_panel_add_instanced(
+ ARegion *region, ListBase *panels, char *panel_idname, int list_index, PointerRNA *custom_data)
{
ARegionType *region_type = region->type;
@@ -307,12 +275,12 @@ Panel *UI_panel_add_instanced(ScrArea *area,
return NULL;
}
- return UI_panel_add_instanced_ex(area, region, panels, panel_type, list_index, custom_data);
+ return UI_panel_add_instanced_ex(region, panels, panel_type, list_index, custom_data);
}
/**
- * Find a unique key to append to the idname for the lookup to the panel's #uiBlock. Needed for
- * instanced panels, where there can be multiple with the same type and idname.
+ * Find a unique key to append to the #PanelTyype.idname for the lookup to the panel's #uiBlock.
+ * Needed for instanced panels, where there can be multiple with the same type and identifier.
*/
void UI_list_panel_unique_str(Panel *panel, char *r_name)
{
@@ -399,7 +367,7 @@ void UI_panels_free_instanced(const bContext *C, ARegion *region)
* don't match in any way.
*
* \param data: The list of data to check against the instanced panels.
- * \param panel_idname_func: Function to find the panel type idname for each item in the data list.
+ * \param panel_idname_func: Function to find the #PanelType.idname for each item in the data list.
* For a readability and generality, this lookup happens separately for each type of panel list.
*/
bool UI_panel_list_matches_data(ARegion *region,
@@ -529,14 +497,10 @@ static void reorder_instanced_panel_list(bContext *C, ARegion *region, Panel *dr
*/
static bool panel_set_expand_from_list_data_recursive(Panel *panel, short flag, short *flag_index)
{
- bool open = (flag & (1 << *flag_index));
- bool changed = (open == (bool)(panel->flag & PNL_CLOSEDY));
- if (open) {
- panel->flag &= ~PNL_CLOSEDY;
- }
- else {
- panel->flag |= PNL_CLOSEDY;
- }
+ const bool open = (flag & (1 << *flag_index));
+ bool changed = (open == (bool)(panel->flag & PNL_CLOSED));
+ SET_FLAG_FROM_TEST(panel->flag, !open, PNL_CLOSED);
+
LISTBASE_FOREACH (Panel *, child, &panel->children) {
*flag_index = *flag_index + 1;
changed |= panel_set_expand_from_list_data_recursive(child, flag, flag_index);
@@ -558,7 +522,7 @@ void UI_panel_set_expand_from_list_data(const bContext *C, Panel *panel)
return;
}
- short expand_flag = panel->type->get_list_data_expand_flag(C, panel);
+ const short expand_flag = panel->type->get_list_data_expand_flag(C, panel);
short flag_index = 0;
/* Start panel animation if the open state was changed. */
@@ -572,13 +536,9 @@ void UI_panel_set_expand_from_list_data(const bContext *C, Panel *panel)
*/
static void get_panel_expand_flag(Panel *panel, short *flag, short *flag_index)
{
- bool open = !(panel->flag & PNL_CLOSEDY);
- if (open) {
- *flag |= (1 << *flag_index);
- }
- else {
- *flag &= ~(1 << *flag_index);
- }
+ const bool open = !(panel->flag & PNL_CLOSED);
+ SET_FLAG_FROM_TEST(*flag, open, (1 << *flag_index));
+
LISTBASE_FOREACH (Panel *, child, &panel->children) {
*flag_index = *flag_index + 1;
get_panel_expand_flag(child, flag, flag_index);
@@ -586,14 +546,14 @@ static void get_panel_expand_flag(Panel *panel, short *flag, short *flag_index)
}
/**
- * Call the callback to store the panel and subpanel expansion settings in the list item that
+ * Call the callback to store the panel and sub-panel expansion settings in the list item that
* corresponds to this panel.
*
* \note This needs to iterate through all of the regions panels because the panel with changed
- * expansion could have been the subpanel of a instanced panel, meaning it might not know
+ * expansion could have been the sub-panel of a instanced panel, meaning it might not know
* which list item it corresponds to.
*/
-static void set_panels_list_data_expand_flag(const bContext *C, ARegion *region)
+static void set_panels_list_data_expand_flag(const bContext *C, const ARegion *region)
{
LISTBASE_FOREACH (Panel *, panel, &region->panels) {
PanelType *panel_type = panel->type;
@@ -613,7 +573,11 @@ static void set_panels_list_data_expand_flag(const bContext *C, ARegion *region)
}
}
-/****************************** panels ******************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Panels
+ * \{ */
/**
* Set flag state for a panel and its sub-panels.
@@ -622,7 +586,7 @@ static void set_panels_list_data_expand_flag(const bContext *C, ARegion *region)
*/
static bool panel_set_flag_recursive(Panel *panel, int flag, bool value)
{
- short flag_original = panel->flag;
+ const short flag_original = panel->flag;
SET_FLAG_FROM_TEST(panel->flag, value, flag);
@@ -635,14 +599,10 @@ static bool panel_set_flag_recursive(Panel *panel, int flag, bool value)
return changed;
}
-static void panels_collapse_all(const bContext *C,
- ScrArea *area,
- ARegion *region,
- const Panel *from_panel)
+static void panels_collapse_all(ARegion *region, const Panel *from_panel)
{
const bool has_category_tabs = UI_panel_category_is_visible(region);
const char *category = has_category_tabs ? UI_panel_category_active_get(region, false) : NULL;
- const int flag = ((panel_aligned(area, region) == BUT_HORIZONTAL) ? PNL_CLOSEDX : PNL_CLOSEDY);
const PanelType *from_pt = from_panel->type;
LISTBASE_FOREACH (Panel *, panel, &region->panels) {
@@ -653,13 +613,11 @@ static void panels_collapse_all(const bContext *C,
if (!pt->context[0] || !from_pt->context[0] || STREQ(pt->context, from_pt->context)) {
if ((panel->flag & PNL_PIN) || !category || !pt->category[0] ||
STREQ(pt->category, category)) {
- panel->flag &= ~PNL_CLOSED;
- panel->flag |= flag;
+ panel->flag |= PNL_CLOSED;
}
}
}
}
- set_panels_list_data_expand_flag(C, region);
}
static bool panel_type_context_poll(ARegion *region,
@@ -692,19 +650,13 @@ Panel *UI_panel_find_by_type(ListBase *lb, PanelType *pt)
/**
* \note \a panel should be return value from #UI_panel_find_by_type and can be NULL.
*/
-Panel *UI_panel_begin(ScrArea *area,
- ARegion *region,
- ListBase *lb,
- uiBlock *block,
- PanelType *pt,
- Panel *panel,
- bool *r_open)
+Panel *UI_panel_begin(
+ ARegion *region, ListBase *lb, uiBlock *block, PanelType *pt, Panel *panel, bool *r_open)
{
Panel *panel_last;
const char *drawname = CTX_IFACE_(pt->translation_context, pt->label);
const char *idname = pt->idname;
const bool newpanel = (panel == NULL);
- int align = panel_aligned(area, region);
if (!newpanel) {
panel->type = pt;
@@ -716,12 +668,7 @@ Panel *UI_panel_begin(ScrArea *area,
BLI_strncpy(panel->panelname, idname, sizeof(panel->panelname));
if (pt->flag & PNL_DEFAULT_CLOSED) {
- if (align == BUT_VERTICAL) {
- panel->flag |= PNL_CLOSEDY;
- }
- else {
- panel->flag |= PNL_CLOSEDX;
- }
+ panel->flag |= PNL_CLOSED;
}
panel->ofsx = 0;
@@ -790,11 +737,10 @@ Panel *UI_panel_begin(ScrArea *area,
return panel;
}
-static float panel_region_offset_x_get(const ARegion *region, int align)
+static float panel_region_offset_x_get(const ARegion *region)
{
if (UI_panel_category_is_visible(region)) {
- if (align == BUT_VERTICAL &&
- (RGN_ALIGN_ENUM_FROM_MASK(region->alignment) != RGN_ALIGN_RIGHT)) {
+ if (RGN_ALIGN_ENUM_FROM_MASK(region->alignment) != RGN_ALIGN_RIGHT) {
return UI_PANEL_CATEGORY_MARGIN_WIDTH;
}
}
@@ -802,8 +748,7 @@ static float panel_region_offset_x_get(const ARegion *region, int align)
return 0;
}
-void UI_panel_end(
- const ScrArea *area, const ARegion *region, uiBlock *block, int width, int height, bool open)
+void UI_panel_end(const ARegion *region, uiBlock *block, int width, int height, bool open)
{
Panel *panel = block->panel;
@@ -826,8 +771,8 @@ void UI_panel_end(
panel->sizey = height;
}
else {
- int old_sizex = panel->sizex, old_sizey = panel->sizey;
- int old_region_ofsx = panel->runtime.region_ofsx;
+ const int old_sizex = panel->sizex, old_sizey = panel->sizey;
+ const int old_region_ofsx = panel->runtime.region_ofsx;
/* update width/height if non-zero */
if (width != 0) {
@@ -843,8 +788,7 @@ void UI_panel_end(
panel->ofsy += old_sizey - panel->sizey;
}
- int align = panel_aligned(area, region);
- panel->runtime.region_ofsx = panel_region_offset_x_get(region, align);
+ panel->runtime.region_ofsx = panel_region_offset_x_get(region);
if (old_region_ofsx != panel->runtime.region_ofsx) {
panel->runtime_flag |= PNL_ANIM_ALIGN;
}
@@ -858,7 +802,7 @@ static void ui_offset_panel_block(uiBlock *block)
/* compute bounds and offset */
ui_block_bounds_calc(block);
- int ofsy = block->panel->sizey - style->panelspace;
+ const int ofsy = block->panel->sizey - style->panelspace;
LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
but->rect.ymin += ofsy;
@@ -870,14 +814,18 @@ static void ui_offset_panel_block(uiBlock *block)
block->rect.xmin = block->rect.ymin = 0.0;
}
-/**************************** drawing *******************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Drawing
+ * \{ */
/* triangle 'icon' for panel header */
void UI_draw_icon_tri(float x, float y, char dir, const float color[4])
{
- float f3 = 0.05 * U.widget_unit;
- float f5 = 0.15 * U.widget_unit;
- float f7 = 0.25 * U.widget_unit;
+ const float f3 = 0.05 * U.widget_unit;
+ const float f5 = 0.15 * U.widget_unit;
+ const float f7 = 0.25 * U.widget_unit;
if (dir == 'h') {
UI_draw_anti_tria(x - f3, y - f5, x - f3, y + f5, x + f7, y, color);
@@ -906,57 +854,46 @@ void UI_panel_label_offset(uiBlock *block, int *r_x, int *r_y)
}
}
-static void ui_draw_aligned_panel_header(
- uiStyle *style, uiBlock *block, const rcti *rect, char dir, const bool show_background)
+static void ui_draw_aligned_panel_header(const uiStyle *style,
+ const uiBlock *block,
+ const rcti *rect,
+ const bool show_background)
{
- Panel *panel = block->panel;
- rcti hrect;
- int pnl_icons;
- const char *activename = panel->drawname;
+ const Panel *panel = block->panel;
const bool is_subpanel = (panel->type && panel->type->parent);
- uiFontStyle *fontstyle = (is_subpanel) ? &style->widgetlabel : &style->paneltitle;
- uchar col_title[4];
+ const uiFontStyle *fontstyle = (is_subpanel) ? &style->widgetlabel : &style->paneltitle;
/* + 0.001f to avoid flirting with float inaccuracy */
- pnl_icons = (panel->labelofs + (1.1f * PNL_ICON)) / block->aspect + 0.001f;
+ const int pnl_icons = (panel->labelofs + (1.1f * PNL_ICON)) / block->aspect + 0.001f;
/* draw text label */
+ uchar col_title[4];
panel_title_color_get(show_background, col_title);
col_title[3] = 255;
- hrect = *rect;
- if (dir == 'h') {
- hrect.xmin = rect->xmin + pnl_icons;
- hrect.ymin -= 2.0f / block->aspect;
- UI_fontstyle_draw(fontstyle,
- &hrect,
- activename,
- col_title,
- &(struct uiFontStyleDraw_Params){
- .align = UI_STYLE_TEXT_LEFT,
- });
- }
- else {
- /* ignore 'pnl_icons', otherwise the text gets offset horizontally
- * + 0.001f to avoid flirting with float inaccuracy
- */
- hrect.xmin = rect->xmin + (PNL_ICON + 5) / block->aspect + 0.001f;
- UI_fontstyle_draw_rotated(fontstyle, &hrect, activename, col_title);
- }
+ rcti hrect = *rect;
+ hrect.xmin = rect->xmin + pnl_icons;
+ hrect.ymin -= 2.0f / block->aspect;
+ UI_fontstyle_draw(fontstyle,
+ &hrect,
+ panel->drawname,
+ col_title,
+ &(struct uiFontStyleDraw_Params){
+ .align = UI_STYLE_TEXT_LEFT,
+ });
}
-/* panel integrated in buttonswindow, tool/property lists etc */
-void ui_draw_aligned_panel(uiStyle *style,
- uiBlock *block,
+/**
+ * Panel integrated in buttons-window, tool/property lists etc
+ */
+void ui_draw_aligned_panel(const uiStyle *style,
+ const uiBlock *block,
const rcti *rect,
const bool show_pin,
const bool show_background)
{
- Panel *panel = block->panel;
- rctf itemrect;
+ const Panel *panel = block->panel;
float color[4];
- const bool is_closed_x = (panel->flag & PNL_CLOSEDX) ? true : false;
- const bool is_closed_y = (panel->flag & PNL_CLOSEDY) ? true : false;
const bool is_subpanel = (panel->type && panel->type->parent);
const bool show_drag = (!is_subpanel &&
/* FIXME(campbell): currently no background means floating panel which
@@ -972,7 +909,8 @@ void ui_draw_aligned_panel(uiStyle *style,
box_wcol = &btheme->tui.wcol_box;
}
- uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ const uint pos = GPU_vertformat_attr_add(
+ immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
if (panel->type && (panel->type->flag & PNL_NO_HEADER)) {
if (show_background) {
@@ -984,7 +922,7 @@ void ui_draw_aligned_panel(uiStyle *style,
return;
}
- /* Calculate header rect with + 0.001f to prevent flicker due to float inaccuracy */
+ /* Calculate header rectangle with + 0.001f to prevent flicker due to float inaccuracy. */
rcti headrect = {
rect->xmin, rect->xmax, rect->ymax, rect->ymax + floor(PNL_HEADER / block->aspect + 0.001f)};
@@ -993,12 +931,12 @@ void ui_draw_aligned_panel(uiStyle *style,
/* Expand the top a tiny bit to give header buttons equal size above and below. */
rcti box_rect = {rect->xmin,
rect->xmax,
- (is_closed_x || is_closed_y) ? headrect.ymin : rect->ymin,
+ (panel->flag & PNL_CLOSED) ? headrect.ymin : rect->ymin,
headrect.ymax + U.pixelsize};
ui_draw_box_opaque(&box_rect, UI_CNR_ALL);
- /* Mimick the border between aligned box widgets for the bottom of the header. */
- if (!(is_closed_x || is_closed_y)) {
+ /* Mimic the border between aligned box widgets for the bottom of the header. */
+ if (!(panel->flag & PNL_CLOSED)) {
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
GPU_blend(GPU_BLEND_ALPHA);
@@ -1020,24 +958,23 @@ void ui_draw_aligned_panel(uiStyle *style,
/* Draw the header backdrop. */
if (show_background && !is_subpanel && !draw_box_style) {
- float minx = rect->xmin;
- float maxx = is_closed_x ? (minx + PNL_HEADER / block->aspect) : rect->xmax;
- float y = headrect.ymax;
+ const float minx = rect->xmin;
+ const float y = headrect.ymax;
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
GPU_blend(GPU_BLEND_ALPHA);
/* draw with background color */
immUniformThemeColor(TH_PANEL_HEADER);
- immRectf(pos, minx, headrect.ymin, maxx, y);
+ immRectf(pos, minx, headrect.ymin, rect->xmax, y);
immBegin(GPU_PRIM_LINES, 4);
immVertex2f(pos, minx, y);
- immVertex2f(pos, maxx, y);
+ immVertex2f(pos, rect->xmax, y);
immVertex2f(pos, minx, y);
- immVertex2f(pos, maxx, y);
+ immVertex2f(pos, rect->xmax, y);
immEnd();
@@ -1045,13 +982,8 @@ void ui_draw_aligned_panel(uiStyle *style,
immUnbindProgram();
}
-/* draw optional pin icon */
-#ifdef USE_PIN_HIDDEN
- if (show_pin && (block->panel->flag & PNL_PIN))
-#else
- if (show_pin)
-#endif
- {
+ /* draw optional pin icon */
+ if (show_pin && (block->panel->flag & PNL_PIN)) {
uchar col_title[4];
panel_title_color_get(show_background, col_title);
@@ -1072,45 +1004,35 @@ void ui_draw_aligned_panel(uiStyle *style,
if (is_subpanel) {
titlerect.xmin += (0.7f * UI_UNIT_X) / block->aspect + 0.001f;
}
- if (is_closed_x == false) {
- ui_draw_aligned_panel_header(style, block, &titlerect, 'h', show_background);
+ ui_draw_aligned_panel_header(style, block, &titlerect, show_background);
- if (show_drag) {
- /* itemrect smaller */
- const float scale = 0.7;
- itemrect.xmax = headrect.xmax - (0.2f * UI_UNIT_X);
- itemrect.xmin = itemrect.xmax - BLI_rcti_size_y(&headrect);
- itemrect.ymin = headrect.ymin;
- itemrect.ymax = headrect.ymax;
- BLI_rctf_scale(&itemrect, scale);
+ if (show_drag) {
+ /* Make `itemrect` smaller. */
+ const float scale = 0.7;
+ rctf itemrect;
+ itemrect.xmax = headrect.xmax - (0.2f * UI_UNIT_X);
+ itemrect.xmin = itemrect.xmax - BLI_rcti_size_y(&headrect);
+ itemrect.ymin = headrect.ymin;
+ itemrect.ymax = headrect.ymax;
+ BLI_rctf_scale(&itemrect, scale);
- GPU_matrix_push();
- GPU_matrix_translate_2f(itemrect.xmin, itemrect.ymin);
+ GPU_matrix_push();
+ GPU_matrix_translate_2f(itemrect.xmin, itemrect.ymin);
- const int col_tint = 84;
- float col_high[4], col_dark[4];
- UI_GetThemeColorShade4fv(TH_PANEL_HEADER, col_tint, col_high);
- UI_GetThemeColorShade4fv(TH_PANEL_BACK, -col_tint, col_dark);
+ const int col_tint = 84;
+ float col_high[4], col_dark[4];
+ UI_GetThemeColorShade4fv(TH_PANEL_HEADER, col_tint, col_high);
+ UI_GetThemeColorShade4fv(TH_PANEL_BACK, -col_tint, col_dark);
- GPUBatch *batch = GPU_batch_preset_panel_drag_widget(
- U.pixelsize, col_high, col_dark, BLI_rcti_size_y(&headrect) * scale);
- GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_FLAT_COLOR);
- GPU_batch_draw(batch);
- GPU_matrix_pop();
- }
+ GPUBatch *batch = GPU_batch_preset_panel_drag_widget(
+ U.pixelsize, col_high, col_dark, BLI_rcti_size_y(&headrect) * scale);
+ GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_FLAT_COLOR);
+ GPU_batch_draw(batch);
+ GPU_matrix_pop();
}
/* Draw panel backdrop. */
- if (is_closed_y) {
- /* skip */
- }
- else if (is_closed_x) {
- /* draw vertical title */
- ui_draw_aligned_panel_header(style, block, &headrect, 'v', show_background);
- pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- }
- /* an open panel */
- else {
+ if (!(panel->flag & PNL_CLOSED)) {
/* in some occasions, draw a border */
if (panel->flag & PNL_SELECT && !is_subpanel) {
float radius;
@@ -1168,28 +1090,21 @@ void ui_draw_aligned_panel(uiStyle *style,
immUnbindProgram();
}
- uchar col_title[4];
- panel_title_color_get(show_background, col_title);
-
/* draw collapse icon */
-
- /* itemrect smaller */
- itemrect.xmin = titlerect.xmin;
- itemrect.xmax = itemrect.xmin + BLI_rcti_size_y(&titlerect);
- itemrect.ymin = titlerect.ymin;
- itemrect.ymax = titlerect.ymax;
-
- BLI_rctf_scale(&itemrect, 0.25f);
-
{
+ rctf itemrect = {.xmin = titlerect.xmin,
+ .xmax = itemrect.xmin + BLI_rcti_size_y(&titlerect),
+ .ymin = titlerect.ymin,
+ .ymax = titlerect.ymax};
+ BLI_rctf_scale(&itemrect, 0.25f);
+
+ uchar col_title[4];
+ panel_title_color_get(show_background, col_title);
float tria_color[4];
rgb_uchar_to_float(tria_color, col_title);
tria_color[3] = 1.0f;
- if (is_closed_y) {
- ui_draw_anti_tria_rect(&itemrect, 'h', tria_color);
- }
- else if (is_closed_x) {
+ if (panel->flag & PNL_CLOSED) {
ui_draw_anti_tria_rect(&itemrect, 'h', tria_color);
}
else {
@@ -1198,17 +1113,434 @@ void ui_draw_aligned_panel(uiStyle *style,
}
}
-/************************** panel alignment *************************/
+/** \} */
-static int get_panel_header(const Panel *panel)
+/* -------------------------------------------------------------------- */
+/** \name Category Drawing (Tabs)
+ * \{ */
+
+static void imm_buf_append(
+ float vbuf[][2], uchar cbuf[][3], float x, float y, const uchar col[3], int *index)
{
- if (panel->type && (panel->type->flag & PNL_NO_HEADER)) {
- return 0;
+ ARRAY_SET_ITEMS(vbuf[*index], x, y);
+ ARRAY_SET_ITEMS(cbuf[*index], UNPACK3(col));
+ (*index)++;
+}
+
+/* based on UI_draw_roundbox, check on making a version which allows us to skip some sides */
+static void ui_panel_category_draw_tab(bool filled,
+ float minx,
+ float miny,
+ float maxx,
+ float maxy,
+ float rad,
+ const int roundboxtype,
+ const bool use_highlight,
+ const bool use_shadow,
+ const bool use_flip_x,
+ const uchar highlight_fade[3],
+ const uchar col[3])
+{
+ float vec[4][2] = {{0.195, 0.02}, {0.55, 0.169}, {0.831, 0.45}, {0.98, 0.805}};
+
+ /* Multiply `vec` by radius. */
+ for (int a = 0; a < 4; a++) {
+ mul_v2_fl(vec[a], rad);
+ }
+
+ uint vert_len = 0;
+ if (use_highlight) {
+ vert_len += (roundboxtype & UI_CNR_TOP_RIGHT) ? 6 : 1;
+ vert_len += (roundboxtype & UI_CNR_TOP_LEFT) ? 6 : 1;
+ }
+ if (use_highlight && !use_shadow) {
+ vert_len++;
+ }
+ else {
+ vert_len += (roundboxtype & UI_CNR_BOTTOM_RIGHT) ? 6 : 1;
+ vert_len += (roundboxtype & UI_CNR_BOTTOM_LEFT) ? 6 : 1;
+ }
+ /* Maximum size. */
+ float vbuf[24][2];
+ uchar cbuf[24][3];
+ int buf_index = 0;
+
+ /* start with corner right-top */
+ if (use_highlight) {
+ if (roundboxtype & UI_CNR_TOP_RIGHT) {
+ imm_buf_append(vbuf, cbuf, maxx, maxy - rad, col, &buf_index);
+ for (int a = 0; a < 4; a++) {
+ imm_buf_append(vbuf, cbuf, maxx - vec[a][1], maxy - rad + vec[a][0], col, &buf_index);
+ }
+ imm_buf_append(vbuf, cbuf, maxx - rad, maxy, col, &buf_index);
+ }
+ else {
+ imm_buf_append(vbuf, cbuf, maxx, maxy, col, &buf_index);
+ }
+
+ /* corner left-top */
+ if (roundboxtype & UI_CNR_TOP_LEFT) {
+ imm_buf_append(vbuf, cbuf, minx + rad, maxy, col, &buf_index);
+ for (int a = 0; a < 4; a++) {
+ imm_buf_append(vbuf, cbuf, minx + rad - vec[a][0], maxy - vec[a][1], col, &buf_index);
+ }
+ imm_buf_append(vbuf, cbuf, minx, maxy - rad, col, &buf_index);
+ }
+ else {
+ imm_buf_append(vbuf, cbuf, minx, maxy, col, &buf_index);
+ }
+ }
+
+ if (use_highlight && !use_shadow) {
+ imm_buf_append(
+ vbuf, cbuf, minx, miny + rad, highlight_fade ? col : highlight_fade, &buf_index);
+ }
+ else {
+ /* corner left-bottom */
+ if (roundboxtype & UI_CNR_BOTTOM_LEFT) {
+ imm_buf_append(vbuf, cbuf, minx, miny + rad, col, &buf_index);
+ for (int a = 0; a < 4; a++) {
+ imm_buf_append(vbuf, cbuf, minx + vec[a][1], miny + rad - vec[a][0], col, &buf_index);
+ }
+ imm_buf_append(vbuf, cbuf, minx + rad, miny, col, &buf_index);
+ }
+ else {
+ imm_buf_append(vbuf, cbuf, minx, miny, col, &buf_index);
+ }
+
+ /* corner right-bottom */
+ if (roundboxtype & UI_CNR_BOTTOM_RIGHT) {
+ imm_buf_append(vbuf, cbuf, maxx - rad, miny, col, &buf_index);
+ for (int a = 0; a < 4; a++) {
+ imm_buf_append(vbuf, cbuf, maxx - rad + vec[a][0], miny + vec[a][1], col, &buf_index);
+ }
+ imm_buf_append(vbuf, cbuf, maxx, miny + rad, col, &buf_index);
+ }
+ else {
+ imm_buf_append(vbuf, cbuf, maxx, miny, col, &buf_index);
+ }
+ }
+
+ if (use_flip_x) {
+ const float midx = (minx + maxx) / 2.0f;
+ for (int i = 0; i < buf_index; i++) {
+ vbuf[i][0] = midx - (vbuf[i][0] - midx);
+ }
+ }
+
+ GPUVertFormat *format = immVertexFormat();
+ const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ uint color = GPU_vertformat_attr_add(
+ format, "color", GPU_COMP_U8, 3, GPU_FETCH_INT_TO_FLOAT_UNIT);
+
+ immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR);
+ immBegin(filled ? GPU_PRIM_TRI_FAN : GPU_PRIM_LINE_STRIP, vert_len);
+ for (int i = 0; i < buf_index; i++) {
+ immAttr3ubv(color, cbuf[i]);
+ immVertex2fv(pos, vbuf[i]);
+ }
+ immEnd();
+ immUnbindProgram();
+}
+
+/**
+ * Draw vertical tabs on the left side of the region,
+ * one tab per category.
+ */
+void UI_panel_category_draw_all(ARegion *region, const char *category_id_active)
+{
+ /* no tab outlines for */
+ // #define USE_FLAT_INACTIVE
+ const bool is_left = RGN_ALIGN_ENUM_FROM_MASK(region->alignment != RGN_ALIGN_RIGHT);
+ View2D *v2d = &region->v2d;
+ const uiStyle *style = UI_style_get();
+ const uiFontStyle *fstyle = &style->widget;
+ const int fontid = fstyle->uifont_id;
+ short fstyle_points = fstyle->points;
+ const float aspect = ((uiBlock *)region->uiblocks.first)->aspect;
+ const float zoom = 1.0f / aspect;
+ const int px = max_ii(1, round_fl_to_int(U.pixelsize));
+ const int px_x_sign = is_left ? px : -px;
+ const int category_tabs_width = round_fl_to_int(UI_PANEL_CATEGORY_MARGIN_WIDTH * zoom);
+ const float dpi_fac = UI_DPI_FAC;
+ /* padding of tabs around text */
+ const int tab_v_pad_text = round_fl_to_int((2 + ((px * 3) * dpi_fac)) * zoom);
+ /* padding between tabs */
+ const int tab_v_pad = round_fl_to_int((4 + (2 * px * dpi_fac)) * zoom);
+ const float tab_curve_radius = ((px * 3) * dpi_fac) * zoom;
+ /* We flip the tab drawing, so always use these flags. */
+ const int roundboxtype = UI_CNR_TOP_LEFT | UI_CNR_BOTTOM_LEFT;
+ bool is_alpha;
+ bool do_scaletabs = false;
+#ifdef USE_FLAT_INACTIVE
+ bool is_active_prev = false;
+#endif
+ float scaletabs = 1.0f;
+ /* same for all tabs */
+ /* intentionally don't scale by 'px' */
+ const int rct_xmin = is_left ? v2d->mask.xmin + 3 : (v2d->mask.xmax - category_tabs_width);
+ const int rct_xmax = is_left ? v2d->mask.xmin + category_tabs_width : (v2d->mask.xmax - 3);
+ const int text_v_ofs = (rct_xmax - rct_xmin) * 0.3f;
+
+ int y_ofs = tab_v_pad;
+
+ /* Primary theme colors */
+ uchar theme_col_back[4];
+ uchar theme_col_text[3];
+ uchar theme_col_text_hi[3];
+
+ /* Tab colors */
+ uchar theme_col_tab_bg[4];
+ uchar theme_col_tab_active[3];
+ uchar theme_col_tab_inactive[3];
+
+ /* Secondary theme colors */
+ uchar theme_col_tab_outline[3];
+ uchar theme_col_tab_divider[3]; /* line that divides tabs from the main region */
+ uchar theme_col_tab_highlight[3];
+ uchar theme_col_tab_highlight_inactive[3];
+
+ UI_GetThemeColor4ubv(TH_BACK, theme_col_back);
+ UI_GetThemeColor3ubv(TH_TEXT, theme_col_text);
+ UI_GetThemeColor3ubv(TH_TEXT_HI, theme_col_text_hi);
+
+ UI_GetThemeColor4ubv(TH_TAB_BACK, theme_col_tab_bg);
+ UI_GetThemeColor3ubv(TH_TAB_ACTIVE, theme_col_tab_active);
+ UI_GetThemeColor3ubv(TH_TAB_INACTIVE, theme_col_tab_inactive);
+ UI_GetThemeColor3ubv(TH_TAB_OUTLINE, theme_col_tab_outline);
+
+ interp_v3_v3v3_uchar(theme_col_tab_divider, theme_col_back, theme_col_tab_outline, 0.3f);
+ interp_v3_v3v3_uchar(theme_col_tab_highlight, theme_col_back, theme_col_text_hi, 0.2f);
+ interp_v3_v3v3_uchar(
+ theme_col_tab_highlight_inactive, theme_col_tab_inactive, theme_col_text_hi, 0.12f);
+
+ is_alpha = (region->overlap && (theme_col_back[3] != 255));
+
+ if (fstyle->kerning == 1) {
+ BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
+ }
+
+ BLF_enable(fontid, BLF_ROTATION);
+ BLF_rotation(fontid, M_PI_2);
+ // UI_fontstyle_set(&style->widget);
+ ui_fontscale(&fstyle_points, aspect / (U.pixelsize * 1.1f));
+ BLF_size(fontid, fstyle_points, U.dpi);
+
+ /* Check the region type supports categories to avoid an assert
+ * for showing 3D view panels in the properties space. */
+ if ((1 << region->regiontype) & RGN_TYPE_HAS_CATEGORY_MASK) {
+ BLI_assert(UI_panel_category_is_visible(region));
}
- return PNL_HEADER;
+ /* Calculate tab rectangle and check if we need to scale down. */
+ LISTBASE_FOREACH (PanelCategoryDyn *, pc_dyn, &region->panels_category) {
+
+ rcti *rct = &pc_dyn->rect;
+ const char *category_id = pc_dyn->idname;
+ const char *category_id_draw = IFACE_(category_id);
+ const int category_width = BLF_width(fontid, category_id_draw, BLF_DRAW_STR_DUMMY_MAX);
+
+ rct->xmin = rct_xmin;
+ rct->xmax = rct_xmax;
+
+ rct->ymin = v2d->mask.ymax - (y_ofs + category_width + (tab_v_pad_text * 2));
+ rct->ymax = v2d->mask.ymax - (y_ofs);
+
+ y_ofs += category_width + tab_v_pad + (tab_v_pad_text * 2);
+ }
+
+ if (y_ofs > BLI_rcti_size_y(&v2d->mask)) {
+ scaletabs = (float)BLI_rcti_size_y(&v2d->mask) / (float)y_ofs;
+
+ LISTBASE_FOREACH (PanelCategoryDyn *, pc_dyn, &region->panels_category) {
+ rcti *rct = &pc_dyn->rect;
+ rct->ymin = ((rct->ymin - v2d->mask.ymax) * scaletabs) + v2d->mask.ymax;
+ rct->ymax = ((rct->ymax - v2d->mask.ymax) * scaletabs) + v2d->mask.ymax;
+ }
+
+ do_scaletabs = true;
+ }
+
+ /* begin drawing */
+ GPU_line_smooth(true);
+
+ uint pos = GPU_vertformat_attr_add(
+ immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
+ immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+
+ /* draw the background */
+ if (is_alpha) {
+ GPU_blend(GPU_BLEND_ALPHA);
+ immUniformColor4ubv(theme_col_tab_bg);
+ }
+ else {
+ immUniformColor3ubv(theme_col_tab_bg);
+ }
+
+ if (is_left) {
+ immRecti(
+ pos, v2d->mask.xmin, v2d->mask.ymin, v2d->mask.xmin + category_tabs_width, v2d->mask.ymax);
+ }
+ else {
+ immRecti(
+ pos, v2d->mask.xmax - category_tabs_width, v2d->mask.ymin, v2d->mask.xmax, v2d->mask.ymax);
+ }
+
+ if (is_alpha) {
+ GPU_blend(GPU_BLEND_NONE);
+ }
+
+ immUnbindProgram();
+
+ const int divider_xmin = is_left ? (v2d->mask.xmin + (category_tabs_width - px)) :
+ (v2d->mask.xmax - category_tabs_width) + px;
+ const int divider_xmax = is_left ? (v2d->mask.xmin + category_tabs_width) :
+ (v2d->mask.xmax - (category_tabs_width + px)) + px;
+
+ LISTBASE_FOREACH (PanelCategoryDyn *, pc_dyn, &region->panels_category) {
+ const rcti *rct = &pc_dyn->rect;
+ const char *category_id = pc_dyn->idname;
+ const char *category_id_draw = IFACE_(category_id);
+ const int category_width = BLI_rcti_size_y(rct) - (tab_v_pad_text * 2);
+ size_t category_draw_len = BLF_DRAW_STR_DUMMY_MAX;
+#if 0
+ int category_width = BLF_width(fontid, category_id_draw, BLF_DRAW_STR_DUMMY_MAX);
+#endif
+
+ const bool is_active = STREQ(category_id, category_id_active);
+
+ GPU_blend(GPU_BLEND_ALPHA);
+
+#ifdef USE_FLAT_INACTIVE
+ if (is_active)
+#endif
+ {
+ const bool use_flip_x = !is_left;
+ ui_panel_category_draw_tab(true,
+ rct->xmin,
+ rct->ymin,
+ rct->xmax,
+ rct->ymax,
+ tab_curve_radius - px,
+ roundboxtype,
+ true,
+ true,
+ use_flip_x,
+ NULL,
+ is_active ? theme_col_tab_active : theme_col_tab_inactive);
+
+ /* Tab outline */
+ ui_panel_category_draw_tab(false,
+ rct->xmin - px_x_sign,
+ rct->ymin - px,
+ rct->xmax - px_x_sign,
+ rct->ymax + px,
+ tab_curve_radius,
+ roundboxtype,
+ true,
+ true,
+ use_flip_x,
+ NULL,
+ theme_col_tab_outline);
+
+ /* Tab highlight (3d look) */
+ ui_panel_category_draw_tab(false,
+ rct->xmin,
+ rct->ymin,
+ rct->xmax,
+ rct->ymax,
+ tab_curve_radius,
+ roundboxtype,
+ true,
+ false,
+ use_flip_x,
+ is_active ? theme_col_back : theme_col_tab_inactive,
+ is_active ? theme_col_tab_highlight :
+ theme_col_tab_highlight_inactive);
+ }
+
+ /* Tab black-line. */
+ if (!is_active) {
+ pos = GPU_vertformat_attr_add(
+ immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
+ immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+
+ immUniformColor3ubv(theme_col_tab_divider);
+ immRecti(pos, divider_xmin, rct->ymin - tab_v_pad, divider_xmax, rct->ymax + tab_v_pad);
+ immUnbindProgram();
+ }
+
+ if (do_scaletabs) {
+ category_draw_len = BLF_width_to_strlen(
+ fontid, category_id_draw, category_draw_len, category_width, NULL);
+ }
+
+ BLF_position(fontid, rct->xmax - text_v_ofs, rct->ymin + tab_v_pad_text, 0.0f);
+
+ /* Tab titles. */
+
+ /* Draw white shadow to give text more depth. */
+ BLF_color3ubv(fontid, theme_col_text);
+
+ /* Main tab title. */
+ BLF_draw(fontid, category_id_draw, category_draw_len);
+
+ GPU_blend(GPU_BLEND_NONE);
+
+ /* Tab black-line remaining (last tab). */
+ pos = GPU_vertformat_attr_add(
+ immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
+ immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ if (pc_dyn->prev == NULL) {
+ immUniformColor3ubv(theme_col_tab_divider);
+ immRecti(pos, divider_xmin, rct->ymax + px, divider_xmax, v2d->mask.ymax);
+ }
+ if (pc_dyn->next == NULL) {
+ immUniformColor3ubv(theme_col_tab_divider);
+ immRecti(pos, divider_xmin, 0, divider_xmax, rct->ymin);
+ }
+
+#ifdef USE_FLAT_INACTIVE
+ /* Draw line between inactive tabs. */
+ if (is_active == false && is_active_prev == false && pc_dyn->prev) {
+ immUniformColor3ubv(theme_col_tab_divider);
+ immRecti(pos,
+ v2d->mask.xmin + (category_tabs_width / 5),
+ rct->ymax + px,
+ (v2d->mask.xmin + category_tabs_width) - (category_tabs_width / 5),
+ rct->ymax + (px * 3));
+ }
+
+ is_active_prev = is_active;
+#endif
+ immUnbindProgram();
+
+ /* not essential, but allows events to be handled right up until the region edge [#38171] */
+ if (is_left) {
+ pc_dyn->rect.xmin = v2d->mask.xmin;
+ }
+ else {
+ pc_dyn->rect.xmax = v2d->mask.xmax;
+ }
+ }
+
+ GPU_line_smooth(false);
+
+ BLF_disable(fontid, BLF_ROTATION);
+
+ if (fstyle->kerning == 1) {
+ BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
+ }
+
+#undef USE_FLAT_INACTIVE
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Panel Alignment
+ * \{ */
+
static int get_panel_size_y(const Panel *panel)
{
if (panel->type && (panel->type->flag & PNL_NO_HEADER)) {
@@ -1220,7 +1552,7 @@ static int get_panel_size_y(const Panel *panel)
static int get_panel_real_size_y(const Panel *panel)
{
- int sizey = (panel->flag & PNL_CLOSED) ? 0 : panel->sizey;
+ const int sizey = (panel->flag & PNL_CLOSED) ? 0 : panel->sizey;
if (panel->type && (panel->type->flag & PNL_NO_HEADER)) {
return sizey;
@@ -1234,24 +1566,18 @@ int UI_panel_size_y(const Panel *panel)
return get_panel_real_size_y(panel);
}
-/* this function is needed because uiBlock and Panel itself don't
- * change sizey or location when closed */
+/**
+ * This function is needed because #uiBlock and Panel itself don't
+ * change #Panel.sizey or location when closed.
+ */
static int get_panel_real_ofsy(Panel *panel)
{
- if (panel->flag & PNL_CLOSEDY) {
+ if (panel->flag & PNL_CLOSED) {
return panel->ofsy + panel->sizey;
}
return panel->ofsy;
}
-static int get_panel_real_ofsx(Panel *panel)
-{
- if (panel->flag & PNL_CLOSEDX) {
- return panel->ofsx + get_panel_header(panel);
- }
- return panel->ofsx + panel->sizex;
-}
-
bool UI_panel_is_dragging(const struct Panel *panel)
{
uiHandlePanelData *data = panel->activedata;
@@ -1264,32 +1590,12 @@ bool UI_panel_is_dragging(const struct Panel *panel)
/**
* \note about sorting;
- * the sortorder has a lower value for new panels being added.
+ * the #Panel.sortorder has a lower value for new panels being added.
* however, that only works to insert a single panel, when more new panels get
* added the coordinates of existing panels and the previously stored to-be-inserted
* panels do not match for sorting
*/
-static int find_leftmost_panel(const void *a1, const void *a2)
-{
- const PanelSort *ps1 = a1, *ps2 = a2;
-
- if (ps1->panel->ofsx > ps2->panel->ofsx) {
- return 1;
- }
- if (ps1->panel->ofsx < ps2->panel->ofsx) {
- return -1;
- }
- if (ps1->panel->sortorder > ps2->panel->sortorder) {
- return 1;
- }
- if (ps1->panel->sortorder < ps2->panel->sortorder) {
- return -1;
- }
-
- return 0;
-}
-
static int find_highest_panel(const void *a1, const void *a2)
{
const PanelSort *ps1 = a1, *ps2 = a2;
@@ -1297,7 +1603,7 @@ static int find_highest_panel(const void *a1, const void *a2)
/* stick uppermost header-less panels to the top of the region -
* prevent them from being sorted (multiple header-less panels have to be sorted though) */
if (ps1->panel->type->flag & PNL_NO_HEADER && ps2->panel->type->flag & PNL_NO_HEADER) {
- /* skip and check for ofs and sortorder below */
+ /* Skip and check for `ofsy` and #Panel.sortorder below. */
}
if (ps1->panel->type->flag & PNL_NO_HEADER) {
return -1;
@@ -1356,14 +1662,12 @@ static void align_sub_panels(Panel *panel)
/* this doesn't draw */
/* returns 1 when it did something */
-static bool uiAlignPanelStep(ScrArea *area, ARegion *region, const float fac, const bool drag)
+static bool uiAlignPanelStep(ARegion *region, const float fac, const bool drag)
{
- PanelSort *ps, *panelsort, *psnext;
- int a, tot = 0;
- bool done;
- int align = panel_aligned(area, region);
+ int i;
/* count active, not tabbed panels */
+ int tot = 0;
LISTBASE_FOREACH (Panel *, panel, &region->panels) {
if (panel->runtime_flag & PNL_ACTIVE) {
tot++;
@@ -1374,22 +1678,10 @@ static bool uiAlignPanelStep(ScrArea *area, ARegion *region, const float fac, co
return 0;
}
- /* extra; change close direction? */
- LISTBASE_FOREACH (Panel *, panel, &region->panels) {
- if (panel->runtime_flag & PNL_ACTIVE) {
- if ((panel->flag & PNL_CLOSEDX) && (align == BUT_VERTICAL)) {
- panel->flag ^= PNL_CLOSED;
- }
- else if ((panel->flag & PNL_CLOSEDY) && (align == BUT_HORIZONTAL)) {
- panel->flag ^= PNL_CLOSED;
- }
- }
- }
-
/* sort panels */
- panelsort = MEM_callocN(tot * sizeof(PanelSort), "panelsort");
+ PanelSort *panelsort = MEM_callocN(tot * sizeof(PanelSort), "panelsort");
- ps = panelsort;
+ PanelSort *ps = panelsort;
LISTBASE_FOREACH (Panel *, panel, &region->panels) {
if (panel->runtime_flag & PNL_ACTIVE) {
ps->panel = MEM_dupallocN(panel);
@@ -1399,49 +1691,37 @@ static bool uiAlignPanelStep(ScrArea *area, ARegion *region, const float fac, co
}
if (drag) {
- /* while we are dragging, we sort on location and update sortorder */
- if (align == BUT_VERTICAL) {
- qsort(panelsort, tot, sizeof(PanelSort), find_highest_panel);
- }
- else {
- qsort(panelsort, tot, sizeof(PanelSort), find_leftmost_panel);
- }
+ /* While we are dragging, we sort on location and update #Panel.sortorder. */
+ qsort(panelsort, tot, sizeof(PanelSort), find_highest_panel);
- for (ps = panelsort, a = 0; a < tot; a++, ps++) {
- ps->orig->sortorder = a;
+ for (ps = panelsort, i = 0; i < tot; i++, ps++) {
+ ps->orig->sortorder = i;
}
}
else {
- /* otherwise use sortorder */
+ /* Otherwise use #Panel.sortorder. */
qsort(panelsort, tot, sizeof(PanelSort), compare_panel);
}
- /* no smart other default start loc! this keeps switching f5/f6/etc compatible */
+ /* No smart other default start location! This keeps switching f5/f6/etc compatible. */
ps = panelsort;
- ps->panel->runtime.region_ofsx = panel_region_offset_x_get(region, align);
+ ps->panel->runtime.region_ofsx = panel_region_offset_x_get(region);
ps->panel->ofsx = 0;
ps->panel->ofsy = -get_panel_size_y(ps->panel);
ps->panel->ofsx += ps->panel->runtime.region_ofsx;
- for (a = 0; a < tot - 1; a++, ps++) {
- psnext = ps + 1;
+ for (i = 0; i < tot - 1; i++, ps++) {
+ PanelSort *psnext = ps + 1;
- if (align == BUT_VERTICAL) {
- bool use_box = ps->panel->type && ps->panel->type->flag & PNL_DRAW_BOX;
- bool use_box_next = psnext->panel->type && psnext->panel->type->flag & PNL_DRAW_BOX;
- psnext->panel->ofsx = ps->panel->ofsx;
- psnext->panel->ofsy = get_panel_real_ofsy(ps->panel) - get_panel_size_y(psnext->panel);
+ const bool use_box = ps->panel->type && ps->panel->type->flag & PNL_DRAW_BOX;
+ const bool use_box_next = psnext->panel->type && psnext->panel->type->flag & PNL_DRAW_BOX;
+ psnext->panel->ofsx = ps->panel->ofsx;
+ psnext->panel->ofsy = get_panel_real_ofsy(ps->panel) - get_panel_size_y(psnext->panel);
- /* Extra margin for box style panels. */
- ps->panel->ofsx += (use_box) ? UI_PANEL_BOX_STYLE_MARGIN : 0.0f;
- if (use_box || use_box_next) {
- psnext->panel->ofsy -= UI_PANEL_BOX_STYLE_MARGIN;
- }
- }
- else {
- psnext->panel->ofsx = get_panel_real_ofsx(ps->panel);
- psnext->panel->ofsy = ps->panel->ofsy + get_panel_size_y(ps->panel) -
- get_panel_size_y(psnext->panel);
+ /* Extra margin for box style panels. */
+ ps->panel->ofsx += (use_box) ? UI_PANEL_BOX_STYLE_MARGIN : 0.0f;
+ if (use_box || use_box_next) {
+ psnext->panel->ofsy -= UI_PANEL_BOX_STYLE_MARGIN;
}
}
/* Extra margin for the last panel if it's a box-style panel. */
@@ -1450,16 +1730,16 @@ static bool uiAlignPanelStep(ScrArea *area, ARegion *region, const float fac, co
}
/* we interpolate */
- done = false;
+ bool changed = false;
ps = panelsort;
- for (a = 0; a < tot; a++, ps++) {
+ for (i = 0; i < tot; i++, ps++) {
if ((ps->panel->flag & PNL_SELECT) == 0) {
if ((ps->orig->ofsx != ps->panel->ofsx) || (ps->orig->ofsy != ps->panel->ofsy)) {
ps->orig->ofsx = round_fl_to_int(fac * (float)ps->panel->ofsx +
(1.0f - fac) * (float)ps->orig->ofsx);
ps->orig->ofsy = round_fl_to_int(fac * (float)ps->panel->ofsy +
(1.0f - fac) * (float)ps->orig->ofsy);
- done = true;
+ changed = true;
}
}
}
@@ -1473,34 +1753,25 @@ static bool uiAlignPanelStep(ScrArea *area, ARegion *region, const float fac, co
}
}
- /* free panelsort array */
- for (ps = panelsort, a = 0; a < tot; a++, ps++) {
+ /* Free `panelsort` array. */
+ for (ps = panelsort, i = 0; i < tot; i++, ps++) {
MEM_freeN(ps->panel);
}
MEM_freeN(panelsort);
- return done;
+ return changed;
}
-static void ui_panels_size(ScrArea *area, ARegion *region, int *r_x, int *r_y)
+static void ui_panels_size(ARegion *region, int *r_x, int *r_y)
{
- int align = panel_aligned(area, region);
int sizex = 0;
int sizey = 0;
/* compute size taken up by panels, for setting in view2d */
LISTBASE_FOREACH (Panel *, panel, &region->panels) {
if (panel->runtime_flag & PNL_ACTIVE) {
- int pa_sizex, pa_sizey;
-
- if (align == BUT_VERTICAL) {
- pa_sizex = panel->ofsx + panel->sizex;
- pa_sizey = get_panel_real_ofsy(panel);
- }
- else {
- pa_sizex = get_panel_real_ofsx(panel) + panel->sizex;
- pa_sizey = panel->ofsy + get_panel_size_y(panel);
- }
+ const int pa_sizex = panel->ofsx + panel->sizex;
+ const int pa_sizey = get_panel_real_ofsy(panel);
sizex = max_ii(sizex, pa_sizex);
sizey = min_ii(sizey, pa_sizey);
@@ -1521,15 +1792,13 @@ static void ui_panels_size(ScrArea *area, ARegion *region, int *r_x, int *r_y)
static void ui_do_animate(bContext *C, Panel *panel)
{
uiHandlePanelData *data = panel->activedata;
- ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
- float fac;
- fac = (PIL_check_seconds_timer() - data->starttime) / ANIMATION_TIME;
+ float fac = (PIL_check_seconds_timer() - data->starttime) / ANIMATION_TIME;
fac = min_ff(sqrtf(fac), 1.0f);
/* for max 1 second, interpolate positions */
- if (uiAlignPanelStep(area, region, fac, false)) {
+ if (uiAlignPanelStep(region, fac, false)) {
ED_region_tag_redraw(region);
}
else {
@@ -1542,8 +1811,8 @@ static void ui_do_animate(bContext *C, Panel *panel)
panel_activate_state(C, panel, PANEL_STATE_EXIT);
if (is_drag_drop) {
- /* Note: doing this in #panel_activate_state would require removing const for context in many
- * other places. */
+ /* Note: doing this in #panel_activate_state would require removing `const` for context in
+ * many other places. */
reorder_instanced_panel_list(C, region, panel);
}
return;
@@ -1575,7 +1844,6 @@ void UI_panels_begin(const bContext *UNUSED(C), ARegion *region)
void UI_panels_end(const bContext *C, ARegion *region, int *r_x, int *r_y)
{
ScrArea *area = CTX_wm_area(C);
- Panel *panel, *panel_first;
/* offset contents */
LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
@@ -1585,17 +1853,18 @@ void UI_panels_end(const bContext *C, ARegion *region, int *r_x, int *r_y)
}
/* re-align, possibly with animation */
+ Panel *panel;
if (panels_need_realign(area, region, &panel)) {
if (panel) {
panel_activate_state(C, panel, PANEL_STATE_ANIMATION);
}
else {
- uiAlignPanelStep(area, region, 1.0, false);
+ uiAlignPanelStep(region, 1.0, false);
}
}
/* tag first panel */
- panel_first = NULL;
+ Panel *panel_first = NULL;
LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
if (block->active && block->panel) {
if (!panel_first || block->panel->sortorder < panel_first->sortorder) {
@@ -1609,15 +1878,11 @@ void UI_panels_end(const bContext *C, ARegion *region, int *r_x, int *r_y)
}
/* compute size taken up by panel */
- ui_panels_size(area, region, r_x, r_y);
+ ui_panels_size(region, r_x, r_y);
}
void UI_panels_draw(const bContext *C, ARegion *region)
{
- if (region->alignment != RGN_ALIGN_FLOAT) {
- UI_ThemeClearColor(TH_BACK);
- }
-
/* Draw panels, selected on top. Also in reverse order, because
* UI blocks are added in reverse order and we need child panels
* to draw on top. */
@@ -1638,7 +1903,7 @@ void UI_panels_scale(ARegion *region, float new_width)
{
LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
if (block->panel) {
- float fac = new_width / (float)block->panel->sizex;
+ const float fac = new_width / (float)block->panel->sizex;
block->panel->sizex = new_width;
LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
@@ -1649,26 +1914,28 @@ void UI_panels_scale(ARegion *region, float new_width)
}
}
-/************************ panel dragging ****************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Panel Dragging
+ * \{ */
#define DRAG_REGION_PAD (PNL_HEADER * 0.5)
static void ui_do_drag(const bContext *C, const wmEvent *event, Panel *panel)
{
uiHandlePanelData *data = panel->activedata;
- ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
- short align = panel_aligned(area, region);
/* Keep the drag position in the region with a small pad to keep the panel visible. */
- int x = clamp_i(event->x, region->winrct.xmin, region->winrct.xmax + DRAG_REGION_PAD);
- int y = clamp_i(event->y, region->winrct.ymin, region->winrct.ymax + DRAG_REGION_PAD);
+ const int x = clamp_i(event->x, region->winrct.xmin, region->winrct.xmax + DRAG_REGION_PAD);
+ const int y = clamp_i(event->y, region->winrct.ymin, region->winrct.ymax + DRAG_REGION_PAD);
float dx = (float)(x - data->startx);
float dy = (float)(y - data->starty);
/* Adjust for region zoom. */
- dx *= (float)BLI_rctf_size_x(&region->v2d.cur) / (float)BLI_rcti_size_x(&region->winrct);
- dy *= (float)BLI_rctf_size_y(&region->v2d.cur) / (float)BLI_rcti_size_y(&region->winrct);
+ dx *= BLI_rctf_size_x(&region->v2d.cur) / (float)BLI_rcti_size_x(&region->winrct);
+ dy *= BLI_rctf_size_y(&region->v2d.cur) / (float)BLI_rcti_size_y(&region->winrct);
if (data->state == PANEL_STATE_DRAG_SCALE) {
panel->sizex = MAX2(data->startsizex + dx, UI_PANEL_MINX);
@@ -1690,43 +1957,38 @@ static void ui_do_drag(const bContext *C, const wmEvent *event, Panel *panel)
panel->ofsx = data->startofsx + round_fl_to_int(dx);
panel->ofsy = data->startofsy + round_fl_to_int(dy);
- if (align) {
- uiAlignPanelStep(area, region, 0.2f, true);
- }
+ uiAlignPanelStep(region, 0.2f, true);
}
ED_region_tag_redraw(region);
}
#undef DRAG_REGION_PAD
-/******************* region level panel interaction *****************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Region Level Panel Interaction
+ * \{ */
static uiPanelMouseState ui_panel_mouse_state_get(const uiBlock *block,
const Panel *panel,
const int mx,
const int my)
{
- /* open panel */
- if (panel->flag & PNL_CLOSEDX) {
- if ((block->rect.xmin <= mx) && (block->rect.xmin + PNL_HEADER >= mx)) {
- return PANEL_MOUSE_INSIDE_HEADER;
- }
+ if (!IN_RANGE((float)mx, block->rect.xmin, block->rect.xmax)) {
+ return PANEL_MOUSE_OUTSIDE;
}
- /* outside left/right side */
- else if ((block->rect.xmin > mx) || (block->rect.xmax < mx)) {
- /* pass */
- }
- else if ((block->rect.ymax <= my) && (block->rect.ymax + PNL_HEADER >= my)) {
+
+ if (IN_RANGE((float)my, block->rect.ymax, block->rect.ymax + PNL_HEADER)) {
return PANEL_MOUSE_INSIDE_HEADER;
}
- /* open panel */
- else if (!(panel->flag & PNL_CLOSEDY)) {
- if ((block->rect.xmin <= mx) && (block->rect.xmax >= mx)) {
- if ((block->rect.ymin <= my) && (block->rect.ymax + PNL_HEADER >= my)) {
- return PANEL_MOUSE_INSIDE_CONTENT;
- }
+
+ if (!(panel->flag & PNL_CLOSED)) {
+ if (IN_RANGE((float)my, block->rect.ymin, block->rect.ymax + PNL_HEADER)) {
+ return PANEL_MOUSE_INSIDE_CONTENT;
}
}
+
return PANEL_MOUSE_OUTSIDE;
}
@@ -1741,55 +2003,38 @@ static void ui_panel_drag_collapse_handler_remove(bContext *UNUSED(C), void *use
MEM_freeN(dragcol_data);
}
-static void ui_panel_drag_collapse(bContext *C,
- uiPanelDragCollapseHandle *dragcol_data,
+static void ui_panel_drag_collapse(const bContext *C,
+ const uiPanelDragCollapseHandle *dragcol_data,
const int xy_dst[2])
{
- ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
- Panel *panel;
LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
float xy_a_block[2] = {UNPACK2(dragcol_data->xy_init)};
float xy_b_block[2] = {UNPACK2(xy_dst)};
- rctf rect = block->rect;
- int oldflag;
- const bool is_horizontal = (panel_aligned(area, region) == BUT_HORIZONTAL);
+ Panel *panel = block->panel;
- if ((panel = block->panel) == 0 || (panel->type && (panel->type->flag & PNL_NO_HEADER))) {
+ if (panel == NULL || (panel->type && (panel->type->flag & PNL_NO_HEADER))) {
continue;
}
- oldflag = panel->flag;
+ const int oldflag = panel->flag;
- /* lock one axis */
- if (is_horizontal) {
- xy_b_block[1] = dragcol_data->xy_init[1];
- }
- else {
- xy_b_block[0] = dragcol_data->xy_init[0];
- }
+ /* lock axis */
+ xy_b_block[0] = dragcol_data->xy_init[0];
/* use cursor coords in block space */
ui_window_to_block_fl(region, block, &xy_a_block[0], &xy_a_block[1]);
ui_window_to_block_fl(region, block, &xy_b_block[0], &xy_b_block[1]);
- /* set up rect to match header size */
+ /* Set up `rect` to match header size. */
+ rctf rect = block->rect;
rect.ymin = rect.ymax;
rect.ymax = rect.ymin + PNL_HEADER;
- if (panel->flag & PNL_CLOSEDX) {
- rect.xmax = rect.xmin + PNL_HEADER;
- }
/* touch all panels between last mouse coord and the current one */
if (BLI_rctf_isect_segment(&rect, xy_a_block, xy_b_block)) {
- /* force panel to close */
- if (dragcol_data->was_first_open == true) {
- panel->flag |= (is_horizontal ? PNL_CLOSEDX : PNL_CLOSEDY);
- }
- /* force panel to open */
- else {
- panel->flag &= ~PNL_CLOSED;
- }
+ /* Force panel to open or close. */
+ SET_FLAG_FROM_TEST(panel->flag, dragcol_data->was_first_open, PNL_CLOSED);
/* if panel->flag has changed this means a panel was opened/closed here */
if (panel->flag != oldflag) {
@@ -1853,145 +2098,112 @@ static void ui_panel_drag_collapse_handler_add(const bContext *C, const bool was
0);
}
-/* this function is supposed to call general window drawing too */
-/* also it supposes a block has panel, and isn't a menu */
-static void ui_handle_panel_header(
- const bContext *C, uiBlock *block, int mx, int my, int event, short ctrl, short shift)
+/**
+ * Supposing the block has a panel and isn't a menu, handle opening, closing, pinning, etc.
+ * Code currently assumes layout style for location of widgets
+ *
+ * \param mx The mouse x coordinate, in panel space.
+ */
+static void ui_handle_panel_header(const bContext *C,
+ uiBlock *block,
+ const int mx,
+ short int event_type,
+ const short ctrl,
+ const short shift)
{
- ScrArea *area = CTX_wm_area(C);
+ Panel *panel = block->panel;
ARegion *region = CTX_wm_region(C);
-#ifdef USE_PIN_HIDDEN
- const bool show_pin = UI_panel_category_is_visible(region) &&
- (block->panel->type->parent == NULL) && (block->panel->flag & PNL_PIN);
-#else
- const bool show_pin = UI_panel_category_is_visible(region) &&
- (block->panel->type->parent == NULL);
-#endif
- const bool is_subpanel = (block->panel->type && block->panel->type->parent);
- const bool show_drag = !is_subpanel;
- int align = panel_aligned(area, region), button = 0;
+ BLI_assert(panel->type != NULL);
+ BLI_assert(!(panel->type->flag & PNL_NO_HEADER));
- rctf rect_drag, rect_pin;
- float rect_leftmost;
+ const bool is_subpanel = (panel->type->parent != NULL);
+ const bool use_pin = UI_panel_category_is_visible(region) && !is_subpanel;
+ const bool show_pin = use_pin && (panel->flag & PNL_PIN);
+ const bool show_drag = !is_subpanel;
- /* drag and pin rect's */
- rect_drag = block->rect;
- rect_drag.xmin = block->rect.xmax - (PNL_ICON * 1.5f);
- rect_pin = rect_drag;
- if (show_pin) {
- BLI_rctf_translate(&rect_pin, -PNL_ICON, 0.0f);
+ /* Handle panel pinning. */
+ if (use_pin && ELEM(event_type, EVT_RETKEY, EVT_PADENTER, LEFTMOUSE) && shift) {
+ panel->flag ^= PNL_PIN;
+ ED_region_tag_redraw(region);
+ return;
}
- rect_leftmost = rect_pin.xmin;
-
- /* mouse coordinates in panel space! */
- /* XXX weak code, currently it assumes layout style for location of widgets */
-
- /* check open/collapsed button */
- if (event == EVT_RETKEY) {
- button = 1;
- }
- else if (event == EVT_AKEY) {
- button = 1;
- }
- else if (ELEM(event, 0, EVT_RETKEY, LEFTMOUSE) && shift) {
- if (block->panel->type->parent == NULL) {
- block->panel->flag ^= PNL_PIN;
- button = 2;
- }
+ float expansion_area_xmax = block->rect.xmax;
+ if (show_drag) {
+ expansion_area_xmax -= (PNL_ICON * 1.5f);
}
- else if (block->panel->flag & PNL_CLOSEDX) {
- if (my >= block->rect.ymax) {
- button = 1;
- }
- }
- else if (mx < rect_leftmost) {
- button = 1;
+ if (show_pin) {
+ expansion_area_xmax -= PNL_ICON;
}
- if (button) {
- if (button == 2) { /* close */
- ED_region_tag_redraw(region);
- }
- else {
- /* Collapse and expand panels. */
-
- if (ctrl) {
- /* For parent panels, collapse all other panels or toggle children. */
- if (block->panel->type != NULL && block->panel->type->parent == NULL) {
- if (block->panel->flag & PNL_CLOSED || BLI_listbase_is_empty(&block->panel->children)) {
- panels_collapse_all(C, area, region, block->panel);
+ /* Collapse and expand panels. */
+ if (ELEM(event_type, EVT_RETKEY, EVT_PADENTER, EVT_AKEY) || mx < expansion_area_xmax) {
+ if (ctrl && !is_subpanel) {
+ /* For parent panels, collapse all other panels or toggle children. */
+ if (panel->flag & PNL_CLOSED || BLI_listbase_is_empty(&panel->children)) {
+ panels_collapse_all(region, panel);
- /* Reset the view - we don't want to display a view without content. */
- UI_view2d_offset(&region->v2d, 0.0f, 1.0f);
- }
- else {
- const int closed_flag = (align == BUT_HORIZONTAL) ? PNL_CLOSEDX : PNL_CLOSEDY;
- /* If a panel has sub-panels and it's open, toggle the expansion
- * of the sub-panels (based on the expansion of the first subpanel). */
- Panel *first_child = block->panel->children.first;
- BLI_assert(first_child != NULL);
- panel_set_flag_recursive(
- block->panel, closed_flag, (first_child->flag & PNL_CLOSED) == 0);
- block->panel->flag |= closed_flag;
- }
- }
+ /* Reset the view - we don't want to display a view without content. */
+ UI_view2d_offset(&region->v2d, 0.0f, 1.0f);
}
-
- if (block->panel->flag & PNL_CLOSED) {
- block->panel->flag &= ~PNL_CLOSED;
- /* snap back up so full panel aligns with screen edge */
- if (block->panel->snap & PNL_SNAP_BOTTOM) {
- block->panel->ofsy = 0;
- }
-
- if (event == LEFTMOUSE) {
- ui_panel_drag_collapse_handler_add(C, false);
- }
+ else {
+ /* If a panel has sub-panels and it's open, toggle the expansion
+ * of the sub-panels (based on the expansion of the first sub-panel). */
+ Panel *first_child = panel->children.first;
+ BLI_assert(first_child != NULL);
+ panel_set_flag_recursive(panel, PNL_CLOSED, !(first_child->flag & PNL_CLOSED));
+ panel->flag |= PNL_CLOSED;
}
- else if (align == BUT_HORIZONTAL) {
- block->panel->flag |= PNL_CLOSEDX;
+ }
- if (event == LEFTMOUSE) {
- ui_panel_drag_collapse_handler_add(C, true);
- }
+ if (panel->flag & PNL_CLOSED) {
+ panel->flag &= ~PNL_CLOSED;
+ /* Snap back up so full panel aligns with screen edge. */
+ if (panel->snap & PNL_SNAP_BOTTOM) {
+ panel->ofsy = 0;
}
- else {
- /* snap down to bottom screen edge */
- block->panel->flag |= PNL_CLOSEDY;
- if (block->panel->snap & PNL_SNAP_BOTTOM) {
- block->panel->ofsy = -block->panel->sizey;
- }
- if (event == LEFTMOUSE) {
- ui_panel_drag_collapse_handler_add(C, true);
- }
+ if (event_type == LEFTMOUSE) {
+ ui_panel_drag_collapse_handler_add(C, false);
}
-
- set_panels_list_data_expand_flag(C, region);
- }
-
- if (align) {
- panel_activate_state(C, block->panel, PANEL_STATE_ANIMATION);
}
else {
- /* FIXME: this doesn't update the panel drawing, assert to avoid debugging why this is.
- * We could fix this in the future if it's ever needed. */
- BLI_assert(0);
- ED_region_tag_redraw(region);
+ /* Snap down to bottom screen edge. */
+ panel->flag |= PNL_CLOSED;
+ if (panel->snap & PNL_SNAP_BOTTOM) {
+ panel->ofsy = -panel->sizey;
+ }
+
+ if (event_type == LEFTMOUSE) {
+ ui_panel_drag_collapse_handler_add(C, true);
+ }
}
+
+ set_panels_list_data_expand_flag(C, region);
+ panel_activate_state(C, panel, PANEL_STATE_ANIMATION);
+ return;
}
- else if (show_drag && BLI_rctf_isect_x(&rect_drag, mx)) {
- /* XXX, for now don't allow dragging in floating windows yet. */
- if (region->alignment == RGN_ALIGN_FLOAT) {
+
+ /* Handle panel dragging. For now don't allow dragging in floating regions. */
+ if (show_drag && !(region->alignment == RGN_ALIGN_FLOAT)) {
+ const float drag_area_xmin = block->rect.xmax - (PNL_ICON * 1.5f);
+ const float drag_area_xmax = block->rect.xmax;
+ if (IN_RANGE(mx, drag_area_xmin, drag_area_xmax)) {
+ panel_activate_state(C, panel, PANEL_STATE_DRAG);
return;
}
- panel_activate_state(C, block->panel, PANEL_STATE_DRAG);
}
- else if (show_pin && BLI_rctf_isect_x(&rect_pin, mx)) {
- block->panel->flag ^= PNL_PIN;
- ED_region_tag_redraw(region);
+
+ /* Handle panel unpinning. */
+ if (show_pin) {
+ const float pin_area_xmin = expansion_area_xmax;
+ const float pin_area_xmax = pin_area_xmin + PNL_ICON;
+ if (IN_RANGE(mx, pin_area_xmin, pin_area_xmax)) {
+ panel->flag ^= PNL_PIN;
+ ED_region_tag_redraw(region);
+ return;
+ }
}
}
@@ -2027,16 +2239,16 @@ static void ui_panel_category_active_set(ARegion *region, const char *idname, bo
}
if (fallback) {
- /* For fallbacks, add at the end so explicitly chosen categories have priority. */
+ /* For fall-backs, add at the end so explicitly chosen categories have priority. */
BLI_addtail(lb, pc_act);
}
else {
BLI_addhead(lb, pc_act);
}
- /* validate all active panels, we could do this on load,
+ /* Validate all active panels, we could do this on load,
* they are harmless - but we should remove somewhere.
- * (addons could define own and gather cruft over time) */
+ * (add-ons could define own and gather cruft over time). */
{
PanelCategoryStack *pc_act_next;
/* intentionally skip first */
@@ -2114,421 +2326,6 @@ void UI_panel_category_clear_all(ARegion *region)
BLI_freelistN(&region->panels_category);
}
-static void imm_buf_append(
- float vbuf[][2], uchar cbuf[][3], float x, float y, const uchar col[3], int *index)
-{
- ARRAY_SET_ITEMS(vbuf[*index], x, y);
- ARRAY_SET_ITEMS(cbuf[*index], UNPACK3(col));
- (*index)++;
-}
-
-/* based on UI_draw_roundbox, check on making a version which allows us to skip some sides */
-static void ui_panel_category_draw_tab(bool filled,
- float minx,
- float miny,
- float maxx,
- float maxy,
- float rad,
- const int roundboxtype,
- const bool use_highlight,
- const bool use_shadow,
- const bool use_flip_x,
- const uchar highlight_fade[3],
- const uchar col[3])
-{
- float vec[4][2] = {{0.195, 0.02}, {0.55, 0.169}, {0.831, 0.45}, {0.98, 0.805}};
- int a;
-
- /* mult */
- for (a = 0; a < 4; a++) {
- mul_v2_fl(vec[a], rad);
- }
-
- uint vert_len = 0;
- if (use_highlight) {
- vert_len += (roundboxtype & UI_CNR_TOP_RIGHT) ? 6 : 1;
- vert_len += (roundboxtype & UI_CNR_TOP_LEFT) ? 6 : 1;
- }
- if (use_highlight && !use_shadow) {
- vert_len++;
- }
- else {
- vert_len += (roundboxtype & UI_CNR_BOTTOM_RIGHT) ? 6 : 1;
- vert_len += (roundboxtype & UI_CNR_BOTTOM_LEFT) ? 6 : 1;
- }
- /* Maximum size. */
- float vbuf[24][2];
- uchar cbuf[24][3];
- int buf_index = 0;
-
- /* start with corner right-top */
- if (use_highlight) {
- if (roundboxtype & UI_CNR_TOP_RIGHT) {
- imm_buf_append(vbuf, cbuf, maxx, maxy - rad, col, &buf_index);
- for (a = 0; a < 4; a++) {
- imm_buf_append(vbuf, cbuf, maxx - vec[a][1], maxy - rad + vec[a][0], col, &buf_index);
- }
- imm_buf_append(vbuf, cbuf, maxx - rad, maxy, col, &buf_index);
- }
- else {
- imm_buf_append(vbuf, cbuf, maxx, maxy, col, &buf_index);
- }
-
- /* corner left-top */
- if (roundboxtype & UI_CNR_TOP_LEFT) {
- imm_buf_append(vbuf, cbuf, minx + rad, maxy, col, &buf_index);
- for (a = 0; a < 4; a++) {
- imm_buf_append(vbuf, cbuf, minx + rad - vec[a][0], maxy - vec[a][1], col, &buf_index);
- }
- imm_buf_append(vbuf, cbuf, minx, maxy - rad, col, &buf_index);
- }
- else {
- imm_buf_append(vbuf, cbuf, minx, maxy, col, &buf_index);
- }
- }
-
- if (use_highlight && !use_shadow) {
- imm_buf_append(
- vbuf, cbuf, minx, miny + rad, highlight_fade ? col : highlight_fade, &buf_index);
- }
- else {
- /* corner left-bottom */
- if (roundboxtype & UI_CNR_BOTTOM_LEFT) {
- imm_buf_append(vbuf, cbuf, minx, miny + rad, col, &buf_index);
- for (a = 0; a < 4; a++) {
- imm_buf_append(vbuf, cbuf, minx + vec[a][1], miny + rad - vec[a][0], col, &buf_index);
- }
- imm_buf_append(vbuf, cbuf, minx + rad, miny, col, &buf_index);
- }
- else {
- imm_buf_append(vbuf, cbuf, minx, miny, col, &buf_index);
- }
-
- /* corner right-bottom */
- if (roundboxtype & UI_CNR_BOTTOM_RIGHT) {
- imm_buf_append(vbuf, cbuf, maxx - rad, miny, col, &buf_index);
- for (a = 0; a < 4; a++) {
- imm_buf_append(vbuf, cbuf, maxx - rad + vec[a][0], miny + vec[a][1], col, &buf_index);
- }
- imm_buf_append(vbuf, cbuf, maxx, miny + rad, col, &buf_index);
- }
- else {
- imm_buf_append(vbuf, cbuf, maxx, miny, col, &buf_index);
- }
- }
-
- if (use_flip_x) {
- float midx = (minx + maxx) / 2.0f;
- for (int i = 0; i < buf_index; i++) {
- vbuf[i][0] = midx - (vbuf[i][0] - midx);
- }
- }
-
- GPUVertFormat *format = immVertexFormat();
- uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- uint color = GPU_vertformat_attr_add(
- format, "color", GPU_COMP_U8, 3, GPU_FETCH_INT_TO_FLOAT_UNIT);
-
- immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR);
- immBegin(filled ? GPU_PRIM_TRI_FAN : GPU_PRIM_LINE_STRIP, vert_len);
- for (int i = 0; i < buf_index; i++) {
- immAttr3ubv(color, cbuf[i]);
- immVertex2fv(pos, vbuf[i]);
- }
- immEnd();
- immUnbindProgram();
-}
-
-/**
- * Draw vertical tabs on the left side of the region,
- * one tab per category.
- */
-void UI_panel_category_draw_all(ARegion *region, const char *category_id_active)
-{
- /* no tab outlines for */
- // #define USE_FLAT_INACTIVE
- const bool is_left = RGN_ALIGN_ENUM_FROM_MASK(region->alignment != RGN_ALIGN_RIGHT);
- View2D *v2d = &region->v2d;
- const uiStyle *style = UI_style_get();
- const uiFontStyle *fstyle = &style->widget;
- const int fontid = fstyle->uifont_id;
- short fstyle_points = fstyle->points;
- const float aspect = ((uiBlock *)region->uiblocks.first)->aspect;
- const float zoom = 1.0f / aspect;
- const int px = max_ii(1, round_fl_to_int(U.pixelsize));
- const int px_x_sign = is_left ? px : -px;
- const int category_tabs_width = round_fl_to_int(UI_PANEL_CATEGORY_MARGIN_WIDTH * zoom);
- const float dpi_fac = UI_DPI_FAC;
- /* padding of tabs around text */
- const int tab_v_pad_text = round_fl_to_int((2 + ((px * 3) * dpi_fac)) * zoom);
- /* padding between tabs */
- const int tab_v_pad = round_fl_to_int((4 + (2 * px * dpi_fac)) * zoom);
- const float tab_curve_radius = ((px * 3) * dpi_fac) * zoom;
- /* We flip the tab drawing, so always use these flags. */
- const int roundboxtype = UI_CNR_TOP_LEFT | UI_CNR_BOTTOM_LEFT;
- bool is_alpha;
- bool do_scaletabs = false;
-#ifdef USE_FLAT_INACTIVE
- bool is_active_prev = false;
-#endif
- float scaletabs = 1.0f;
- /* same for all tabs */
- /* intentionally dont scale by 'px' */
- const int rct_xmin = is_left ? v2d->mask.xmin + 3 : (v2d->mask.xmax - category_tabs_width);
- const int rct_xmax = is_left ? v2d->mask.xmin + category_tabs_width : (v2d->mask.xmax - 3);
- const int text_v_ofs = (rct_xmax - rct_xmin) * 0.3f;
-
- int y_ofs = tab_v_pad;
-
- /* Primary theme colors */
- uchar theme_col_back[4];
- uchar theme_col_text[3];
- uchar theme_col_text_hi[3];
-
- /* Tab colors */
- uchar theme_col_tab_bg[4];
- uchar theme_col_tab_active[3];
- uchar theme_col_tab_inactive[3];
-
- /* Secondary theme colors */
- uchar theme_col_tab_outline[3];
- uchar theme_col_tab_divider[3]; /* line that divides tabs from the main region */
- uchar theme_col_tab_highlight[3];
- uchar theme_col_tab_highlight_inactive[3];
-
- UI_GetThemeColor4ubv(TH_BACK, theme_col_back);
- UI_GetThemeColor3ubv(TH_TEXT, theme_col_text);
- UI_GetThemeColor3ubv(TH_TEXT_HI, theme_col_text_hi);
-
- UI_GetThemeColor4ubv(TH_TAB_BACK, theme_col_tab_bg);
- UI_GetThemeColor3ubv(TH_TAB_ACTIVE, theme_col_tab_active);
- UI_GetThemeColor3ubv(TH_TAB_INACTIVE, theme_col_tab_inactive);
- UI_GetThemeColor3ubv(TH_TAB_OUTLINE, theme_col_tab_outline);
-
- interp_v3_v3v3_uchar(theme_col_tab_divider, theme_col_back, theme_col_tab_outline, 0.3f);
- interp_v3_v3v3_uchar(theme_col_tab_highlight, theme_col_back, theme_col_text_hi, 0.2f);
- interp_v3_v3v3_uchar(
- theme_col_tab_highlight_inactive, theme_col_tab_inactive, theme_col_text_hi, 0.12f);
-
- is_alpha = (region->overlap && (theme_col_back[3] != 255));
-
- if (fstyle->kerning == 1) {
- BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
- }
-
- BLF_enable(fontid, BLF_ROTATION);
- BLF_rotation(fontid, M_PI_2);
- // UI_fontstyle_set(&style->widget);
- ui_fontscale(&fstyle_points, aspect / (U.pixelsize * 1.1f));
- BLF_size(fontid, fstyle_points, U.dpi);
-
- /* Check the region type supports categories to avoid an assert
- * for showing 3D view panels in the properties space. */
- if ((1 << region->regiontype) & RGN_TYPE_HAS_CATEGORY_MASK) {
- BLI_assert(UI_panel_category_is_visible(region));
- }
-
- /* calculate tab rect's and check if we need to scale down */
- LISTBASE_FOREACH (PanelCategoryDyn *, pc_dyn, &region->panels_category) {
-
- rcti *rct = &pc_dyn->rect;
- const char *category_id = pc_dyn->idname;
- const char *category_id_draw = IFACE_(category_id);
- const int category_width = BLF_width(fontid, category_id_draw, BLF_DRAW_STR_DUMMY_MAX);
-
- rct->xmin = rct_xmin;
- rct->xmax = rct_xmax;
-
- rct->ymin = v2d->mask.ymax - (y_ofs + category_width + (tab_v_pad_text * 2));
- rct->ymax = v2d->mask.ymax - (y_ofs);
-
- y_ofs += category_width + tab_v_pad + (tab_v_pad_text * 2);
- }
-
- if (y_ofs > BLI_rcti_size_y(&v2d->mask)) {
- scaletabs = (float)BLI_rcti_size_y(&v2d->mask) / (float)y_ofs;
-
- LISTBASE_FOREACH (PanelCategoryDyn *, pc_dyn, &region->panels_category) {
- rcti *rct = &pc_dyn->rect;
- rct->ymin = ((rct->ymin - v2d->mask.ymax) * scaletabs) + v2d->mask.ymax;
- rct->ymax = ((rct->ymax - v2d->mask.ymax) * scaletabs) + v2d->mask.ymax;
- }
-
- do_scaletabs = true;
- }
-
- /* begin drawing */
- GPU_line_smooth(true);
-
- uint pos = GPU_vertformat_attr_add(
- immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
-
- /* draw the background */
- if (is_alpha) {
- GPU_blend(GPU_BLEND_ALPHA);
- immUniformColor4ubv(theme_col_tab_bg);
- }
- else {
- immUniformColor3ubv(theme_col_tab_bg);
- }
-
- if (is_left) {
- immRecti(
- pos, v2d->mask.xmin, v2d->mask.ymin, v2d->mask.xmin + category_tabs_width, v2d->mask.ymax);
- }
- else {
- immRecti(
- pos, v2d->mask.xmax - category_tabs_width, v2d->mask.ymin, v2d->mask.xmax, v2d->mask.ymax);
- }
-
- if (is_alpha) {
- GPU_blend(GPU_BLEND_NONE);
- }
-
- immUnbindProgram();
-
- const int divider_xmin = is_left ? (v2d->mask.xmin + (category_tabs_width - px)) :
- (v2d->mask.xmax - category_tabs_width) + px;
- const int divider_xmax = is_left ? (v2d->mask.xmin + category_tabs_width) :
- (v2d->mask.xmax - (category_tabs_width + px)) + px;
-
- LISTBASE_FOREACH (PanelCategoryDyn *, pc_dyn, &region->panels_category) {
- const rcti *rct = &pc_dyn->rect;
- const char *category_id = pc_dyn->idname;
- const char *category_id_draw = IFACE_(category_id);
- int category_width = BLI_rcti_size_y(rct) - (tab_v_pad_text * 2);
- size_t category_draw_len = BLF_DRAW_STR_DUMMY_MAX;
- // int category_width = BLF_width(fontid, category_id_draw, BLF_DRAW_STR_DUMMY_MAX);
-
- const bool is_active = STREQ(category_id, category_id_active);
-
- GPU_blend(GPU_BLEND_ALPHA);
-
-#ifdef USE_FLAT_INACTIVE
- if (is_active)
-#endif
- {
- const bool use_flip_x = !is_left;
- ui_panel_category_draw_tab(true,
- rct->xmin,
- rct->ymin,
- rct->xmax,
- rct->ymax,
- tab_curve_radius - px,
- roundboxtype,
- true,
- true,
- use_flip_x,
- NULL,
- is_active ? theme_col_tab_active : theme_col_tab_inactive);
-
- /* tab outline */
- ui_panel_category_draw_tab(false,
- rct->xmin - px_x_sign,
- rct->ymin - px,
- rct->xmax - px_x_sign,
- rct->ymax + px,
- tab_curve_radius,
- roundboxtype,
- true,
- true,
- use_flip_x,
- NULL,
- theme_col_tab_outline);
-
- /* tab highlight (3d look) */
- ui_panel_category_draw_tab(false,
- rct->xmin,
- rct->ymin,
- rct->xmax,
- rct->ymax,
- tab_curve_radius,
- roundboxtype,
- true,
- false,
- use_flip_x,
- is_active ? theme_col_back : theme_col_tab_inactive,
- is_active ? theme_col_tab_highlight :
- theme_col_tab_highlight_inactive);
- }
-
- /* tab blackline */
- if (!is_active) {
- pos = GPU_vertformat_attr_add(
- immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
-
- immUniformColor3ubv(theme_col_tab_divider);
- immRecti(pos, divider_xmin, rct->ymin - tab_v_pad, divider_xmax, rct->ymax + tab_v_pad);
- immUnbindProgram();
- }
-
- if (do_scaletabs) {
- category_draw_len = BLF_width_to_strlen(
- fontid, category_id_draw, category_draw_len, category_width, NULL);
- }
-
- BLF_position(fontid, rct->xmax - text_v_ofs, rct->ymin + tab_v_pad_text, 0.0f);
-
- /* tab titles */
-
- /* draw white shadow to give text more depth */
- BLF_color3ubv(fontid, theme_col_text);
-
- /* main tab title */
- BLF_draw(fontid, category_id_draw, category_draw_len);
-
- GPU_blend(GPU_BLEND_NONE);
-
- /* tab blackline remaining (last tab) */
- pos = GPU_vertformat_attr_add(
- immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
- if (pc_dyn->prev == NULL) {
- immUniformColor3ubv(theme_col_tab_divider);
- immRecti(pos, divider_xmin, rct->ymax + px, divider_xmax, v2d->mask.ymax);
- }
- if (pc_dyn->next == NULL) {
- immUniformColor3ubv(theme_col_tab_divider);
- immRecti(pos, divider_xmin, 0, divider_xmax, rct->ymin);
- }
-
-#ifdef USE_FLAT_INACTIVE
- /* draw line between inactive tabs */
- if (is_active == false && is_active_prev == false && pc_dyn->prev) {
- immUniformColor3ubv(theme_col_tab_divider);
- immRecti(pos,
- v2d->mask.xmin + (category_tabs_width / 5),
- rct->ymax + px,
- (v2d->mask.xmin + category_tabs_width) - (category_tabs_width / 5),
- rct->ymax + (px * 3));
- }
-
- is_active_prev = is_active;
-#endif
- immUnbindProgram();
-
- /* not essential, but allows events to be handled right up until the region edge [#38171] */
- if (is_left) {
- pc_dyn->rect.xmin = v2d->mask.xmin;
- }
- else {
- pc_dyn->rect.xmax = v2d->mask.xmax;
- }
- }
-
- GPU_line_smooth(false);
-
- BLF_disable(fontid, BLF_ROTATION);
-
- if (fstyle->kerning == 1) {
- BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
- }
-
-#undef USE_FLAT_INACTIVE
-}
-
static int ui_handle_panel_category_cycling(const wmEvent *event,
ARegion *region,
const uiBut *active_but)
@@ -2581,132 +2378,108 @@ static int ui_handle_panel_category_cycling(const wmEvent *event,
return WM_UI_HANDLER_CONTINUE;
}
-/* XXX should become modal keymap */
-/* AKey is opening/closing panels, independent of button state now */
-
+/**
+ * Handle region panel events like opening and closing panels, changing categories, etc.
+ *
+ * \note Could become a modal key-map.
+ */
int ui_handler_panel_region(bContext *C,
const wmEvent *event,
ARegion *region,
const uiBut *active_but)
{
- Panel *panel;
- int retval, mx, my;
- bool has_category_tabs = UI_panel_category_is_visible(region);
+ /* Mouse-move events are handled by separate handlers for dragging and drag collapsing. */
+ if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ return WM_UI_HANDLER_CONTINUE;
+ }
- retval = WM_UI_HANDLER_CONTINUE;
+ /* We only use KM_PRESS events in this function, so it's simpler to return early. */
+ if (event->val != KM_PRESS) {
+ return WM_UI_HANDLER_CONTINUE;
+ }
- /* Scrollbars can overlap panels now, they have handling priority. */
+ /* Scroll-bars can overlap panels now, they have handling priority. */
if (UI_view2d_mouse_in_scrollers(region, &region->v2d, event->x, event->y)) {
- return retval;
+ return WM_UI_HANDLER_CONTINUE;
}
- /* handle category tabs */
- if (has_category_tabs) {
- if (event->val == KM_PRESS) {
- if (event->type == LEFTMOUSE) {
- PanelCategoryDyn *pc_dyn = UI_panel_category_find_mouse_over(region, event);
- if (pc_dyn) {
- UI_panel_category_active_set(region, pc_dyn->idname);
- ED_region_tag_redraw(region);
+ int retval = WM_UI_HANDLER_CONTINUE;
- /* reset scroll to the top [#38348] */
- UI_view2d_offset(&region->v2d, -1.0f, 1.0f);
+ /* Handle category tabs. */
+ if (UI_panel_category_is_visible(region)) {
+ if (event->type == LEFTMOUSE) {
+ PanelCategoryDyn *pc_dyn = UI_panel_category_find_mouse_over(region, event);
+ if (pc_dyn) {
+ UI_panel_category_active_set(region, pc_dyn->idname);
+ ED_region_tag_redraw(region);
- retval = WM_UI_HANDLER_BREAK;
- }
- }
- else if ((event->type == EVT_TABKEY && event->ctrl) ||
- ELEM(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE)) {
- /* cycle tabs */
- retval = ui_handle_panel_category_cycling(event, region, active_but);
+ /* Reset scroll to the top (T38348). */
+ UI_view2d_offset(&region->v2d, -1.0f, 1.0f);
+
+ retval = WM_UI_HANDLER_BREAK;
}
}
+ else if ((event->type == EVT_TABKEY && event->ctrl) ||
+ ELEM(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE)) {
+ /* Cycle tabs. */
+ retval = ui_handle_panel_category_cycling(event, region, active_but);
+ }
}
if (retval == WM_UI_HANDLER_BREAK) {
return retval;
}
- LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
- uiPanelMouseState mouse_state;
-
- mx = event->x;
- my = event->y;
- ui_window_to_block(region, block, &mx, &my);
-
- /* checks for mouse position inside */
- panel = block->panel;
+ const bool region_has_active_button = (ui_region_find_active_but(region) != NULL);
- if (!panel) {
+ LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
+ Panel *panel = block->panel;
+ if (panel == NULL || panel->type == NULL) {
continue;
}
- /* XXX - accessed freed panels when scripts reload, need to fix. */
- if (panel->type && panel->type->flag & PNL_NO_HEADER) {
+ /* We can't expand or collapse panels without headers, they would disappear. */
+ if (panel->type->flag & PNL_NO_HEADER) {
continue;
}
- mouse_state = ui_panel_mouse_state_get(block, panel, mx, my);
-
- /* XXX hardcoded key warning */
- if (ELEM(mouse_state, PANEL_MOUSE_INSIDE_CONTENT, PANEL_MOUSE_INSIDE_HEADER) &&
- event->val == KM_PRESS) {
- if (event->type == EVT_AKEY &&
- ((event->ctrl + event->oskey + event->shift + event->alt) == 0)) {
+ int mx = event->x;
+ int my = event->y;
+ ui_window_to_block(region, block, &mx, &my);
- if (panel->flag & PNL_CLOSEDY) {
- if ((block->rect.ymax <= my) && (block->rect.ymax + PNL_HEADER >= my)) {
- ui_handle_panel_header(C, block, mx, my, event->type, event->ctrl, event->shift);
- }
- }
- else {
- ui_handle_panel_header(C, block, mx, my, event->type, event->ctrl, event->shift);
- }
+ const uiPanelMouseState mouse_state = ui_panel_mouse_state_get(block, panel, mx, my);
+ /* The panel collapse / expand key "A" is special as it takes priority over
+ * active button handling. */
+ if (ELEM(mouse_state, PANEL_MOUSE_INSIDE_CONTENT, PANEL_MOUSE_INSIDE_HEADER)) {
+ if (event->type == EVT_AKEY && !IS_EVENT_MOD(event, shift, ctrl, alt, oskey)) {
retval = WM_UI_HANDLER_BREAK;
- continue;
+ ui_handle_panel_header(C, block, mx, event->type, event->ctrl, event->shift);
+ break;
}
}
- /* on active button, do not handle panels */
- if (ui_region_find_active_but(region) != NULL) {
+ /* Don't do any other panel handling with an active button. */
+ if (region_has_active_button) {
continue;
}
- if (ELEM(mouse_state, PANEL_MOUSE_INSIDE_CONTENT, PANEL_MOUSE_INSIDE_HEADER)) {
-
- if (event->val == KM_PRESS) {
+ /* All mouse clicks inside panels should return in break, but continue handling
+ * in case there is a sub-panel header at the mouse location. */
+ if (event->type == LEFTMOUSE &&
+ ELEM(mouse_state, PANEL_MOUSE_INSIDE_CONTENT, PANEL_MOUSE_INSIDE_HEADER)) {
+ retval = WM_UI_HANDLER_BREAK;
+ }
- /* open close on header */
- if (ELEM(event->type, EVT_RETKEY, EVT_PADENTER)) {
- if (mouse_state == PANEL_MOUSE_INSIDE_HEADER) {
- ui_handle_panel_header(C, block, mx, my, EVT_RETKEY, event->ctrl, event->shift);
- retval = WM_UI_HANDLER_BREAK;
- break;
- }
- }
- else if (event->type == LEFTMOUSE) {
- /* all inside clicks should return in break - overlapping/float panels */
- retval = WM_UI_HANDLER_BREAK;
-
- if (mouse_state == PANEL_MOUSE_INSIDE_HEADER) {
- ui_handle_panel_header(C, block, mx, my, event->type, event->ctrl, event->shift);
- retval = WM_UI_HANDLER_BREAK;
- break;
- }
- if ((mouse_state == PANEL_MOUSE_INSIDE_SCALE) && !(panel->flag & PNL_CLOSED)) {
- panel_activate_state(C, panel, PANEL_STATE_DRAG_SCALE);
- retval = WM_UI_HANDLER_BREAK;
- break;
- }
- }
- else if (event->type == RIGHTMOUSE) {
- if (mouse_state == PANEL_MOUSE_INSIDE_HEADER) {
- ui_popup_context_menu_for_panel(C, region, block->panel);
- retval = WM_UI_HANDLER_BREAK;
- break;
- }
- }
+ if (mouse_state == PANEL_MOUSE_INSIDE_HEADER) {
+ if (ELEM(event->type, EVT_RETKEY, EVT_PADENTER, LEFTMOUSE)) {
+ retval = WM_UI_HANDLER_BREAK;
+ ui_handle_panel_header(C, block, mx, event->type, event->ctrl, event->shift);
+ }
+ else if (event->type == RIGHTMOUSE) {
+ retval = WM_UI_HANDLER_BREAK;
+ ui_popup_context_menu_for_panel(C, region, block->panel);
}
+ break;
}
}
@@ -2748,7 +2521,7 @@ PointerRNA *UI_region_panel_custom_data_under_cursor(const bContext *C, const wm
int mx = event->x;
int my = event->y;
ui_window_to_block(region, block, &mx, &my);
- int mouse_state = ui_panel_mouse_state_get(block, panel, mx, my);
+ const int mouse_state = ui_panel_mouse_state_get(block, panel, mx, my);
if (ELEM(mouse_state, PANEL_MOUSE_INSIDE_CONTENT, PANEL_MOUSE_INSIDE_HEADER)) {
break;
}
@@ -2763,7 +2536,11 @@ PointerRNA *UI_region_panel_custom_data_under_cursor(const bContext *C, const wm
return customdata;
}
-/**************** window level modal panel interaction **************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Window Level Modal Panel Interaction
+ * \{ */
/* note, this is modal handler and should not swallow events for animation */
static int ui_handler_panel(bContext *C, const wmEvent *event, void *userdata)
@@ -2773,16 +2550,7 @@ static int ui_handler_panel(bContext *C, const wmEvent *event, void *userdata)
/* verify if we can stop */
if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
- ScrArea *area = CTX_wm_area(C);
- ARegion *region = CTX_wm_region(C);
- int align = panel_aligned(area, region);
-
- if (align) {
- panel_activate_state(C, panel, PANEL_STATE_ANIMATION);
- }
- else {
- panel_activate_state(C, panel, PANEL_STATE_EXIT);
- }
+ panel_activate_state(C, panel, PANEL_STATE_ANIMATION);
}
else if (event->type == MOUSEMOVE) {
if (data->state == PANEL_STATE_DRAG) {
@@ -2823,7 +2591,7 @@ static void panel_activate_state(const bContext *C, Panel *panel, uiHandlePanelS
return;
}
- bool was_drag_drop = (data && data->state == PANEL_STATE_DRAG);
+ const bool was_drag_drop = (data && data->state == PANEL_STATE_DRAG);
/* Set selection state for the panel and its sub-panels, which need to know they are selected
* too so they can be drawn above their parent when it's dragged. */
@@ -2897,3 +2665,5 @@ PanelType *UI_paneltype_find(int space_id, int region_id, const char *idname)
}
return NULL;
}
+
+/** \} */
diff --git a/source/blender/editors/interface/interface_query.c b/source/blender/editors/interface/interface_query.c
index 8648a54f7d5..9dcfe8c872b 100644
--- a/source/blender/editors/interface/interface_query.c
+++ b/source/blender/editors/interface/interface_query.c
@@ -256,7 +256,7 @@ bool ui_but_contains_point_px_icon(const uiBut *but, ARegion *region, const wmEv
rect.xmax = rect.xmin + (BLI_rcti_size_y(&rect));
}
else {
- int delta = BLI_rcti_size_x(&rect) - BLI_rcti_size_y(&rect);
+ const int delta = BLI_rcti_size_x(&rect) - BLI_rcti_size_y(&rect);
rect.xmin += delta / 2;
rect.xmax -= delta / 2;
}
@@ -315,7 +315,7 @@ uiBut *ui_but_find_rect_over(const struct ARegion *region, const rcti *rect_px)
}
/* Currently no need to expose this at the moment. */
- bool labeledit = true;
+ const bool labeledit = true;
rctf rect_px_fl;
BLI_rctf_rcti_copy(&rect_px_fl, rect_px);
uiBut *butover = NULL;
diff --git a/source/blender/editors/interface/interface_region_color_picker.c b/source/blender/editors/interface/interface_region_color_picker.c
index 8bc0f18886b..de80d6270bf 100644
--- a/source/blender/editors/interface/interface_region_color_picker.c
+++ b/source/blender/editors/interface/interface_region_color_picker.c
@@ -351,7 +351,7 @@ static void ui_colorpicker_hide_reveal(uiBlock *block, enum ePickerType colormod
static void ui_colorpicker_create_mode_cb(bContext *UNUSED(C), void *bt1, void *UNUSED(arg))
{
uiBut *bt = bt1;
- short colormode = ui_but_value_get(bt);
+ const short colormode = ui_but_value_get(bt);
ui_colorpicker_hide_reveal(bt->block, colormode);
}
diff --git a/source/blender/editors/interface/interface_region_hud.c b/source/blender/editors/interface/interface_region_hud.c
index 1773a7b3057..cecfe6941fc 100644
--- a/source/blender/editors/interface/interface_region_hud.c
+++ b/source/blender/editors/interface/interface_region_hud.c
@@ -178,7 +178,7 @@ static void hud_region_layout(const bContext *C, ARegion *region)
}
ScrArea *area = CTX_wm_area(C);
- int size_y = region->sizey;
+ const int size_y = region->sizey;
ED_region_panels_layout(C, region);
@@ -201,7 +201,7 @@ static void hud_region_layout(const bContext *C, ARegion *region)
region->winrct.xmax = (region->winrct.xmin + region->winx) - 1;
region->winrct.ymax = (region->winrct.ymin + region->winy) - 1;
- UI_view2d_region_reinit(v2d, V2D_COMMONVIEW_PANELS_UI, region->winx, region->winy);
+ UI_view2d_region_reinit(v2d, V2D_COMMONVIEW_LIST, region->winx, region->winy);
/* Weak, but needed to avoid glitches, especially with hi-dpi
* (where resizing the view glitches often).
@@ -217,8 +217,7 @@ static void hud_region_draw(const bContext *C, ARegion *region)
{
UI_view2d_view_ortho(&region->v2d);
wmOrtho2_region_pixelspace(region);
- GPU_clear_color(0, 0, 0, 0.0f);
- GPU_clear(GPU_COLOR_BIT);
+ GPU_clear_color(0.0f, 0.0f, 0.0f, 0.0f);
if ((region->flag & RGN_FLAG_HIDDEN) == 0) {
ui_draw_menu_back(NULL,
@@ -317,7 +316,7 @@ void ED_area_type_hud_ensure(bContext *C, ScrArea *area)
}
bool init = false;
- bool was_hidden = region == NULL || region->visible == false;
+ const bool was_hidden = region == NULL || region->visible == false;
ARegion *region_op = CTX_wm_region(C);
BLI_assert((region_op == NULL) || (region_op->regiontype != RGN_TYPE_HUD));
if (!last_redo_poll(C, region_op ? region_op->regiontype : -1)) {
diff --git a/source/blender/editors/interface/interface_region_menu_pie.c b/source/blender/editors/interface/interface_region_menu_pie.c
index 1371c7524ae..631f395390f 100644
--- a/source/blender/editors/interface/interface_region_menu_pie.c
+++ b/source/blender/editors/interface/interface_region_menu_pie.c
@@ -387,7 +387,7 @@ void ui_pie_menu_level_create(uiBlock *block,
{
const int totitem_parent = PIE_MAX_ITEMS - 1;
const int totitem_remain = totitem - totitem_parent;
- size_t array_size = sizeof(EnumPropertyItem) * totitem_remain;
+ const size_t array_size = sizeof(EnumPropertyItem) * totitem_remain;
/* used as but->func_argN so freeing is handled elsewhere */
EnumPropertyItem *remaining = MEM_mallocN(array_size + sizeof(EnumPropertyItem),
diff --git a/source/blender/editors/interface/interface_region_popover.c b/source/blender/editors/interface/interface_region_popover.c
index 43233205877..9ef68e9e187 100644
--- a/source/blender/editors/interface/interface_region_popover.c
+++ b/source/blender/editors/interface/interface_region_popover.c
@@ -151,7 +151,7 @@ static uiBlock *ui_block_func_POPOVER(bContext *C, uiPopupBlockHandle *handle, v
UI_block_bounds_set_normal(block, block_margin);
/* If menu slides out of other menu, override direction. */
- bool slideout = ui_block_is_menu(pup->but->block);
+ const bool slideout = ui_block_is_menu(pup->but->block);
if (slideout) {
UI_block_direction_set(block, UI_DIR_RIGHT);
}
@@ -254,7 +254,7 @@ uiPopupBlockHandle *ui_popover_panel_create(
/* FIXME: maybe one day we want non panel popovers? */
{
- int ui_units_x = ((PanelType *)arg)->ui_units_x;
+ const int ui_units_x = ((PanelType *)arg)->ui_units_x;
pup->ui_size_x = U.widget_unit * (ui_units_x ? ui_units_x : UI_POPOVER_WIDTH_UNITS);
}
diff --git a/source/blender/editors/interface/interface_region_popup.c b/source/blender/editors/interface/interface_region_popup.c
index 947bdca4f9e..5445b098e5b 100644
--- a/source/blender/editors/interface/interface_region_popup.c
+++ b/source/blender/editors/interface/interface_region_popup.c
@@ -565,8 +565,8 @@ uiBlock *ui_popup_block_refresh(bContext *C,
wmWindow *window = CTX_wm_window(C);
ARegion *region = handle->region;
- uiBlockCreateFunc create_func = handle->popup_create_vars.create_func;
- uiBlockHandleCreateFunc handle_create_func = handle->popup_create_vars.handle_create_func;
+ const uiBlockCreateFunc create_func = handle->popup_create_vars.create_func;
+ const uiBlockHandleCreateFunc handle_create_func = handle->popup_create_vars.handle_create_func;
void *arg = handle->popup_create_vars.arg;
uiBlock *block_old = region->uiblocks.first;
@@ -638,7 +638,7 @@ uiBlock *ui_popup_block_refresh(bContext *C,
}
if (block->flag & UI_BLOCK_RADIAL) {
- int win_width = UI_SCREEN_MARGIN;
+ const int win_width = UI_SCREEN_MARGIN;
int winx, winy;
int x_offset = 0, y_offset = 0;
@@ -712,7 +712,7 @@ uiBlock *ui_popup_block_refresh(bContext *C,
* the same height. */
if (handle->refresh && handle->prev_block_rect.ymax > block->rect.ymax) {
if (block->bounds_type != UI_BLOCK_BOUNDS_POPUP_CENTER) {
- float offset = handle->prev_block_rect.ymax - block->rect.ymax;
+ const float offset = handle->prev_block_rect.ymax - block->rect.ymax;
UI_block_translate(block, 0, offset);
block->rect.ymin = handle->prev_block_rect.ymin;
}
diff --git a/source/blender/editors/interface/interface_region_search.c b/source/blender/editors/interface/interface_region_search.c
index adb2c1c802f..0711d953ebd 100644
--- a/source/blender/editors/interface/interface_region_search.c
+++ b/source/blender/editors/interface/interface_region_search.c
@@ -256,8 +256,8 @@ static void ui_searchbox_butrect(rcti *r_rect, uiSearchboxData *data, int itemnr
{
/* thumbnail preview */
if (data->preview) {
- int butw = (BLI_rcti_size_x(&data->bbox) - 2 * MENU_BORDER) / data->prv_cols;
- int buth = (BLI_rcti_size_y(&data->bbox) - 2 * MENU_BORDER) / data->prv_rows;
+ const int butw = (BLI_rcti_size_x(&data->bbox) - 2 * MENU_BORDER) / data->prv_cols;
+ const int buth = (BLI_rcti_size_y(&data->bbox) - 2 * MENU_BORDER) / data->prv_rows;
int row, col;
*r_rect = data->bbox;
@@ -273,7 +273,7 @@ static void ui_searchbox_butrect(rcti *r_rect, uiSearchboxData *data, int itemnr
}
/* list view */
else {
- int buth = (BLI_rcti_size_y(&data->bbox) - 2 * UI_POPUP_MENU_TOP) / SEARCH_ITEMS;
+ const int buth = (BLI_rcti_size_y(&data->bbox) - 2 * UI_POPUP_MENU_TOP) / SEARCH_ITEMS;
*r_rect = data->bbox;
r_rect->xmin = data->bbox.xmin + 3.0f;
@@ -701,7 +701,7 @@ ARegion *ui_searchbox_create_generic(bContext *C, ARegion *butregion, uiButSearc
static ARegionType type;
ARegion *region;
uiSearchboxData *data;
- float aspect = but->block->aspect;
+ const float aspect = but->block->aspect;
rctf rect_fl;
rcti rect_i;
const int margin = UI_POPUP_MARGIN;
diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c
index c324e27dff9..afeeb4cedc9 100644
--- a/source/blender/editors/interface/interface_region_tooltip.c
+++ b/source/blender/editors/interface/interface_region_tooltip.c
@@ -221,8 +221,8 @@ static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *region
/* offset to the end of the last line */
if (field->text_suffix) {
- float xofs = field->geom.x_pos;
- float yofs = data->lineh * (field->geom.lines - 1);
+ const float xofs = field->geom.x_pos;
+ const float yofs = data->lineh * (field->geom.lines - 1);
bbox.xmin += xofs;
bbox.ymax -= yofs;
@@ -535,13 +535,13 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
char *shortcut = NULL;
{
- uiStringInfo op_keymap = {BUT_GET_OP_KEYMAP, NULL};
+ const uiStringInfo op_keymap = {BUT_GET_OP_KEYMAP, NULL};
UI_but_string_info_get(C, but, &op_keymap, NULL);
shortcut = op_keymap.strinfo;
}
if (shortcut == NULL) {
- ePaintMode paint_mode = BKE_paintmode_get_active_from_context(C);
+ const ePaintMode paint_mode = BKE_paintmode_get_active_from_context(C);
const char *tool_attr = BKE_paint_get_tool_prop_id_from_paintmode(paint_mode);
if (tool_attr != NULL) {
const EnumPropertyItem *items = BKE_paint_get_tool_enum_from_paintmode(paint_mode);
@@ -768,14 +768,14 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but)
{
- uiStringInfo but_label = {BUT_GET_LABEL, NULL};
- uiStringInfo but_tip = {BUT_GET_TIP, NULL};
- uiStringInfo enum_label = {BUT_GET_RNAENUM_LABEL, NULL};
- uiStringInfo enum_tip = {BUT_GET_RNAENUM_TIP, NULL};
- uiStringInfo op_keymap = {BUT_GET_OP_KEYMAP, NULL};
- uiStringInfo prop_keymap = {BUT_GET_PROP_KEYMAP, NULL};
- uiStringInfo rna_struct = {BUT_GET_RNASTRUCT_IDENTIFIER, NULL};
- uiStringInfo rna_prop = {BUT_GET_RNAPROP_IDENTIFIER, NULL};
+ const uiStringInfo but_label = {BUT_GET_LABEL, NULL};
+ const uiStringInfo but_tip = {BUT_GET_TIP, NULL};
+ const uiStringInfo enum_label = {BUT_GET_RNAENUM_LABEL, NULL};
+ const uiStringInfo enum_tip = {BUT_GET_RNAENUM_TIP, NULL};
+ const uiStringInfo op_keymap = {BUT_GET_OP_KEYMAP, NULL};
+ const uiStringInfo prop_keymap = {BUT_GET_PROP_KEYMAP, NULL};
+ const uiStringInfo rna_struct = {BUT_GET_RNASTRUCT_IDENTIFIER, NULL};
+ const uiStringInfo rna_prop = {BUT_GET_RNAPROP_IDENTIFIER, NULL};
char buf[512];
@@ -884,7 +884,7 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but)
}
if (but->rnaprop) {
- int unit_type = UI_but_unit_type_get(but);
+ const int unit_type = UI_but_unit_type_get(but);
if (unit_type == PROP_UNIT_ROTATION) {
if (RNA_property_type(but->rnaprop) == PROP_FLOAT) {
diff --git a/source/blender/editors/interface/interface_style.c b/source/blender/editors/interface/interface_style.c
index 28279996559..b38ad9f6adb 100644
--- a/source/blender/editors/interface/interface_style.c
+++ b/source/blender/editors/interface/interface_style.c
@@ -184,7 +184,7 @@ void UI_fontstyle_draw_ex(const uiFontStyle *fs,
}
else {
/* draw from boundbox center */
- float height = BLF_ascender(fs->uifont_id) + BLF_descender(fs->uifont_id);
+ const float height = BLF_ascender(fs->uifont_id) + BLF_descender(fs->uifont_id);
yofs = ceil(0.5f * (BLI_rcti_size_y(rect) - height));
}
@@ -517,7 +517,8 @@ void uiStyleInit(void)
/* Set default flags based on UI preferences (not render fonts) */
{
- int flag_disable = (BLF_MONOCHROME | BLF_HINTING_NONE | BLF_HINTING_SLIGHT | BLF_HINTING_FULL);
+ const int flag_disable = (BLF_MONOCHROME | BLF_HINTING_NONE | BLF_HINTING_SLIGHT |
+ BLF_HINTING_FULL);
int flag_enable = 0;
if (U.text_render & USER_TEXT_HINTING_NONE) {
diff --git a/source/blender/editors/interface/interface_template_search_menu.c b/source/blender/editors/interface/interface_template_search_menu.c
index 0708714c659..d148ff70751 100644
--- a/source/blender/editors/interface/interface_template_search_menu.c
+++ b/source/blender/editors/interface/interface_template_search_menu.c
@@ -535,7 +535,7 @@ static struct MenuSearch_Data *menu_items_from_ui_create(
RNA_pointer_create(&screen->id, &RNA_Area, area, &ptr);
const int space_type_ui = RNA_property_enum_get(&ptr, prop_ui_type);
- int space_type_ui_index = RNA_enum_from_value(space_type_ui_items, space_type_ui);
+ const int space_type_ui_index = RNA_enum_from_value(space_type_ui_items, space_type_ui);
if (space_type_ui_index == -1) {
continue;
}
@@ -952,7 +952,7 @@ static void menu_search_exec_fn(bContext *C, void *UNUSED(arg1), void *arg2)
case MENU_SEARCH_TYPE_RNA: {
PointerRNA *ptr = &item->rna.ptr;
PropertyRNA *prop = item->rna.prop;
- int index = item->rna.index;
+ const int index = item->rna.index;
const int prop_type = RNA_property_type(prop);
bool changed = false;
@@ -1131,7 +1131,7 @@ void UI_but_func_menu_search(uiBut *but)
ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
/* When run from top-bar scan all areas in the current window. */
- bool include_all_areas = (area && (area->spacetype == SPACE_TOPBAR));
+ const bool include_all_areas = (area && (area->spacetype == SPACE_TOPBAR));
struct MenuSearch_Data *data = menu_items_from_ui_create(
C, win, area, region, include_all_areas);
UI_but_func_search_set(but,
diff --git a/source/blender/editors/interface/interface_template_search_operator.c b/source/blender/editors/interface/interface_template_search_operator.c
index b8070ccbb25..76a6abe22cb 100644
--- a/source/blender/editors/interface/interface_template_search_operator.c
+++ b/source/blender/editors/interface/interface_template_search_operator.c
@@ -91,7 +91,7 @@ static void operator_search_update_fn(const bContext *C,
if (index == words_len) {
if (WM_operator_poll((bContext *)C, ot)) {
char name[256];
- int len = strlen(ot_ui_name);
+ const int len = strlen(ot_ui_name);
/* display name for menu, can hold hotkey */
BLI_strncpy(name, ot_ui_name, sizeof(name));
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index 0e801c8cee2..8962755ea90 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -139,7 +139,7 @@ static void template_add_button_search_menu(const bContext *C,
const bool editable,
const bool live_icon)
{
- PointerRNA active_ptr = RNA_property_pointer_get(ptr, prop);
+ const PointerRNA active_ptr = RNA_property_pointer_get(ptr, prop);
ID *id = (active_ptr.data && RNA_struct_is_ID(active_ptr.type)) ? active_ptr.data : NULL;
const ID *idfrom = ptr->owner_id;
const StructRNA *type = active_ptr.type ? active_ptr.type : RNA_property_pointer_type(ptr, prop);
@@ -164,7 +164,7 @@ static void template_add_button_search_menu(const bContext *C,
but = uiDefBlockButN(block, block_func, block_argN, "", 0, 0, width, height, tip);
if (use_preview_icon) {
- int icon = id ? ui_id_icon_get(C, id, use_big_size) : RNA_struct_ui_icon(type);
+ const int icon = id ? ui_id_icon_get(C, id, use_big_size) : RNA_struct_ui_icon(type);
ui_def_but_icon(but, icon, UI_HAS_ICON | UI_BUT_ICON_PREVIEW);
}
else {
@@ -183,7 +183,7 @@ static void template_add_button_search_menu(const bContext *C,
but = uiDefBlockButN(block, block_func, block_argN, "", 0, 0, UI_UNIT_X * 1.6, UI_UNIT_Y, tip);
if (live_icon) {
- int icon = id ? ui_id_icon_get(C, id, false) : RNA_struct_ui_icon(type);
+ const int icon = id ? ui_id_icon_get(C, id, false) : RNA_struct_ui_icon(type);
ui_def_but_icon(but, icon, UI_HAS_ICON | UI_BUT_ICON_PREVIEW);
}
else {
@@ -395,7 +395,7 @@ static void id_search_cb(const bContext *C,
{
TemplateID *template_ui = (TemplateID *)arg_template;
ListBase *lb = template_ui->idlb;
- int flag = RNA_property_flag(template_ui->prop);
+ const int flag = RNA_property_flag(template_ui->prop);
/* ID listbase */
LISTBASE_FOREACH (ID *, id, lb) {
@@ -415,7 +415,7 @@ static void id_search_cb_tagged(const bContext *C,
{
TemplateID *template_ui = (TemplateID *)arg_template;
ListBase *lb = template_ui->idlb;
- int flag = RNA_property_flag(template_ui->prop);
+ const int flag = RNA_property_flag(template_ui->prop);
/* ID listbase */
LISTBASE_FOREACH (ID *, id, lb) {
@@ -520,7 +520,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
TemplateID *template_ui = (TemplateID *)arg_litem;
PointerRNA idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop);
ID *id = idptr.data;
- int event = POINTER_AS_INT(arg_event);
+ const int event = POINTER_AS_INT(arg_event);
const char *undo_push_label = NULL;
switch (event) {
@@ -1058,7 +1058,7 @@ static void template_ID(const bContext *C,
RNA_int_set(but->opptr, "id_type", GS(id->name));
}
else if (flag & UI_ID_OPEN) {
- int w = id ? UI_UNIT_X : (flag & UI_ID_ADD_NEW) ? UI_UNIT_X * 3 : UI_UNIT_X * 6;
+ const int w = id ? UI_UNIT_X : (flag & UI_ID_ADD_NEW) ? UI_UNIT_X * 3 : UI_UNIT_X * 6;
if (openop) {
but = uiDefIconTextButO(block,
@@ -1859,13 +1859,12 @@ static void modifier_panel_id(void *md_link, char *r_name)
void uiTemplateModifiers(uiLayout *UNUSED(layout), bContext *C)
{
- ScrArea *sa = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
Object *ob = ED_object_active_context(C);
ListBase *modifiers = &ob->modifiers;
- bool panels_match = UI_panel_list_matches_data(region, modifiers, modifier_panel_id);
+ const bool panels_match = UI_panel_list_matches_data(region, modifiers, modifier_panel_id);
if (!panels_match) {
UI_panels_free_instanced(C, region);
@@ -1883,8 +1882,7 @@ void uiTemplateModifiers(uiLayout *UNUSED(layout), bContext *C)
PointerRNA *md_ptr = MEM_mallocN(sizeof(PointerRNA), "panel customdata");
RNA_pointer_create(&ob->id, &RNA_Modifier, md, md_ptr);
- Panel *new_panel = UI_panel_add_instanced(
- sa, region, &region->panels, panel_idname, i, md_ptr);
+ Panel *new_panel = UI_panel_add_instanced(region, &region->panels, panel_idname, i, md_ptr);
if (new_panel != NULL) {
UI_panel_set_expand_from_list_data(C, new_panel);
@@ -1969,7 +1967,7 @@ static ListBase *get_constraints(const bContext *C, bool use_bone_constraints)
*/
static void constraint_reorder(bContext *C, Panel *panel, int new_index)
{
- bool constraint_from_bone = constraint_panel_is_bone(panel);
+ const bool constraint_from_bone = constraint_panel_is_bone(panel);
ListBase *lb = get_constraints(C, constraint_from_bone);
bConstraint *con = BLI_findlink(lb, panel->runtime.list_index);
@@ -1989,7 +1987,7 @@ static void constraint_reorder(bContext *C, Panel *panel, int new_index)
*/
static short get_constraint_expand_flag(const bContext *C, Panel *panel)
{
- bool constraint_from_bone = constraint_panel_is_bone(panel);
+ const bool constraint_from_bone = constraint_panel_is_bone(panel);
ListBase *lb = get_constraints(C, constraint_from_bone);
bConstraint *con = BLI_findlink(lb, panel->runtime.list_index);
@@ -2001,7 +1999,7 @@ static short get_constraint_expand_flag(const bContext *C, Panel *panel)
*/
static void set_constraint_expand_flag(const bContext *C, Panel *panel, short expand_flag)
{
- bool constraint_from_bone = constraint_panel_is_bone(panel);
+ const bool constraint_from_bone = constraint_panel_is_bone(panel);
ListBase *lb = get_constraints(C, constraint_from_bone);
bConstraint *con = BLI_findlink(lb, panel->runtime.list_index);
@@ -2037,7 +2035,6 @@ static void bone_constraint_panel_id(void *md_link, char *r_name)
*/
void uiTemplateConstraints(uiLayout *UNUSED(layout), bContext *C, bool use_bone_constraints)
{
- ScrArea *sa = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
Object *ob = ED_object_active_context(C);
@@ -2047,7 +2044,7 @@ void uiTemplateConstraints(uiLayout *UNUSED(layout), bContext *C, bool use_bone_
uiListPanelIDFromDataFunc panel_id_func = use_bone_constraints ? bone_constraint_panel_id :
object_constraint_panel_id;
- bool panels_match = UI_panel_list_matches_data(region, constraints, panel_id_func);
+ const bool panels_match = UI_panel_list_matches_data(region, constraints, panel_id_func);
if (!panels_match) {
UI_panels_free_instanced(C, region);
@@ -2060,8 +2057,7 @@ void uiTemplateConstraints(uiLayout *UNUSED(layout), bContext *C, bool use_bone_
PointerRNA *con_ptr = MEM_mallocN(sizeof(PointerRNA), "panel customdata");
RNA_pointer_create(&ob->id, &RNA_Constraint, con, con_ptr);
- Panel *new_panel = UI_panel_add_instanced(
- sa, region, &region->panels, panel_idname, i, con_ptr);
+ Panel *new_panel = UI_panel_add_instanced(region, &region->panels, panel_idname, i, con_ptr);
if (new_panel) {
/* Set the list panel functionality function pointers since we don't do it with python. */
@@ -2119,12 +2115,12 @@ static void gpencil_modifier_panel_id(void *md_link, char *r_name)
void uiTemplateGpencilModifiers(uiLayout *UNUSED(layout), bContext *C)
{
- ScrArea *sa = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
Object *ob = ED_object_active_context(C);
ListBase *modifiers = &ob->greasepencil_modifiers;
- bool panels_match = UI_panel_list_matches_data(region, modifiers, gpencil_modifier_panel_id);
+ const bool panels_match = UI_panel_list_matches_data(
+ region, modifiers, gpencil_modifier_panel_id);
if (!panels_match) {
UI_panels_free_instanced(C, region);
@@ -2142,8 +2138,7 @@ void uiTemplateGpencilModifiers(uiLayout *UNUSED(layout), bContext *C)
PointerRNA *md_ptr = MEM_mallocN(sizeof(PointerRNA), "panel customdata");
RNA_pointer_create(&ob->id, &RNA_GpencilModifier, md, md_ptr);
- Panel *new_panel = UI_panel_add_instanced(
- sa, region, &region->panels, panel_idname, i, md_ptr);
+ Panel *new_panel = UI_panel_add_instanced(region, &region->panels, panel_idname, i, md_ptr);
if (new_panel != NULL) {
UI_panel_set_expand_from_list_data(C, new_panel);
@@ -2208,12 +2203,11 @@ static void shaderfx_panel_id(void *fx_v, char *r_idname)
*/
void uiTemplateShaderFx(uiLayout *UNUSED(layout), bContext *C)
{
- ScrArea *sa = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
Object *ob = ED_object_active_context(C);
ListBase *shaderfx = &ob->shader_fx;
- bool panels_match = UI_panel_list_matches_data(region, shaderfx, shaderfx_panel_id);
+ const bool panels_match = UI_panel_list_matches_data(region, shaderfx, shaderfx_panel_id);
if (!panels_match) {
UI_panels_free_instanced(C, region);
@@ -2226,8 +2220,7 @@ void uiTemplateShaderFx(uiLayout *UNUSED(layout), bContext *C)
PointerRNA *fx_ptr = MEM_mallocN(sizeof(PointerRNA), "panel customdata");
RNA_pointer_create(&ob->id, &RNA_ShaderFx, fx, fx_ptr);
- Panel *new_panel = UI_panel_add_instanced(
- sa, region, &region->panels, panel_idname, i, fx_ptr);
+ Panel *new_panel = UI_panel_add_instanced(region, &region->panels, panel_idname, i, fx_ptr);
if (new_panel != NULL) {
UI_panel_set_expand_from_list_data(C, new_panel);
@@ -2310,7 +2303,7 @@ static eAutoPropButsReturn template_operator_property_buts_draw_single(
eAutoPropButsReturn return_info = 0;
if (!op->properties) {
- IDPropertyTemplate val = {0};
+ const IDPropertyTemplate val = {0};
op->properties = IDP_New(IDP_GROUP, &val, "wmOperatorProperties");
}
@@ -3035,8 +3028,8 @@ static void colorband_distribute_cb(bContext *C, ColorBand *coba, bool evenly)
{
if (coba->tot > 1) {
int a;
- int tot = evenly ? coba->tot - 1 : coba->tot;
- float gap = 1.0f / tot;
+ const int tot = evenly ? coba->tot - 1 : coba->tot;
+ const float gap = 1.0f / tot;
float pos = 0.0f;
for (a = 0; a < coba->tot; a++) {
coba->data[a].pos = pos;
@@ -3218,9 +3211,9 @@ static void colorband_buttons_layout(uiLayout *layout,
{
uiLayout *row, *split, *subsplit;
uiBut *bt;
- float unit = BLI_rctf_size_x(butr) / 14.0f;
- float xs = butr->xmin;
- float ys = butr->ymin;
+ const float unit = BLI_rctf_size_x(butr) / 14.0f;
+ const float xs = butr->xmin;
+ const float ys = butr->ymin;
PointerRNA ptr;
RNA_pointer_create(cb->ptr.owner_id, &RNA_ColorRamp, coba, &ptr);
@@ -3893,7 +3886,7 @@ static uiBlock *curvemap_clipping_func(bContext *C, ARegion *region, void *cumap
CurveMapping *cumap = cumap_v;
uiBlock *block;
uiBut *bt;
- float width = 8 * UI_UNIT_X;
+ const float width = 8 * UI_UNIT_X;
block = UI_block_begin(C, region, __func__, UI_EMBOSS);
UI_block_flag_enable(block, UI_BLOCK_KEEP_OPEN | UI_BLOCK_MOVEMOUSE_QUIT);
@@ -4235,7 +4228,7 @@ static void curvemap_buttons_layout(uiLayout *layout,
uiLayout *row, *sub, *split;
uiBlock *block;
uiBut *bt;
- float dx = UI_UNIT_X;
+ const float dx = UI_UNIT_X;
int icon, size;
int bg = -1, i;
@@ -4708,7 +4701,7 @@ static uiBlock *CurveProfile_tools_func(bContext *C, ARegion *region, CurveProfi
{
uiBlock *block;
short yco = 0;
- short menuwidth = 10 * UI_UNIT_X;
+ const short menuwidth = 10 * UI_UNIT_X;
block = UI_block_begin(C, region, __func__, UI_EMBOSS);
UI_block_func_butmenu_set(block, CurveProfile_tools_dofunc, profile);
@@ -5417,7 +5410,7 @@ void uiTemplatePalette(uiLayout *layout,
uiBut *but = NULL;
int row_cols = 0, col_id = 0;
- int cols_per_row = MAX2(uiLayoutGetWidth(layout) / UI_UNIT_X, 1);
+ const int cols_per_row = MAX2(uiLayoutGetWidth(layout) / UI_UNIT_X, 1);
if (!prop) {
RNA_warning("property not found: %s.%s", RNA_struct_identifier(ptr->type), propname);
@@ -5564,7 +5557,7 @@ void uiTemplateCryptoPicker(uiLayout *layout, PointerRNA *ptr, const char *propn
static void handle_layer_buttons(bContext *C, void *arg1, void *arg2)
{
uiBut *but = arg1;
- int cur = POINTER_AS_INT(arg2);
+ const int cur = POINTER_AS_INT(arg2);
wmWindow *win = CTX_wm_window(C);
int i, tot, shift = win->eventstate->shift;
@@ -5599,7 +5592,7 @@ void uiTemplateLayers(uiLayout *layout,
PropertyRNA *prop, *used_prop = NULL;
int groups, cols, layers;
int group, col, layer, row;
- int cols_per_group = 5;
+ const int cols_per_group = 5;
prop = RNA_struct_find_property(ptr, propname);
if (!prop) {
@@ -5647,7 +5640,7 @@ void uiTemplateLayers(uiLayout *layout,
/* add layers as toggle buts */
for (col = 0; (col < cols_per_group) && (layer < layers); col++, layer++) {
int icon = 0;
- int butlay = 1 << layer;
+ const int butlay = 1 << layer;
if (active_layer & butlay) {
icon = ICON_LAYER_ACTIVE;
@@ -5762,7 +5755,7 @@ static void uilist_filter_items_default(struct uiList *ui_list,
const bool filter_exclude = (ui_list->filter_flag & UILST_FLT_EXCLUDE) != 0;
const bool order_by_name = (ui_list->filter_sort_flag & UILST_FLT_SORT_MASK) ==
UILST_FLT_SORT_ALPHA;
- int len = RNA_property_collection_length(dataptr, prop);
+ const int len = RNA_property_collection_length(dataptr, prop);
dyn_data->items_shown = dyn_data->items_len = len;
@@ -5774,7 +5767,7 @@ static void uilist_filter_items_default(struct uiList *ui_list,
names = MEM_callocN(sizeof(StringCmp) * len, "StringCmp");
}
if (filter_raw[0]) {
- size_t slen = strlen(filter_raw);
+ const size_t slen = strlen(filter_raw);
dyn_data->items_filter_flags = MEM_callocN(sizeof(int) * len, "items_filter_flags");
dyn_data->items_shown = 0;
@@ -6212,8 +6205,8 @@ void uiTemplateList(uiLayout *layout,
for (i = layoutdata.start_idx; i < layoutdata.end_idx; i++) {
PointerRNA *itemptr = &items_ptr[i].item;
void *dyntip_data;
- int org_i = items_ptr[i].org_idx;
- int flt_flag = items_ptr[i].flt_flag;
+ const int org_i = items_ptr[i].org_idx;
+ const int flt_flag = items_ptr[i].flt_flag;
subblock = uiLayoutGetBlock(col);
overlap = uiLayoutOverlap(col);
@@ -6299,7 +6292,7 @@ void uiTemplateList(uiLayout *layout,
if ((dataptr->data && prop) && (dyn_data->items_shown > 0) && (activei >= 0) &&
(activei < dyn_data->items_shown)) {
PointerRNA *itemptr = &items_ptr[activei].item;
- int org_i = items_ptr[activei].org_idx;
+ const int org_i = items_ptr[activei].org_idx;
icon = UI_rnaptr_icon_get(C, itemptr, rnaicon, false);
if (icon == ICON_DOT) {
@@ -6349,8 +6342,8 @@ void uiTemplateList(uiLayout *layout,
/* create list items */
for (i = layoutdata.start_idx; i < layoutdata.end_idx; i++) {
PointerRNA *itemptr = &items_ptr[i].item;
- int org_i = items_ptr[i].org_idx;
- int flt_flag = items_ptr[i].flt_flag;
+ const int org_i = items_ptr[i].org_idx;
+ const int flt_flag = items_ptr[i].flt_flag;
/* create button */
if (!(i % columns)) {
@@ -6738,7 +6731,7 @@ void uiTemplateRunningJobs(uiLayout *layout, bContext *C)
if (owner) {
const uiFontStyle *fstyle = UI_FSTYLE_WIDGET;
- bool active = !(G.is_break || WM_jobs_is_stopped(wm, owner));
+ const bool active = !(G.is_break || WM_jobs_is_stopped(wm, owner));
uiLayout *row = uiLayoutRow(layout, false);
block = uiLayoutGetBlock(row);
@@ -7086,7 +7079,7 @@ bool uiTemplateEventFromKeymapItem(struct uiLayout *layout,
#ifdef WITH_HEADLESS
int icon = 0;
#else
- int icon = UI_icon_from_keymap_item(kmi, icon_mod);
+ const int icon = UI_icon_from_keymap_item(kmi, icon_mod);
#endif
if (icon != 0) {
for (int j = 0; j < ARRAY_SIZE(icon_mod) && icon_mod[j]; j++) {
diff --git a/source/blender/editors/interface/interface_utils.c b/source/blender/editors/interface/interface_utils.c
index f44987ac1d2..6be281cd050 100644
--- a/source/blender/editors/interface/interface_utils.c
+++ b/source/blender/editors/interface/interface_utils.c
@@ -249,7 +249,7 @@ uiBut *uiDefAutoButR(uiBlock *block,
break;
case PROP_POINTER: {
if (icon == 0) {
- PointerRNA pptr = RNA_property_pointer_get(ptr, prop);
+ const PointerRNA pptr = RNA_property_pointer_get(ptr, prop);
icon = RNA_struct_ui_icon(pptr.type ? pptr.type : RNA_property_pointer_type(ptr, prop));
}
if (icon == ICON_DOT) {
@@ -436,7 +436,7 @@ void ui_rna_collection_search_update_fn(const struct bContext *C,
int name_prefix_offset = 0;
int iconid = ICON_NONE;
bool has_sep_char = false;
- bool is_id = itemptr.type && RNA_struct_is_ID(itemptr.type);
+ const bool is_id = itemptr.type && RNA_struct_is_ID(itemptr.type);
if (is_id) {
iconid = ui_id_icon_get(C, itemptr.data, false);
diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c
index c1801290152..ab0f6d90caa 100644
--- a/source/blender/editors/interface/interface_widgets.c
+++ b/source/blender/editors/interface/interface_widgets.c
@@ -475,7 +475,7 @@ GPUBatch *ui_batch_roundbox_shadow_get(void)
uint32_t last_data;
GPUVertBufRaw vflag_step;
GPUVertBuf *vbo = GPU_vertbuf_create_with_format(vflag_format());
- int vcount = (WIDGET_SIZE_MAX + 1) * 2 + 2 + WIDGET_SIZE_MAX;
+ const int vcount = (WIDGET_SIZE_MAX + 1) * 2 + 2 + WIDGET_SIZE_MAX;
GPU_vertbuf_data_alloc(vbo, vcount);
GPU_vertbuf_attr_get_raw_data(vbo, g_ui_batch_cache.vflag_id, &vflag_step);
@@ -528,7 +528,8 @@ void UI_draw_anti_tria(
GPU_blend(GPU_BLEND_ALPHA);
- uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ const uint pos = GPU_vertformat_attr_add(
+ immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
immUniformColor4fv(draw_color);
@@ -552,12 +553,12 @@ void UI_draw_anti_tria(
void ui_draw_anti_tria_rect(const rctf *rect, char dir, const float color[4])
{
if (dir == 'h') {
- float half = 0.5f * BLI_rctf_size_y(rect);
+ const float half = 0.5f * BLI_rctf_size_y(rect);
UI_draw_anti_tria(
rect->xmin, rect->ymin, rect->xmin, rect->ymax, rect->xmax, rect->ymin + half, color);
}
else {
- float half = 0.5f * BLI_rctf_size_x(rect);
+ const float half = 0.5f * BLI_rctf_size_x(rect);
UI_draw_anti_tria(
rect->xmin, rect->ymax, rect->xmax, rect->ymax, rect->xmin + half, rect->ymin, color);
}
@@ -572,7 +573,8 @@ void UI_draw_anti_fan(float tri_array[][2], uint length, const float color[4])
GPU_blend(GPU_BLEND_ALPHA);
- uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ const uint pos = GPU_vertformat_attr_add(
+ immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
immUniformColor4fv(draw_color);
@@ -706,14 +708,14 @@ static void round_box__edges(
uiWidgetBase *wt, int roundboxalign, const rcti *rect, float rad, float radi)
{
float vec[WIDGET_CURVE_RESOLU][2], veci[WIDGET_CURVE_RESOLU][2];
- float minx = rect->xmin, miny = rect->ymin, maxx = rect->xmax, maxy = rect->ymax;
- float minxi = minx + U.pixelsize; /* boundbox inner */
- float maxxi = maxx - U.pixelsize;
- float minyi = miny + U.pixelsize;
- float maxyi = maxy - U.pixelsize;
+ const float minx = rect->xmin, miny = rect->ymin, maxx = rect->xmax, maxy = rect->ymax;
+ const float minxi = minx + U.pixelsize; /* boundbox inner */
+ const float maxxi = maxx - U.pixelsize;
+ const float minyi = miny + U.pixelsize;
+ const float maxyi = maxy - U.pixelsize;
/* for uv, can divide by zero */
- float facxi = (maxxi != minxi) ? 1.0f / (maxxi - minxi) : 0.0f;
- float facyi = (maxyi != minyi) ? 1.0f / (maxyi - minyi) : 0.0f;
+ const float facxi = (maxxi != minxi) ? 1.0f / (maxxi - minxi) : 0.0f;
+ const float facyi = (maxyi != minyi) ? 1.0f / (maxyi - minyi) : 0.0f;
int a, tot = 0, minsize;
const int hnum = ((roundboxalign & (UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT)) ==
(UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT) ||
@@ -1012,8 +1014,8 @@ static void widget_draw_vertex_buffer(uint pos,
static void shape_preset_trias_from_rect_menu(uiWidgetTrias *tria, const rcti *rect)
{
- float width = BLI_rcti_size_x(rect);
- float height = BLI_rcti_size_y(rect);
+ const float width = BLI_rcti_size_x(rect);
+ const float height = BLI_rcti_size_y(rect);
float centx, centy, size;
tria->type = ROUNDBOX_TRIA_MENU;
@@ -1096,7 +1098,8 @@ static void widgetbase_outline(uiWidgetBase *wtb, uint pos)
float triangle_strip[WIDGET_SIZE_MAX * 2 + 2][2]; /* + 2 because the last pair is wrapped */
widget_verts_to_triangle_strip(wtb, wtb->totvert, triangle_strip);
- widget_draw_vertex_buffer(pos, 0, GL_TRIANGLE_STRIP, triangle_strip, NULL, wtb->totvert * 2 + 2);
+ widget_draw_vertex_buffer(
+ pos, 0, GPU_PRIM_TRI_STRIP, triangle_strip, NULL, wtb->totvert * 2 + 2);
}
static void widgetbase_set_uniform_alpha_discard(uiWidgetBase *wtb,
@@ -1119,7 +1122,7 @@ static void widgetbase_set_uniform_alpha_check(uiWidgetBase *wtb, const bool alp
static void widgetbase_set_uniform_discard_factor(uiWidgetBase *wtb, const float discard_factor)
{
- bool alpha_check = wtb->uniform_params.alpha_discard < 0.0f;
+ const bool alpha_check = wtb->uniform_params.alpha_discard < 0.0f;
widgetbase_set_uniform_alpha_discard(wtb, alpha_check, discard_factor);
}
@@ -1343,8 +1346,8 @@ static void widget_draw_preview(BIFIconID icon, float alpha, const rcti *rect)
size -= PREVIEW_PAD * 2; /* padding */
if (size > 0) {
- int x = rect->xmin + w / 2 - size / 2;
- int y = rect->ymin + h / 2 - size / 2;
+ const int x = rect->xmin + w / 2 - size / 2;
+ const int y = rect->ymin + h / 2 - size / 2;
UI_icon_draw_preview(x, y, icon, 1.0f, alpha, size);
}
@@ -1405,7 +1408,7 @@ static void widget_draw_icon(
GPU_blend(GPU_BLEND_ALPHA);
if (icon && icon != ICON_BLANK1) {
- float ofs = 1.0f / aspect;
+ const float ofs = 1.0f / aspect;
if (but->drawflag & UI_BUT_ICON_LEFT) {
/* special case - icon_only pie buttons */
@@ -1433,7 +1436,7 @@ static void widget_draw_icon(
/* Get theme color. */
uchar color[4] = {mono_color[0], mono_color[1], mono_color[2], mono_color[3]};
- bool has_theme = UI_icon_get_theme_color(icon, color);
+ const bool has_theme = UI_icon_get_theme_color(icon, color);
/* to indicate draggable */
if (but->dragpoin && (but->flag & UI_ACTIVE)) {
@@ -1485,7 +1488,7 @@ static void widget_draw_submenu_tria(const uiBut *but,
static void ui_text_clip_give_prev_off(uiBut *but, const char *str)
{
const char *prev_utf8 = BLI_str_find_prev_char_utf8(str, str + but->ofs);
- int bytes = str + but->ofs - prev_utf8;
+ const int bytes = str + but->ofs - prev_utf8;
but->ofs -= bytes;
}
@@ -1493,7 +1496,7 @@ static void ui_text_clip_give_prev_off(uiBut *but, const char *str)
static void ui_text_clip_give_next_off(uiBut *but, const char *str)
{
const char *next_utf8 = BLI_str_find_next_char_utf8(str + but->ofs, NULL);
- int bytes = next_utf8 - (str + but->ofs);
+ const int bytes = next_utf8 - (str + but->ofs);
but->ofs += bytes;
}
@@ -1822,7 +1825,7 @@ static void ui_text_clip_right_label(const uiFontStyle *fstyle, uiBut *but, cons
/* chop off the leading text, starting from the right */
while (but->strwidth > okwidth && cp2 > but->drawstr) {
const char *prev_utf8 = BLI_str_find_prev_char_utf8(but->drawstr, cp2);
- int bytes = cp2 - prev_utf8;
+ const int bytes = cp2 - prev_utf8;
/* shift the text after and including cp2 back by 1 char,
* +1 to include null terminator */
@@ -2285,7 +2288,7 @@ static void widget_draw_text_icon(const uiFontStyle *fstyle,
rcti *rect)
{
const bool show_menu_icon = ui_but_draw_menu_icon(but);
- float alpha = (float)wcol->text[3] / 255.0f;
+ const float alpha = (float)wcol->text[3] / 255.0f;
char password_str[UI_MAX_DRAW_STR];
bool no_text_padding = but->drawflag & UI_BUT_NO_TEXT_PADDING;
@@ -2353,7 +2356,7 @@ static void widget_draw_text_icon(const uiFontStyle *fstyle,
#endif
const BIFIconID icon = ui_but_icon(but);
- int icon_size_init = is_tool ? ICON_DEFAULT_HEIGHT_TOOLBAR : ICON_DEFAULT_HEIGHT;
+ const int icon_size_init = is_tool ? ICON_DEFAULT_HEIGHT_TOOLBAR : ICON_DEFAULT_HEIGHT;
const float icon_size = icon_size_init / (but->block->aspect * U.inv_dpi_fac);
const float icon_padding = 2 * UI_DPI_FAC;
@@ -2398,7 +2401,7 @@ static void widget_draw_text_icon(const uiFontStyle *fstyle,
rect->xmin += text_padding;
}
else if (but->flag & UI_BUT_DRAG_MULTI) {
- bool text_is_edited = ui_but_drag_multi_edit_get(but) != NULL;
+ const bool text_is_edited = ui_but_drag_multi_edit_get(but) != NULL;
if (text_is_edited || (but->drawflag & UI_BUT_TEXT_LEFT)) {
rect->xmin += text_padding;
}
@@ -2486,7 +2489,7 @@ static void ui_widget_color_disabled(uiWidgetType *wt)
static void widget_active_color(uiWidgetColors *wcol)
{
- bool dark = (rgb_to_grayscale_byte(wcol->text) > rgb_to_grayscale_byte(wcol->inner));
+ const bool dark = (rgb_to_grayscale_byte(wcol->text) > rgb_to_grayscale_byte(wcol->inner));
color_mul_hsl_v3(wcol->inner, 1.0f, 1.15f, dark ? 1.2f : 1.1f);
color_mul_hsl_v3(wcol->outline, 1.0f, 1.15f, 1.15f);
color_mul_hsl_v3(wcol->text, 1.0f, 1.15f, dark ? 1.25f : 0.8f);
@@ -2745,12 +2748,13 @@ static void widget_softshadow(const rcti *rect, int roundboxalign, const float r
/* we draw a number of increasing size alpha quad strips */
alphastep = 3.0f * btheme->tui.menu_shadow_fac / radout;
- uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ const uint pos = GPU_vertformat_attr_add(
+ immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
for (step = 1; step <= (int)radout; step++) {
- float expfac = sqrtf(step / radout);
+ const float expfac = sqrtf(step / radout);
round_box_shadow_edges(wtb.outer_v, &rect1, radin, UI_CNR_ALL, (float)step);
@@ -2758,7 +2762,7 @@ static void widget_softshadow(const rcti *rect, int roundboxalign, const float r
widget_verts_to_triangle_strip(&wtb, totvert, triangle_strip);
- widget_draw_vertex_buffer(pos, 0, GL_TRIANGLE_STRIP, triangle_strip, NULL, totvert * 2);
+ widget_draw_vertex_buffer(pos, 0, GPU_PRIM_TRI_STRIP, triangle_strip, NULL, totvert * 2);
}
immUnbindProgram();
@@ -2797,7 +2801,8 @@ static void widget_menu_back(uiWidgetColors *wcol, rcti *rect, int flag, int dir
static void ui_hsv_cursor(float x, float y)
{
- uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ const uint pos = GPU_vertformat_attr_add(
+ immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
@@ -2860,11 +2865,11 @@ static void ui_draw_but_HSVCIRCLE(uiBut *but, const uiWidgetColors *wcol, const
const float radstep = 2.0f * (float)M_PI / (float)tot;
const float centx = BLI_rcti_cent_x_fl(rect);
const float centy = BLI_rcti_cent_y_fl(rect);
- float radius = (float)min_ii(BLI_rcti_size_x(rect), BLI_rcti_size_y(rect)) / 2.0f;
+ const float radius = (float)min_ii(BLI_rcti_size_x(rect), BLI_rcti_size_y(rect)) / 2.0f;
ColorPicker *cpicker = but->custom_data;
float rgb[3], hsv[3], rgb_center[3];
- bool is_color_gamma = ui_but_is_color_gamma(but);
+ const bool is_color_gamma = ui_but_is_color_gamma(but);
/* Initialize for compatibility. */
copy_v3_v3(hsv, cpicker->color_data);
@@ -2898,7 +2903,7 @@ static void ui_draw_but_HSVCIRCLE(uiBut *but, const uiWidgetColors *wcol, const
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ const uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR);
@@ -2908,8 +2913,8 @@ static void ui_draw_but_HSVCIRCLE(uiBut *but, const uiWidgetColors *wcol, const
float ang = 0.0f;
for (int a = 0; a <= tot; a++, ang += radstep) {
- float si = sinf(ang);
- float co = cosf(ang);
+ const float si = sinf(ang);
+ const float co = cosf(ang);
float hsv_ang[3];
float rgb_ang[3];
@@ -2974,7 +2979,7 @@ void ui_draw_gradient(const rcti *rect,
const int steps = 48;
const float color_step = 1.0f / steps;
int a;
- float h = hsv[0], s = hsv[1], v = hsv[2];
+ const float h = hsv[0], s = hsv[1], v = hsv[2];
float dx, dy, sx1, sx2, sy;
float col0[4][3]; /* left half, rect bottom to top */
float col1[4][3]; /* right half, rect bottom to top */
@@ -3029,8 +3034,8 @@ void ui_draw_gradient(const rcti *rect,
/* old below */
GPUVertFormat *format = immVertexFormat();
- uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+ const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ const uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR);
immBegin(GPU_PRIM_TRIS, steps * 3 * 6);
@@ -3192,7 +3197,8 @@ static void ui_draw_but_HSVCUBE(uiBut *but, const rcti *rect)
ui_hsv_cursor(x, y);
/* outline */
- uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ const uint pos = GPU_vertformat_attr_add(
+ immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
immUniformColor3ub(0, 0, 0);
imm_draw_box_wire_2d(pos, (rect->xmin), (rect->ymin), (rect->xmax), (rect->ymax));
@@ -3223,7 +3229,7 @@ static void ui_draw_but_HSV_v(uiBut *but, const rcti *rect)
/* map v from property range to [0,1] */
if (hsv_but->gradient_type == UI_GRAD_V_ALT) {
- float min = but->softmin, max = but->softmax;
+ const float min = but->softmin, max = but->softmax;
v = (v - min) / (max - min);
}
@@ -3258,7 +3264,7 @@ static void ui_draw_but_HSV_v(uiBut *but, const rcti *rect)
/** Separator for menus. */
static void ui_draw_separator(const rcti *rect, const uiWidgetColors *wcol)
{
- int y = rect->ymin + BLI_rcti_size_y(rect) / 2 - 1;
+ const int y = rect->ymin + BLI_rcti_size_y(rect) / 2 - 1;
const uchar col[4] = {
wcol->text[0],
wcol->text[1],
@@ -3266,7 +3272,8 @@ static void ui_draw_separator(const rcti *rect, const uiWidgetColors *wcol)
30,
};
- uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ const uint pos = GPU_vertformat_attr_add(
+ immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
GPU_blend(GPU_BLEND_ALPHA);
@@ -3618,8 +3625,8 @@ static void widget_progressbar(
widget_init(&wtb_bar);
/* round corners */
- float value = but_progressbar->progress;
- float offs = wcol->roundness * BLI_rcti_size_y(&rect_prog);
+ const float value = but_progressbar->progress;
+ const float offs = wcol->roundness * BLI_rcti_size_y(&rect_prog);
float w = value * BLI_rcti_size_x(&rect_prog);
/* ensure minimium size */
@@ -3646,7 +3653,7 @@ static void widget_nodesocket(
uiBut *but, uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int UNUSED(roundboxalign))
{
uiWidgetBase wtb;
- int radi = 5;
+ const int radi = 5;
uchar old_inner[3], old_outline[3];
widget_init(&wtb);
@@ -3662,8 +3669,8 @@ static void widget_nodesocket(
wcol->outline[2] = 0;
wcol->outline[3] = 150;
- int cent_x = BLI_rcti_cent_x(rect);
- int cent_y = BLI_rcti_cent_y(rect);
+ const int cent_x = BLI_rcti_cent_x(rect);
+ const int cent_y = BLI_rcti_cent_y(rect);
rect->xmin = cent_x - radi;
rect->xmax = cent_x + radi;
rect->ymin = cent_y - radi;
@@ -3711,7 +3718,7 @@ static void widget_numslider(
rect1 = *rect;
float factor, factor_ui;
float factor_discard = 1.0f; /* No discard. */
- float value = (float)ui_but_value_get(but);
+ const float value = (float)ui_but_value_get(but);
if (but->rnaprop && (RNA_property_subtype(but->rnaprop) == PROP_PERCENTAGE)) {
factor = value / but->softmax;
@@ -3720,7 +3727,7 @@ static void widget_numslider(
factor = (value - but->softmin) / (but->softmax - but->softmin);
}
- float width = (float)BLI_rcti_size_x(rect);
+ const float width = (float)BLI_rcti_size_x(rect);
factor_ui = factor * width;
if (factor_ui <= offs) {
@@ -3828,8 +3835,8 @@ static void widget_swatch(
widgetbase_draw_ex(&wtb, wcol, show_alpha_checkers);
if (color_but->is_pallete_color &&
((Palette *)but->rnapoin.owner_id)->active_color == color_but->palette_color_index) {
- float width = rect->xmax - rect->xmin;
- float height = rect->ymax - rect->ymin;
+ const float width = rect->xmax - rect->xmin;
+ const float height = rect->ymax - rect->ymin;
/* find color luminance and change it slightly */
float bw = rgb_to_grayscale(col);
@@ -3840,7 +3847,8 @@ static void widget_swatch(
UI_widgetbase_draw_cache_flush();
GPU_blend(GPU_BLEND_NONE);
- uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ const uint pos = GPU_vertformat_attr_add(
+ immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
immUniformColor3f(bw, bw, bw);
@@ -3968,7 +3976,7 @@ static void widget_menu_radial_itembut(
{
uiWidgetBase wtb;
float rad;
- float fac = but->block->pie_data.alphafac;
+ const float fac = but->block->pie_data.alphafac;
widget_init(&wtb);
@@ -4010,7 +4018,7 @@ static void widget_optionbut(uiWidgetColors *wcol,
int state,
int UNUSED(roundboxalign))
{
- bool text_before_widget = (state & UI_STATE_TEXT_BEFORE_WIDGET);
+ const bool text_before_widget = (state & UI_STATE_TEXT_BEFORE_WIDGET);
uiWidgetBase wtb;
rcti recttemp = *rect;
float rad;
@@ -4232,7 +4240,8 @@ static void widget_draw_extra_mask(const bContext *C, uiBut *but, uiWidgetType *
but->block->drawextra(
C, but->poin, but->block->drawextra_arg1, but->block->drawextra_arg2, rect);
- uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ const uint pos = GPU_vertformat_attr_add(
+ immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
/* make mask to draw over image */
@@ -4474,7 +4483,7 @@ static int widget_roundbox_set(uiBut *but, rcti *rect)
/* align with open menu */
if (but->active && (but->type != UI_BTYPE_POPOVER) && !ui_but_menu_draw_as_popover(but)) {
- int direction = ui_but_menu_direction(but);
+ const int direction = ui_but_menu_direction(but);
if (direction == UI_DIR_UP) {
roundbox &= ~(UI_CNR_TOP_RIGHT | UI_CNR_TOP_LEFT);
@@ -4922,7 +4931,8 @@ static void ui_draw_popover_back_impl(const uiWidgetColors *wcol,
/* Draw popover arrow (top/bottom) */
if (ELEM(direction, UI_DIR_UP, UI_DIR_DOWN)) {
- uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ const uint pos = GPU_vertformat_attr_add(
+ immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
const bool is_down = (direction == UI_DIR_DOWN);
@@ -5043,18 +5053,18 @@ static void draw_disk_shaded(float start,
void ui_draw_pie_center(uiBlock *block)
{
bTheme *btheme = UI_GetTheme();
- float cx = block->pie_data.pie_center_spawned[0];
- float cy = block->pie_data.pie_center_spawned[1];
+ const float cx = block->pie_data.pie_center_spawned[0];
+ const float cy = block->pie_data.pie_center_spawned[1];
float *pie_dir = block->pie_data.pie_dir;
- float pie_radius_internal = U.dpi_fac * U.pie_menu_threshold;
- float pie_radius_external = U.dpi_fac * (U.pie_menu_threshold + 7.0f);
+ const float pie_radius_internal = U.dpi_fac * U.pie_menu_threshold;
+ const float pie_radius_external = U.dpi_fac * (U.pie_menu_threshold + 7.0f);
- int subd = 40;
+ const int subd = 40;
- float angle = atan2f(pie_dir[1], pie_dir[0]);
- float range = (block->pie_data.flags & UI_PIE_DEGREES_RANGE_LARGE) ? M_PI_2 : M_PI_4;
+ const float angle = atan2f(pie_dir[1], pie_dir[0]);
+ const float range = (block->pie_data.flags & UI_PIE_DEGREES_RANGE_LARGE) ? M_PI_2 : M_PI_4;
GPU_matrix_push();
GPU_matrix_translate_2f(cx, cy);
@@ -5117,7 +5127,7 @@ void ui_draw_pie_center(uiBlock *block)
}
GPUVertFormat *format = immVertexFormat();
- uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
immUniformColor4ubv(btheme->tui.wcol_pie_menu.outline);
@@ -5128,8 +5138,9 @@ void ui_draw_pie_center(uiBlock *block)
if (U.pie_menu_confirm > 0 &&
!(block->pie_data.flags & (UI_PIE_INVALID_DIR | UI_PIE_CLICK_STYLE))) {
- float pie_confirm_radius = U.dpi_fac * (pie_radius_internal + U.pie_menu_confirm);
- float pie_confirm_external = U.dpi_fac * (pie_radius_internal + U.pie_menu_confirm + 7.0f);
+ const float pie_confirm_radius = U.dpi_fac * (pie_radius_internal + U.pie_menu_confirm);
+ const float pie_confirm_external = U.dpi_fac *
+ (pie_radius_internal + U.pie_menu_confirm + 7.0f);
const uchar col[4] = {UNPACK3(btheme->tui.wcol_pie_menu.text_sel), 64};
draw_disk_shaded(angle - range / 2.0f,
@@ -5212,7 +5223,7 @@ void ui_draw_menu_item(const uiFontStyle *fstyle,
int *r_xmax)
{
uiWidgetType *wt = widget_type(UI_WTYPE_MENU_ITEM);
- rcti _rect = *rect;
+ const rcti _rect = *rect;
char *cpoin = NULL;
wt->state(wt, state, 0);
@@ -5282,8 +5293,8 @@ void ui_draw_menu_item(const uiFontStyle *fstyle,
if (iconid) {
float height, aspect;
- int xs = rect->xmin + 0.2f * UI_UNIT_X;
- int ys = rect->ymin + 0.1f * BLI_rcti_size_y(rect);
+ const int xs = rect->xmin + 0.2f * UI_UNIT_X;
+ const int ys = rect->ymin + 0.1f * BLI_rcti_size_y(rect);
height = ICON_SIZE_FROM_BUTRECT(rect);
aspect = ICON_DEFAULT_HEIGHT / height;
diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c
index 84fe3e13426..87474369e8d 100644
--- a/source/blender/editors/interface/resources.c
+++ b/source/blender/editors/interface/resources.c
@@ -774,9 +774,6 @@ const uchar *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colorid)
cp = ts->metadatatext;
break;
- case TH_UV_OTHERS:
- cp = ts->uv_others;
- break;
case TH_UV_SHADOW:
cp = ts->uv_shadow;
break;
@@ -1474,13 +1471,6 @@ void UI_ThemeClearColor(int colorid)
GPU_clear_color(col[0], col[1], col[2], 1.0f);
}
-void UI_ThemeClearColorAlpha(int colorid, float alpha)
-{
- float col[3];
- UI_GetThemeColor3fv(colorid, col);
- GPU_clear_color(col[0], col[1], col[2], alpha);
-}
-
int UI_ThemeMenuShadowWidth(void)
{
bTheme *btheme = UI_GetTheme();
diff --git a/source/blender/editors/interface/view2d.c b/source/blender/editors/interface/view2d.c
index d4f81a89bc3..7651989c2df 100644
--- a/source/blender/editors/interface/view2d.c
+++ b/source/blender/editors/interface/view2d.c
@@ -351,7 +351,7 @@ void UI_view2d_region_reinit(View2D *v2d, short type, int winx, int winy)
v2d->scroll |= (V2D_SCROLL_HORIZONTAL_HIDE | V2D_SCROLL_VERTICAL_HIDE);
if (do_init) {
- float panelzoom = (style) ? style->panelzoom : 1.0f;
+ const float panelzoom = (style) ? style->panelzoom : 1.0f;
v2d->tot.xmin = 0.0f;
v2d->tot.xmax = winx;
@@ -566,7 +566,7 @@ static void ui_view2d_curRect_validate_resize(View2D *v2d, bool resize)
* - width is not adjusted for changed ratios here.
*/
if (winx < v2d->oldwinx) {
- float temp = v2d->oldwinx - winx;
+ const float temp = v2d->oldwinx - winx;
cur->xmin -= temp;
cur->xmax -= temp;
@@ -588,7 +588,7 @@ static void ui_view2d_curRect_validate_resize(View2D *v2d, bool resize)
*/
if (winy < v2d->oldwiny) {
- float temp = v2d->oldwiny - winy;
+ const float temp = v2d->oldwiny - winy;
if (v2d->align & V2D_ALIGN_NO_NEG_Y) {
cur->ymin -= temp;
@@ -1121,14 +1121,14 @@ static void view2d_map_cur_using_mask(const View2D *v2d, rctf *r_curmasked)
*r_curmasked = v2d->cur;
if (view2d_scroll_mapped(v2d->scroll)) {
- float sizex = BLI_rcti_size_x(&v2d->mask);
- float sizey = BLI_rcti_size_y(&v2d->mask);
+ const float sizex = BLI_rcti_size_x(&v2d->mask);
+ const float sizey = BLI_rcti_size_y(&v2d->mask);
/* prevent tiny or narrow regions to get
* invalid coordinates - mask can get negative even... */
if (sizex > 0.0f && sizey > 0.0f) {
- float dx = BLI_rctf_size_x(&v2d->cur) / (sizex + 1);
- float dy = BLI_rctf_size_y(&v2d->cur) / (sizey + 1);
+ const float dx = BLI_rctf_size_x(&v2d->cur) / (sizex + 1);
+ const float dy = BLI_rctf_size_y(&v2d->cur) / (sizey + 1);
if (v2d->mask.xmin != 0) {
r_curmasked->xmin -= dx * (float)v2d->mask.xmin;
@@ -1225,8 +1225,8 @@ void UI_view2d_view_orthoSpecial(ARegion *region, View2D *v2d, const bool xaxis)
void UI_view2d_view_restore(const bContext *C)
{
ARegion *region = CTX_wm_region(C);
- int width = BLI_rcti_size_x(&region->winrct) + 1;
- int height = BLI_rcti_size_y(&region->winrct) + 1;
+ const int width = BLI_rcti_size_x(&region->winrct) + 1;
+ const int height = BLI_rcti_size_y(&region->winrct) + 1;
wmOrtho2(0.0f, (float)width, 0.0f, (float)height);
GPU_matrix_identity_set();
@@ -1278,8 +1278,8 @@ void UI_view2d_constant_grid_draw(const View2D *v2d, float step)
if (count_x > 0 || count_y > 0) {
GPUVertFormat *format = immVertexFormat();
- uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ const uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
float theme_color[3];
UI_GetThemeColorShade3fv(TH_BACK, -10, theme_color);
@@ -1331,7 +1331,7 @@ void UI_view2d_multi_grid_draw(
vertex_count += 2 * ((int)((v2d->cur.ymax - v2d->cur.ymin) / lstep) + 1);
GPUVertFormat *format = immVertexFormat();
- uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
uint color = GPU_vertformat_attr_add(
format, "color", GPU_COMP_U8, 3, GPU_FETCH_INT_TO_FLOAT_UNIT);
@@ -1428,7 +1428,7 @@ void UI_view2d_scrollers_calc(View2D *v2d,
{
rcti vert, hor;
float fac1, fac2, totsize, scrollsize;
- int scroll = view2d_scroll_mapped(v2d->scroll);
+ const int scroll = view2d_scroll_mapped(v2d->scroll);
int smaller;
/* Always update before drawing (for dynamically sized scrollers). */
@@ -1910,7 +1910,7 @@ View2D *UI_view2d_fromcontext_rwin(const bContext *C)
* disabled. */
void UI_view2d_scroller_size_get(const View2D *v2d, float *r_x, float *r_y)
{
- int scroll = view2d_scroll_mapped(v2d->scroll);
+ const int scroll = view2d_scroll_mapped(v2d->scroll);
if (r_x) {
if (scroll & V2D_SCROLL_VERTICAL) {
@@ -2128,7 +2128,7 @@ void UI_view2d_text_cache_add(
BLI_assert(str_len == strlen(str));
if (UI_view2d_view_to_region_clip(v2d, x, y, &mval[0], &mval[1])) {
- int alloc_len = str_len + 1;
+ const int alloc_len = str_len + 1;
View2DString *v2s;
if (g_v2d_strings_arena == NULL) {
@@ -2159,7 +2159,7 @@ void UI_view2d_text_cache_add_rectf(
BLI_assert(str_len == strlen(str));
if (UI_view2d_view_to_region_rcti_clip(v2d, rect_view, &rect)) {
- int alloc_len = str_len + 1;
+ const int alloc_len = str_len + 1;
View2DString *v2s;
if (g_v2d_strings_arena == NULL) {
diff --git a/source/blender/editors/interface/view2d_draw.c b/source/blender/editors/interface/view2d_draw.c
index 54b25939baf..5801b7cdbdb 100644
--- a/source/blender/editors/interface/view2d_draw.c
+++ b/source/blender/editors/interface/view2d_draw.c
@@ -67,10 +67,10 @@ static float select_major_distance(const float *possible_distances,
return possible_distances[0];
}
- float pixels_per_view_unit = pixel_width / view_width;
+ const float pixels_per_view_unit = pixel_width / view_width;
for (uint i = 0; i < amount; i++) {
- float distance = possible_distances[i];
+ const float distance = possible_distances[i];
if (pixels_per_view_unit * distance >= MIN_MAJOR_LINE_DISTANCE) {
return distance;
}
@@ -111,7 +111,7 @@ static float view2d_major_step_y__continuous(const View2D *v2d)
static float view2d_major_step_x__time(const View2D *v2d, const Scene *scene)
{
- double fps = FPS;
+ const double fps = FPS;
float *possible_distances = NULL;
BLI_array_staticdeclare(possible_distances, 32);
@@ -207,7 +207,7 @@ static void draw_parallel_lines(const ParallelLinesSet *lines,
}
GPUVertFormat *format = immVertexFormat();
- uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
if (U.pixelsize > 1.0f) {
float viewport[4];
@@ -227,14 +227,14 @@ static void draw_parallel_lines(const ParallelLinesSet *lines,
if (direction == 'v') {
for (uint i = 0; i < steps; i++) {
- float xpos = first + i * lines->distance;
+ const float xpos = first + i * lines->distance;
immVertex2f(pos, xpos, rect->ymin);
immVertex2f(pos, xpos, rect->ymax);
}
}
else {
for (uint i = 0; i < steps; i++) {
- float ypos = first + i * lines->distance;
+ const float ypos = first + i * lines->distance;
immVertex2f(pos, rect->xmin, ypos);
immVertex2f(pos, rect->xmax, ypos);
}
@@ -322,16 +322,16 @@ static void draw_horizontal_scale_indicators(const ARegion *region,
BLF_batch_draw_begin();
- float ypos = rect->ymin + 4 * UI_DPI_FAC;
- float xmin = rect->xmin;
- float xmax = rect->xmax;
+ const float ypos = rect->ymin + 4 * UI_DPI_FAC;
+ const float xmin = rect->xmin;
+ const float xmax = rect->xmax;
for (uint i = 0; i < steps; i++) {
- float xpos_view = start + i * distance;
- float xpos_region = UI_view2d_view_to_region_x(v2d, xpos_view);
+ const float xpos_view = start + i * distance;
+ const float xpos_region = UI_view2d_view_to_region_x(v2d, xpos_view);
char text[32];
to_string(to_string_data, xpos_view, distance, sizeof(text), text);
- float text_width = BLF_width(font_id, text, strlen(text));
+ const float text_width = BLF_width(font_id, text, strlen(text));
if (xpos_region - text_width / 2.0f >= xmin && xpos_region + text_width / 2.0f <= xmax) {
BLF_draw_default_ascii(xpos_region - text_width / 2.0f, ypos, 0.0f, text, sizeof(text));
@@ -384,16 +384,16 @@ static void draw_vertical_scale_indicators(const ARegion *region,
BLF_batch_draw_begin();
- float xpos = rect->xmax - 2.0f * UI_DPI_FAC;
- float ymin = rect->ymin;
- float ymax = rect->ymax;
+ const float xpos = rect->xmax - 2.0f * UI_DPI_FAC;
+ const float ymin = rect->ymin;
+ const float ymax = rect->ymax;
for (uint i = 0; i < steps; i++) {
- float ypos_view = start + i * distance;
- float ypos_region = UI_view2d_view_to_region_y(v2d, ypos_view + display_offset);
+ const float ypos_view = start + i * distance;
+ const float ypos_region = UI_view2d_view_to_region_y(v2d, ypos_view + display_offset);
char text[32];
to_string(to_string_data, ypos_view, distance, sizeof(text), text);
- float text_width = BLF_width(font_id, text, strlen(text));
+ const float text_width = BLF_width(font_id, text, strlen(text));
if (ypos_region - text_width / 2.0f >= ymin && ypos_region + text_width / 2.0f <= ymax) {
BLF_draw_default_ascii(xpos, ypos_region - text_width / 2.0f, 0.0f, text, sizeof(text));
@@ -417,7 +417,7 @@ static void view_to_string__time(
{
const Scene *scene = (const Scene *)user_data;
- int brevity_level = 0;
+ const int brevity_level = 0;
BLI_timecode_string_from_time(
r_str, max_len, brevity_level, v2d_pos / (float)FPS, FPS, U.timecode_style);
}
@@ -462,25 +462,25 @@ float UI_view2d_grid_resolution_y__values(const struct View2D *v2d)
void UI_view2d_draw_lines_x__discrete_values(const View2D *v2d)
{
- uint major_line_distance = view2d_major_step_x__discrete(v2d);
+ const uint major_line_distance = view2d_major_step_x__discrete(v2d);
view2d_draw_lines(v2d, major_line_distance, major_line_distance > 1, 'v');
}
void UI_view2d_draw_lines_x__values(const View2D *v2d)
{
- float major_line_distance = view2d_major_step_x__continuous(v2d);
+ const float major_line_distance = view2d_major_step_x__continuous(v2d);
view2d_draw_lines(v2d, major_line_distance, true, 'v');
}
void UI_view2d_draw_lines_y__values(const View2D *v2d)
{
- float major_line_distance = view2d_major_step_y__continuous(v2d);
+ const float major_line_distance = view2d_major_step_y__continuous(v2d);
view2d_draw_lines(v2d, major_line_distance, true, 'h');
}
void UI_view2d_draw_lines_x__discrete_time(const View2D *v2d, const Scene *scene)
{
- float major_line_distance = view2d_major_step_x__time(v2d, scene);
+ const float major_line_distance = view2d_major_step_x__time(v2d, scene);
view2d_draw_lines(v2d, major_line_distance, major_line_distance > 1, 'v');
}
@@ -516,7 +516,7 @@ static void UI_view2d_draw_scale_x__discrete_values(const ARegion *region,
const rcti *rect,
int colorid)
{
- float number_step = view2d_major_step_x__discrete(v2d);
+ const float number_step = view2d_major_step_x__discrete(v2d);
draw_horizontal_scale_indicators(
region, v2d, number_step, rect, view_to_string__frame_number, NULL, colorid);
}
@@ -524,7 +524,7 @@ static void UI_view2d_draw_scale_x__discrete_values(const ARegion *region,
static void UI_view2d_draw_scale_x__discrete_time(
const ARegion *region, const View2D *v2d, const rcti *rect, const Scene *scene, int colorid)
{
- float step = view2d_major_step_x__time(v2d, scene);
+ const float step = view2d_major_step_x__time(v2d, scene);
draw_horizontal_scale_indicators(
region, v2d, step, rect, view_to_string__time, (void *)scene, colorid);
}
@@ -534,7 +534,7 @@ static void UI_view2d_draw_scale_x__values(const ARegion *region,
const rcti *rect,
int colorid)
{
- float step = view2d_major_step_x__continuous(v2d);
+ const float step = view2d_major_step_x__continuous(v2d);
draw_horizontal_scale_indicators(region, v2d, step, rect, view_to_string__value, NULL, colorid);
}
@@ -543,7 +543,7 @@ void UI_view2d_draw_scale_y__values(const ARegion *region,
const rcti *rect,
int colorid)
{
- float step = view2d_major_step_y__continuous(v2d);
+ const float step = view2d_major_step_y__continuous(v2d);
draw_vertical_scale_indicators(
region, v2d, step, 0.0f, rect, view_to_string__value, NULL, colorid);
}
diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c
index eb47c5c3e6d..7234e279da8 100644
--- a/source/blender/editors/interface/view2d_ops.c
+++ b/source/blender/editors/interface/view2d_ops.c
@@ -436,8 +436,8 @@ static float edge_pan_speed(v2dViewPanData *vpd,
ARegion *region = vpd->region;
/* Find the distance from the start of the drag zone. */
- int min = (x_dir ? region->winrct.xmin : region->winrct.ymin) + EDGE_PAN_REGION_PAD;
- int max = (x_dir ? region->winrct.xmax : region->winrct.ymax) - EDGE_PAN_REGION_PAD;
+ const int min = (x_dir ? region->winrct.xmin : region->winrct.ymin) + EDGE_PAN_REGION_PAD;
+ const int max = (x_dir ? region->winrct.xmax : region->winrct.ymax) - EDGE_PAN_REGION_PAD;
int distance = 0.0;
if (event_loc > max) {
distance = event_loc - max;
@@ -451,8 +451,8 @@ static float edge_pan_speed(v2dViewPanData *vpd,
}
/* Apply a fade in to the speed based on a start time delay. */
- double start_time = x_dir ? vpd->edge_pan_start_time_x : vpd->edge_pan_start_time_y;
- float delay_factor = smootherstep(EDGE_PAN_DELAY, (float)(current_time - start_time));
+ const double start_time = x_dir ? vpd->edge_pan_start_time_x : vpd->edge_pan_start_time_y;
+ const float delay_factor = smootherstep(EDGE_PAN_DELAY, (float)(current_time - start_time));
return distance * EDGE_PAN_SPEED_PER_PIXEL * delay_factor;
}
@@ -475,7 +475,7 @@ static int view_edge_pan_modal(bContext *C, wmOperator *op, const wmEvent *event
* On successful handling, always pass events on to other handlers. */
const int success_retval = OPERATOR_PASS_THROUGH;
- int outside_padding = RNA_int_get(op->ptr, "outside_padding") * UI_UNIT_X;
+ const int outside_padding = RNA_int_get(op->ptr, "outside_padding") * UI_UNIT_X;
rcti padding_rect;
if (outside_padding != 0) {
padding_rect = region->winrct;
@@ -504,14 +504,14 @@ static int view_edge_pan_modal(bContext *C, wmOperator *op, const wmEvent *event
edge_pan_manage_delay_timers(vpd, pan_dir_x, pan_dir_y, current_time);
/* Calculate the delta since the last time the operator was called. */
- float dtime = (float)(current_time - vpd->edge_pan_last_time);
+ const float dtime = (float)(current_time - vpd->edge_pan_last_time);
float dx = 0.0f, dy = 0.0f;
if (pan_dir_x != 0) {
- float speed = edge_pan_speed(vpd, event->x, true, current_time);
+ const float speed = edge_pan_speed(vpd, event->x, true, current_time);
dx = dtime * speed * (float)pan_dir_x;
}
if (pan_dir_y != 0) {
- float speed = edge_pan_speed(vpd, event->y, false, current_time);
+ const float speed = edge_pan_speed(vpd, event->y, false, current_time);
dy = dtime * speed * (float)pan_dir_y;
}
vpd->edge_pan_last_time = current_time;
@@ -911,9 +911,9 @@ static void view_zoomstep_apply_ex(
/* only move view to mouse if zoom fac is inside minzoom/maxzoom */
if (((v2d->keepzoom & V2D_LIMITZOOM) == 0) ||
IN_RANGE_INCL(zoomx, v2d->minzoom, v2d->maxzoom)) {
- float mval_fac = (vzd->mx_2d - cur_old.xmin) / BLI_rctf_size_x(&cur_old);
- float mval_faci = 1.0f - mval_fac;
- float ofs = (mval_fac * dx) - (mval_faci * dx);
+ const float mval_fac = (vzd->mx_2d - cur_old.xmin) / BLI_rctf_size_x(&cur_old);
+ const float mval_faci = 1.0f - mval_fac;
+ const float ofs = (mval_fac * dx) - (mval_faci * dx);
v2d->cur.xmin += ofs;
v2d->cur.xmax += ofs;
@@ -946,9 +946,9 @@ static void view_zoomstep_apply_ex(
/* only move view to mouse if zoom fac is inside minzoom/maxzoom */
if (((v2d->keepzoom & V2D_LIMITZOOM) == 0) ||
IN_RANGE_INCL(zoomy, v2d->minzoom, v2d->maxzoom)) {
- float mval_fac = (vzd->my_2d - cur_old.ymin) / BLI_rctf_size_y(&cur_old);
- float mval_faci = 1.0f - mval_fac;
- float ofs = (mval_fac * dy) - (mval_faci * dy);
+ const float mval_fac = (vzd->my_2d - cur_old.ymin) / BLI_rctf_size_y(&cur_old);
+ const float mval_faci = 1.0f - mval_fac;
+ const float ofs = (mval_fac * dy) - (mval_faci * dy);
v2d->cur.ymin += ofs;
v2d->cur.ymax += ofs;
@@ -1167,8 +1167,8 @@ static void view_zoomdrag_apply(bContext *C, wmOperator *op)
/* continuous zoom shouldn't move that fast... */
if (U.viewzoom == USER_ZOOM_CONT) { // XXX store this setting as RNA prop?
- double time = PIL_check_seconds_timer();
- float time_step = (float)(time - vzd->timer_lastdraw);
+ const double time = PIL_check_seconds_timer();
+ const float time_step = (float)(time - vzd->timer_lastdraw);
dx *= time_step * 0.5f;
dy *= time_step * 0.5f;
@@ -1183,9 +1183,9 @@ static void view_zoomdrag_apply(bContext *C, wmOperator *op)
}
else {
if (zoom_to_pos) {
- float mval_fac = (vzd->mx_2d - v2d->cur.xmin) / BLI_rctf_size_x(&v2d->cur);
- float mval_faci = 1.0f - mval_fac;
- float ofs = (mval_fac * dx) - (mval_faci * dx);
+ const float mval_fac = (vzd->mx_2d - v2d->cur.xmin) / BLI_rctf_size_x(&v2d->cur);
+ const float mval_faci = 1.0f - mval_fac;
+ const float ofs = (mval_fac * dx) - (mval_faci * dx);
v2d->cur.xmin += ofs + dx;
v2d->cur.xmax += ofs - dx;
@@ -1202,9 +1202,9 @@ static void view_zoomdrag_apply(bContext *C, wmOperator *op)
}
else {
if (zoom_to_pos) {
- float mval_fac = (vzd->my_2d - v2d->cur.ymin) / BLI_rctf_size_y(&v2d->cur);
- float mval_faci = 1.0f - mval_fac;
- float ofs = (mval_fac * dy) - (mval_faci * dy);
+ const float mval_fac = (vzd->my_2d - v2d->cur.ymin) / BLI_rctf_size_y(&v2d->cur);
+ const float mval_faci = 1.0f - mval_fac;
+ const float ofs = (mval_fac * dy) - (mval_faci * dy);
v2d->cur.ymin += ofs + dy;
v2d->cur.ymax += ofs - dy;
@@ -1987,8 +1987,8 @@ static short mouse_in_scroller_handle(int mouse, int sc_min, int sc_max, int sh_
(mouse >= (sh_min - V2D_SCROLL_HANDLE_SIZE_HOTSPOT)));
bool in_bar = ((mouse < (sh_max - V2D_SCROLL_HANDLE_SIZE_HOTSPOT)) &&
(mouse > (sh_min + V2D_SCROLL_HANDLE_SIZE_HOTSPOT)));
- bool out_min = mouse < (sh_min - V2D_SCROLL_HANDLE_SIZE_HOTSPOT);
- bool out_max = mouse > (sh_max + V2D_SCROLL_HANDLE_SIZE_HOTSPOT);
+ const bool out_min = mouse < (sh_min - V2D_SCROLL_HANDLE_SIZE_HOTSPOT);
+ const bool out_max = mouse > (sh_max + V2D_SCROLL_HANDLE_SIZE_HOTSPOT);
if (in_bar) {
return SCROLLHANDLE_BAR;
diff --git a/source/blender/editors/mask/mask_add.c b/source/blender/editors/mask/mask_add.c
index 3dc6227434e..e43eea35a91 100644
--- a/source/blender/editors/mask/mask_add.c
+++ b/source/blender/editors/mask/mask_add.c
@@ -462,8 +462,10 @@ static int add_vertex_handle_cyclic_at_point(bContext *C,
const float tolerance_in_pixels_squared = 4 * 4;
if (spline->flag & MASK_SPLINE_CYCLIC) {
- /* No cycling toggle needed, we've got nothing meaningful to do in this operator. */
- return OPERATOR_CANCELLED;
+ /* The spline is already cyclic, so there is no need to handle anything here.
+ * Return PASS_THROUGH so that it's possible to add vertices close to the endpoints of the
+ * cyclic spline. */
+ return OPERATOR_PASS_THROUGH;
}
float co_pixel[2];
diff --git a/source/blender/editors/mask/mask_draw.c b/source/blender/editors/mask/mask_draw.c
index dbaa335a9bf..8acbb328ab0 100644
--- a/source/blender/editors/mask/mask_draw.c
+++ b/source/blender/editors/mask/mask_draw.c
@@ -750,7 +750,7 @@ void ED_mask_draw_region(
IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR);
GPU_shader_uniform_vector(
state.shader, GPU_shader_get_uniform(state.shader, "shuffle"), 4, 1, red);
- immDrawPixelsTex(&state, 0.0f, 0.0f, width, height, GL_R16F, false, buffer, 1.0f, 1.0f, NULL);
+ immDrawPixelsTex(&state, 0.0f, 0.0f, width, height, GPU_R16F, false, buffer, 1.0f, 1.0f, NULL);
GPU_matrix_pop();
diff --git a/source/blender/editors/mesh/CMakeLists.txt b/source/blender/editors/mesh/CMakeLists.txt
index e41445aef09..589b51ce942 100644
--- a/source/blender/editors/mesh/CMakeLists.txt
+++ b/source/blender/editors/mesh/CMakeLists.txt
@@ -93,6 +93,10 @@ if(WITH_BULLET)
add_definitions(-DWITH_BULLET)
endif()
+if(WITH_GMP)
+ add_definitions(-DWITH_GMP)
+endif()
+
add_definitions(${GL_DEFINITIONS})
blender_add_lib(bf_editor_mesh "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/mesh/editmesh_intersect.c b/source/blender/editors/mesh/editmesh_intersect.c
index 97bd6ee0039..d56daaf8094 100644
--- a/source/blender/editors/mesh/editmesh_intersect.c
+++ b/source/blender/editors/mesh/editmesh_intersect.c
@@ -39,6 +39,9 @@
#include "WM_types.h"
+#include "UI_interface.h"
+#include "UI_resources.h"
+
#include "ED_mesh.h"
#include "ED_screen.h"
@@ -46,6 +49,7 @@
#include "mesh_intern.h" /* own include */
+#include "tools/bmesh_boolean.h"
#include "tools/bmesh_intersect.h"
#include "tools/bmesh_separate.h"
@@ -134,6 +138,11 @@ enum {
ISECT_SEPARATE_NONE = 2,
};
+enum {
+ ISECT_SOLVER_FAST = 0,
+ ISECT_SOLVER_EXACT = 1,
+};
+
static int edbm_intersect_exec(bContext *C, wmOperator *op)
{
const int mode = RNA_enum_get(op->ptr, "mode");
@@ -142,6 +151,11 @@ static int edbm_intersect_exec(bContext *C, wmOperator *op)
bool use_separate_cut = false;
const int separate_mode = RNA_enum_get(op->ptr, "separate_mode");
const float eps = RNA_float_get(op->ptr, "threshold");
+#ifdef WITH_GMP
+ const bool exact = RNA_enum_get(op->ptr, "solver") == ISECT_SOLVER_EXACT;
+#else
+ const bool exact = false;
+#endif
bool use_self;
bool has_isect;
@@ -186,19 +200,25 @@ static int edbm_intersect_exec(bContext *C, wmOperator *op)
continue;
}
- has_isect = BM_mesh_intersect(em->bm,
- em->looptris,
- em->tottri,
- test_fn,
- NULL,
- use_self,
- use_separate_all,
- true,
- true,
- true,
- true,
- -1,
- eps);
+ if (exact) {
+ has_isect = BM_mesh_boolean_knife(
+ em->bm, em->looptris, em->tottri, test_fn, NULL, use_self, use_separate_all);
+ }
+ else {
+ has_isect = BM_mesh_intersect(em->bm,
+ em->looptris,
+ em->tottri,
+ test_fn,
+ NULL,
+ use_self,
+ use_separate_all,
+ true,
+ true,
+ true,
+ true,
+ -1,
+ eps);
+ }
if (use_separate_cut) {
/* detach selected/un-selected faces */
@@ -220,6 +240,38 @@ static int edbm_intersect_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
+static void edbm_intersect_ui(bContext *UNUSED(C), wmOperator *op)
+{
+ uiLayout *layout = op->layout;
+ uiLayout *row;
+ PointerRNA ptr;
+
+ RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
+
+#ifdef WITH_GMP
+ bool use_exact = RNA_enum_get(&ptr, "solver") == ISECT_SOLVER_EXACT;
+#else
+ bool use_exact = false;
+#endif
+
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ row = uiLayoutRow(layout, false);
+ uiItemR(row, &ptr, "mode", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemS(layout);
+ row = uiLayoutRow(layout, false);
+ uiItemR(row, &ptr, "separate_mode", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemS(layout);
+#ifdef WITH_GMP
+ row = uiLayoutRow(layout, false);
+ uiItemR(row, &ptr, "solver", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemS(layout);
+#endif
+ if (!use_exact) {
+ uiItemR(layout, &ptr, "threshold", 0, NULL, ICON_NONE);
+ }
+}
+
void MESH_OT_intersect(struct wmOperatorType *ot)
{
static const EnumPropertyItem isect_mode_items[] = {
@@ -243,6 +295,12 @@ void MESH_OT_intersect(struct wmOperatorType *ot)
{0, NULL, 0, NULL, NULL},
};
+ static const EnumPropertyItem isect_intersect_solver_items[] = {
+ {ISECT_SOLVER_FAST, "FAST", 0, "Fast", "Faster Solver, some limitations"},
+ {ISECT_SOLVER_EXACT, "EXACT", 0, "Exact", "Exact Solver, slower, handles more cases"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
/* identifiers */
ot->name = "Intersect (Knife)";
ot->description = "Cut an intersection into faces";
@@ -251,6 +309,7 @@ void MESH_OT_intersect(struct wmOperatorType *ot)
/* api callbacks */
ot->exec = edbm_intersect_exec;
ot->poll = ED_operator_editmesh;
+ ot->ui = edbm_intersect_ui;
/* props */
RNA_def_enum(ot->srna, "mode", isect_mode_items, ISECT_SEL_UNSEL, "Source", "");
@@ -258,6 +317,14 @@ void MESH_OT_intersect(struct wmOperatorType *ot)
ot->srna, "separate_mode", isect_separate_items, ISECT_SEPARATE_CUT, "Separate Mode", "");
RNA_def_float_distance(
ot->srna, "threshold", 0.000001f, 0.0, 0.01, "Merge threshold", "", 0.0, 0.001);
+#ifdef WITH_GMP
+ RNA_def_enum(ot->srna,
+ "solver",
+ isect_intersect_solver_items,
+ ISECT_SOLVER_EXACT,
+ "Solver",
+ "Which Intersect solver to use");
+#endif
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -280,6 +347,12 @@ static int edbm_intersect_boolean_exec(bContext *C, wmOperator *op)
{
const int boolean_operation = RNA_enum_get(op->ptr, "operation");
bool use_swap = RNA_boolean_get(op->ptr, "use_swap");
+ bool use_self = RNA_boolean_get(op->ptr, "use_self");
+#ifdef WITH_GMP
+ bool use_exact = RNA_enum_get(op->ptr, "solver") == ISECT_SOLVER_EXACT;
+#else
+ bool use_exact = false;
+#endif
const float eps = RNA_float_get(op->ptr, "threshold");
int (*test_fn)(BMFace *, void *);
bool has_isect;
@@ -298,19 +371,25 @@ static int edbm_intersect_boolean_exec(bContext *C, wmOperator *op)
continue;
}
- has_isect = BM_mesh_intersect(em->bm,
- em->looptris,
- em->tottri,
- test_fn,
- NULL,
- false,
- false,
- true,
- true,
- false,
- true,
- boolean_operation,
- eps);
+ if (use_exact) {
+ has_isect = BM_mesh_boolean(
+ em->bm, em->looptris, em->tottri, test_fn, NULL, use_self, boolean_operation);
+ }
+ else {
+ has_isect = BM_mesh_intersect(em->bm,
+ em->looptris,
+ em->tottri,
+ test_fn,
+ NULL,
+ false,
+ false,
+ true,
+ true,
+ false,
+ true,
+ boolean_operation,
+ eps);
+ }
edbm_intersect_select(em, obedit->data, has_isect);
@@ -326,6 +405,38 @@ static int edbm_intersect_boolean_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
+static void edbm_intersect_boolean_ui(bContext *UNUSED(C), wmOperator *op)
+{
+ uiLayout *layout = op->layout;
+ uiLayout *row;
+ PointerRNA ptr;
+
+ RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
+
+#ifdef WITH_GMP
+ bool use_exact = RNA_enum_get(&ptr, "solver") == ISECT_SOLVER_EXACT;
+#else
+ bool use_exact = false;
+#endif
+
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+
+ row = uiLayoutRow(layout, false);
+ uiItemR(row, &ptr, "operation", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemS(layout);
+#ifdef WITH_GMP
+ row = uiLayoutRow(layout, false);
+ uiItemR(row, &ptr, "solver", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemS(layout);
+#endif
+ uiItemR(layout, &ptr, "use_swap", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "use_self", 0, NULL, ICON_NONE);
+ if (!use_exact) {
+ uiItemR(layout, &ptr, "threshold", 0, NULL, ICON_NONE);
+ }
+}
+
void MESH_OT_intersect_boolean(struct wmOperatorType *ot)
{
static const EnumPropertyItem isect_boolean_operation_items[] = {
@@ -334,6 +445,11 @@ void MESH_OT_intersect_boolean(struct wmOperatorType *ot)
{BMESH_ISECT_BOOLEAN_DIFFERENCE, "DIFFERENCE", 0, "Difference", ""},
{0, NULL, 0, NULL, NULL},
};
+ static const EnumPropertyItem isect_boolean_solver_items[] = {
+ {ISECT_SOLVER_FAST, "FAST", 0, "Fast", "Faster Solver, some limitations"},
+ {ISECT_SOLVER_EXACT, "EXACT", 0, "Exact", "Exact Solver, slower, handles more cases"},
+ {0, NULL, 0, NULL, NULL},
+ };
/* identifiers */
ot->name = "Intersect (Boolean)";
@@ -343,21 +459,31 @@ void MESH_OT_intersect_boolean(struct wmOperatorType *ot)
/* api callbacks */
ot->exec = edbm_intersect_boolean_exec;
ot->poll = ED_operator_editmesh;
+ ot->ui = edbm_intersect_boolean_ui;
/* props */
RNA_def_enum(ot->srna,
"operation",
isect_boolean_operation_items,
BMESH_ISECT_BOOLEAN_DIFFERENCE,
- "Boolean",
- "");
+ "Boolean operation",
+ "Which boolean operation to apply");
RNA_def_boolean(ot->srna,
"use_swap",
false,
"Swap",
"Use with difference intersection to swap which side is kept");
+ RNA_def_boolean(ot->srna, "use_self", false, "Self", "Do self-union or self-intersection");
RNA_def_float_distance(
ot->srna, "threshold", 0.000001f, 0.0, 0.01, "Merge threshold", "", 0.0, 0.001);
+#ifdef WITH_GMP
+ RNA_def_enum(ot->srna,
+ "solver",
+ isect_boolean_solver_items,
+ ISECT_SOLVER_EXACT,
+ "Solver",
+ "Which Boolean solver to use");
+#endif
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c
index 94cd7650abe..6facee77c1e 100644
--- a/source/blender/editors/mesh/editmesh_knife.c
+++ b/source/blender/editors/mesh/editmesh_knife.c
@@ -1051,7 +1051,7 @@ static void knife_init_colors(KnifeColors *colors)
static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), void *arg)
{
const KnifeTool_OpData *kcd = arg;
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
GPU_matrix_push_projection();
GPU_polygon_offset(1.0f, 1.0f);
@@ -1222,7 +1222,7 @@ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), v
GPU_matrix_pop_projection();
/* Reset default */
- GPU_depth_test(true);
+ GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
}
/**
diff --git a/source/blender/editors/mesh/editmesh_preselect_edgering.c b/source/blender/editors/mesh/editmesh_preselect_edgering.c
index d9bd63ef35f..aa1df3d76fc 100644
--- a/source/blender/editors/mesh/editmesh_preselect_edgering.c
+++ b/source/blender/editors/mesh/editmesh_preselect_edgering.c
@@ -159,7 +159,7 @@ void EDBM_preselect_edgering_draw(struct EditMesh_PreSelEdgeRing *psel, const fl
return;
}
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
GPU_matrix_push();
GPU_matrix_mul(matrix);
@@ -197,7 +197,7 @@ void EDBM_preselect_edgering_draw(struct EditMesh_PreSelEdgeRing *psel, const fl
GPU_matrix_pop();
/* Reset default */
- GPU_depth_test(true);
+ GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
}
static void view3d_preselect_mesh_edgering_update_verts_from_edge(
diff --git a/source/blender/editors/mesh/editmesh_preselect_elem.c b/source/blender/editors/mesh/editmesh_preselect_elem.c
index d53a1e2b55c..dfd646c767f 100644
--- a/source/blender/editors/mesh/editmesh_preselect_elem.c
+++ b/source/blender/editors/mesh/editmesh_preselect_elem.c
@@ -133,7 +133,7 @@ void EDBM_preselect_elem_draw(struct EditMesh_PreSelElem *psel, const float matr
return;
}
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
GPU_matrix_push();
GPU_matrix_mul(matrix);
@@ -204,7 +204,7 @@ void EDBM_preselect_elem_draw(struct EditMesh_PreSelElem *psel, const float matr
GPU_matrix_pop();
/* Reset default */
- GPU_depth_test(true);
+ GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
}
static void view3d_preselect_mesh_elem_update_from_vert(struct EditMesh_PreSelElem *psel,
diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c
index d2e9b57e950..efcf4abda06 100644
--- a/source/blender/editors/mesh/editmesh_select.c
+++ b/source/blender/editors/mesh/editmesh_select.c
@@ -1423,15 +1423,13 @@ void MESH_OT_select_mode(wmOperatorType *ot)
static void walker_select_count(BMEditMesh *em,
int walkercode,
void *start,
- const bool select,
- const bool select_mix,
- int *r_totsel,
- int *r_totunsel)
+ int r_count_by_select[2])
{
BMesh *bm = em->bm;
BMElem *ele;
BMWalker walker;
- int tot[2] = {0, 0};
+
+ r_count_by_select[0] = r_count_by_select[1] = 0;
BMW_init(&walker,
bm,
@@ -1443,17 +1441,15 @@ static void walker_select_count(BMEditMesh *em,
BMW_NIL_LAY);
for (ele = BMW_begin(&walker, start); ele; ele = BMW_step(&walker)) {
- tot[(BM_elem_flag_test_bool(ele, BM_ELEM_SELECT) != select)] += 1;
+ r_count_by_select[BM_elem_flag_test(ele, BM_ELEM_SELECT) ? 1 : 0] += 1;
- if (!select_mix && tot[0] && tot[1]) {
- tot[0] = tot[1] = -1;
+ /* Early exit when mixed (could be optional if needed. */
+ if (r_count_by_select[0] && r_count_by_select[1]) {
+ r_count_by_select[0] = r_count_by_select[1] = -1;
break;
}
}
- *r_totsel = tot[0];
- *r_totunsel = tot[1];
-
BMW_end(&walker);
}
@@ -1590,18 +1586,18 @@ static void mouse_mesh_loop_edge(
{
bool edge_boundary = false;
- /* cycle between BMW_EDGELOOP / BMW_EDGEBOUNDARY */
+ /* Cycle between BMW_EDGELOOP / BMW_EDGEBOUNDARY. */
if (select_cycle && BM_edge_is_boundary(eed)) {
- int tot[2];
+ int count_by_select[2];
- /* if the loops selected toggle the boundaries */
- walker_select_count(em, BMW_EDGELOOP, eed, select, false, &tot[0], &tot[1]);
- if (tot[select] == 0) {
+ /* If the loops selected toggle the boundaries. */
+ walker_select_count(em, BMW_EDGELOOP, eed, count_by_select);
+ if (count_by_select[!select] == 0) {
edge_boundary = true;
- /* if the boundaries selected, toggle back to the loop */
- walker_select_count(em, BMW_EDGEBOUNDARY, eed, select, false, &tot[0], &tot[1]);
- if (tot[select] == 0) {
+ /* If the boundaries selected, toggle back to the loop. */
+ walker_select_count(em, BMW_EDGEBOUNDARY, eed, count_by_select);
+ if (count_by_select[!select] == 0) {
edge_boundary = false;
}
}
diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c
index 46c63d2e057..e454328e2d4 100644
--- a/source/blender/editors/mesh/editmesh_utils.c
+++ b/source/blender/editors/mesh/editmesh_utils.c
@@ -523,7 +523,7 @@ void EDBM_flag_enable_all(BMEditMesh *em, const char hflag)
* \{ */
/**
- * Return a new UVVertMap from the editmesh
+ * Return a new #UvVertMap from the edit-mesh.
*/
UvVertMap *BM_uv_vert_map_create(BMesh *bm, const bool use_select, const bool use_winding)
{
diff --git a/source/blender/editors/mesh/mesh_data.c b/source/blender/editors/mesh/mesh_data.c
index f608e5ce6a5..22ea222cf01 100644
--- a/source/blender/editors/mesh/mesh_data.c
+++ b/source/blender/editors/mesh/mesh_data.c
@@ -1014,6 +1014,10 @@ static int mesh_customdata_custom_splitnormals_clear_exec(bContext *C, wmOperato
Mesh *me = ED_mesh_context(C);
if (BKE_mesh_has_custom_loop_normals(me)) {
+ BMEditMesh *em = me->edit_mesh;
+ if (em != NULL && em->bm->lnor_spacearr != NULL) {
+ BKE_lnor_spacearr_clear(em->bm->lnor_spacearr);
+ }
return mesh_customdata_clear_exec__internal(C, BM_LOOP, CD_CUSTOMLOOPNORMAL);
}
return OPERATOR_CANCELLED;
diff --git a/source/blender/editors/mesh/meshtools.c b/source/blender/editors/mesh/meshtools.c
index 5278da67777..bd14919d1d7 100644
--- a/source/blender/editors/mesh/meshtools.c
+++ b/source/blender/editors/mesh/meshtools.c
@@ -590,8 +590,9 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op)
loopofs = 0;
polyofs = 0;
- /* inverse transform for all selected meshes in this object */
- invert_m4_m4(imat, ob->obmat);
+ /* Inverse transform for all selected meshes in this object,
+ * See #object_join_exec for detailed comment on why the safe version is used. */
+ invert_m4_m4_safe_ortho(imat, ob->obmat);
/* Add back active mesh first.
* This allows to keep things similar as they were, as much as possible
@@ -741,6 +742,7 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op)
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
+ WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene);
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c
index 139900d0a4d..72180d58ecb 100644
--- a/source/blender/editors/object/object_add.c
+++ b/source/blender/editors/object/object_add.c
@@ -1450,6 +1450,7 @@ static int collection_instance_add_exec(bContext *C, wmOperator *op)
DEG_relations_tag_update(bmain);
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
+ WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene);
return OPERATOR_FINISHED;
}
@@ -2783,6 +2784,7 @@ static int object_convert_exec(bContext *C, wmOperator *op)
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, scene);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
+ WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene);
return OPERATOR_FINISHED;
}
@@ -3003,6 +3005,7 @@ static int duplicate_exec(bContext *C, wmOperator *op)
DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE | ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
+ WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene);
return OPERATOR_FINISHED;
}
@@ -3094,6 +3097,7 @@ static int object_add_named_exec(bContext *C, wmOperator *op)
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
+ WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene);
ED_outliner_select_sync_from_object_tag(C);
return OPERATOR_FINISHED;
@@ -3163,20 +3167,45 @@ static int object_join_exec(bContext *C, wmOperator *op)
}
}
+ int ret = OPERATOR_CANCELLED;
if (ob->type == OB_MESH) {
- return ED_mesh_join_objects_exec(C, op);
- }
- if (ELEM(ob->type, OB_CURVE, OB_SURF)) {
- return ED_curve_join_objects_exec(C, op);
- }
- if (ob->type == OB_ARMATURE) {
- return ED_armature_join_objects_exec(C, op);
- }
- if (ob->type == OB_GPENCIL) {
- return ED_gpencil_join_objects_exec(C, op);
- }
-
- return OPERATOR_CANCELLED;
+ ret = ED_mesh_join_objects_exec(C, op);
+ }
+ else if (ELEM(ob->type, OB_CURVE, OB_SURF)) {
+ ret = ED_curve_join_objects_exec(C, op);
+ }
+ else if (ob->type == OB_ARMATURE) {
+ ret = ED_armature_join_objects_exec(C, op);
+ }
+ else if (ob->type == OB_GPENCIL) {
+ ret = ED_gpencil_join_objects_exec(C, op);
+ }
+
+ if (ret & OPERATOR_FINISHED) {
+ /* Even though internally failure to invert is accounted for with a fallback,
+ * show a warning since the result may not be what the user expects. See T80077.
+ *
+ * Failure to invert the matrix is typically caused by zero scaled axes
+ * (which can be caused by constraints, even if the input scale isn't zero).
+ *
+ * Internally the join functions use #invert_m4_m4_safe_ortho which creates
+ * an inevitable matrix from one that has one or more degenerate axes.
+ *
+ * In most cases we don't worry about special handling for non-inevitable matrices however for
+ * joining objects there may be flat 2D objects where it's not obvious the scale is zero.
+ * In this case, using #invert_m4_m4_safe_ortho works as well as we can expect,
+ * joining the contents, flattening on the axis that's zero scaled.
+ * If the zero scale is removed, the data on this axis remains un-scaled
+ * (something that wouldn't work for #invert_m4_m4_safe). */
+ float imat_test[4][4];
+ if (!invert_m4_m4(imat_test, ob->obmat)) {
+ BKE_report(op->reports,
+ RPT_WARNING,
+ "Active object final transform has one or more zero scaled axes");
+ }
+ }
+
+ return ret;
}
void OBJECT_OT_join(wmOperatorType *ot)
diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h
index 6bc615c9b9e..5c7370334e8 100644
--- a/source/blender/editors/object/object_intern.h
+++ b/source/blender/editors/object/object_intern.h
@@ -303,6 +303,7 @@ void OBJECT_OT_voxel_remesh(struct wmOperatorType *ot);
void OBJECT_OT_voxel_size_edit(struct wmOperatorType *ot);
void OBJECT_OT_quadriflow_remesh(struct wmOperatorType *ot);
void OBJECT_OT_tetgen_remesh(struct wmOperatorType *ot);
+void OBJECT_OT_tetlattice_remesh(struct wmOperatorType *ot);
/* object_transfer_data.c */
void OBJECT_OT_data_transfer(struct wmOperatorType *ot);
diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c
index c699882ef4a..763d826dbde 100644
--- a/source/blender/editors/object/object_ops.c
+++ b/source/blender/editors/object/object_ops.c
@@ -274,6 +274,7 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_quadriflow_remesh);
WM_operatortype_append(OBJECT_OT_tetgen_remesh);
+ WM_operatortype_append(OBJECT_OT_tetlattice_remesh);
}
void ED_operatormacros_object(void)
diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c
index 732d2f6ad52..d9196425098 100644
--- a/source/blender/editors/object/object_relations.c
+++ b/source/blender/editors/object/object_relations.c
@@ -134,13 +134,11 @@ static int vertex_parent_set_exec(bContext *C, wmOperator *op)
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
Object *obedit = CTX_data_edit_object(C);
- BMVert *eve;
- BMIter iter;
- Nurb *nu;
- BezTriple *bezt;
- BPoint *bp;
Object *par;
- int a, v1 = 0, v2 = 0, v3 = 0, v4 = 0, nr = 1;
+
+#define INDEX_UNSET -1
+ int par1, par2, par3, par4;
+ par1 = par2 = par3 = par4 = INDEX_UNSET;
/* we need 1 to 3 selected vertices */
@@ -165,114 +163,108 @@ static int vertex_parent_set_exec(bContext *C, wmOperator *op)
* objects are also up to date. */
BKE_scene_graph_update_tagged(depsgraph, bmain);
- BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
+ BMVert *eve;
+ BMIter iter;
+ int curr_index;
+ BM_ITER_MESH_INDEX (eve, &iter, em->bm, BM_VERTS_OF_MESH, curr_index) {
if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
- if (v1 == 0) {
- v1 = nr;
+ if (par1 == INDEX_UNSET) {
+ par1 = curr_index;
}
- else if (v2 == 0) {
- v2 = nr;
+ else if (par2 == INDEX_UNSET) {
+ par2 = curr_index;
}
- else if (v3 == 0) {
- v3 = nr;
+ else if (par3 == INDEX_UNSET) {
+ par3 = curr_index;
}
- else if (v4 == 0) {
- v4 = nr;
+ else if (par4 == INDEX_UNSET) {
+ par4 = curr_index;
}
else {
break;
}
}
- nr++;
}
}
else if (ELEM(obedit->type, OB_SURF, OB_CURVE)) {
ListBase *editnurb = object_editcurve_get(obedit);
- nu = editnurb->first;
- while (nu) {
+ for (Nurb *nu = editnurb->first; nu != NULL; nu = nu->next) {
if (nu->type == CU_BEZIER) {
- bezt = nu->bezt;
- a = nu->pntsu;
- while (a--) {
+ BezTriple *bezt = nu->bezt;
+ for (int curr_index = 0; curr_index < nu->pntsu; curr_index++, bezt++) {
if (BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt)) {
- if (v1 == 0) {
- v1 = nr;
+ if (par1 == INDEX_UNSET) {
+ par1 = curr_index;
}
- else if (v2 == 0) {
- v2 = nr;
+ else if (par2 == INDEX_UNSET) {
+ par2 = curr_index;
}
- else if (v3 == 0) {
- v3 = nr;
+ else if (par3 == INDEX_UNSET) {
+ par3 = curr_index;
}
- else if (v4 == 0) {
- v4 = nr;
+ else if (par4 == INDEX_UNSET) {
+ par4 = curr_index;
}
else {
break;
}
}
- nr++;
- bezt++;
}
}
else {
- bp = nu->bp;
- a = nu->pntsu * nu->pntsv;
- while (a--) {
+ BPoint *bp = nu->bp;
+ const int num_points = nu->pntsu * nu->pntsv;
+ for (int curr_index = 0; curr_index < num_points; curr_index++, bp++) {
if (bp->f1 & SELECT) {
- if (v1 == 0) {
- v1 = nr;
+ if (par1 == INDEX_UNSET) {
+ par1 = curr_index;
}
- else if (v2 == 0) {
- v2 = nr;
+ else if (par2 == INDEX_UNSET) {
+ par2 = curr_index;
}
- else if (v3 == 0) {
- v3 = nr;
+ else if (par3 == INDEX_UNSET) {
+ par3 = curr_index;
}
- else if (v4 == 0) {
- v4 = nr;
+ else if (par4 == INDEX_UNSET) {
+ par4 = curr_index;
}
else {
break;
}
}
- nr++;
- bp++;
}
}
- nu = nu->next;
}
}
else if (obedit->type == OB_LATTICE) {
Lattice *lt = obedit->data;
- a = lt->editlatt->latt->pntsu * lt->editlatt->latt->pntsv * lt->editlatt->latt->pntsw;
- bp = lt->editlatt->latt->def;
- while (a--) {
+ const int num_points = lt->editlatt->latt->pntsu * lt->editlatt->latt->pntsv *
+ lt->editlatt->latt->pntsw;
+ BPoint *bp = lt->editlatt->latt->def;
+ for (int curr_index = 0; curr_index < num_points; curr_index++, bp++) {
if (bp->f1 & SELECT) {
- if (v1 == 0) {
- v1 = nr;
+ if (par1 == INDEX_UNSET) {
+ par1 = curr_index;
}
- else if (v2 == 0) {
- v2 = nr;
+ else if (par2 == INDEX_UNSET) {
+ par2 = curr_index;
}
- else if (v3 == 0) {
- v3 = nr;
+ else if (par3 == INDEX_UNSET) {
+ par3 = curr_index;
}
- else if (v4 == 0) {
- v4 = nr;
+ else if (par4 == INDEX_UNSET) {
+ par4 = curr_index;
}
else {
break;
}
}
- nr++;
- bp++;
}
}
- if (v4 || !((v1 && v2 == 0 && v3 == 0) || (v1 && v2 && v3))) {
+ if (par4 != INDEX_UNSET || par1 == INDEX_UNSET || (par2 != INDEX_UNSET && par3 == INDEX_UNSET)) {
BKE_report(op->reports, RPT_ERROR, "Select either 1 or 3 vertices to parent to");
return OPERATOR_CANCELLED;
}
@@ -289,11 +281,11 @@ static int vertex_parent_set_exec(bContext *C, wmOperator *op)
Object workob;
ob->parent = BASACT(view_layer)->object;
- if (v3) {
+ if (par3 != INDEX_UNSET) {
ob->partype = PARVERT3;
- ob->par1 = v1 - 1;
- ob->par2 = v2 - 1;
- ob->par3 = v3 - 1;
+ ob->par1 = par1;
+ ob->par2 = par2;
+ ob->par3 = par3;
/* inverse parent matrix */
BKE_object_workob_calc_parent(depsgraph, scene, ob, &workob);
@@ -301,7 +293,7 @@ static int vertex_parent_set_exec(bContext *C, wmOperator *op)
}
else {
ob->partype = PARVERT1;
- ob->par1 = v1 - 1;
+ ob->par1 = par1;
/* inverse parent matrix */
BKE_object_workob_calc_parent(depsgraph, scene, ob, &workob);
@@ -317,6 +309,8 @@ static int vertex_parent_set_exec(bContext *C, wmOperator *op)
WM_event_add_notifier(C, NC_OBJECT, NULL);
return OPERATOR_FINISHED;
+
+#undef INDEX_UNSET
}
void OBJECT_OT_vertex_parent_set(wmOperatorType *ot)
@@ -1474,7 +1468,8 @@ static int make_links_scene_exec(bContext *C, wmOperator *op)
/* redraw the 3D view because the object center points are colored differently */
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, NULL);
- /* one day multiple scenes will be visible, then we should have some update function for them */
+ /* one day multiple scenes will be visible, then we should have some update function for them
+ */
return OPERATOR_FINISHED;
}
@@ -1794,9 +1789,9 @@ static Collection *single_object_users_collection(Main *bmain,
if (is_master_collection && copy_collections && child->collection != collection_child_new) {
/* We do not want a collection sync here, our collections are in a complete uninitialized
- * state currently. With current code, that would lead to a memory leak - because of reasons.
- * It would be a useless loss of computing anyway, since caller has to fully refresh
- * view-layers/collections caching at the end. */
+ * state currently. With current code, that would lead to a memory leak - because of
+ * reasons. It would be a useless loss of computing anyway, since caller has to fully
+ * refresh view-layers/collections caching at the end. */
BKE_collection_child_add_no_sync(collection, collection_child_new);
BLI_remlink(&collection->children, child);
MEM_freeN(child);
diff --git a/source/blender/editors/object/object_remesh.c b/source/blender/editors/object/object_remesh.c
index 9be84dc4b8c..a47f6329bae 100644
--- a/source/blender/editors/object/object_remesh.c
+++ b/source/blender/editors/object/object_remesh.c
@@ -1252,4 +1252,57 @@ void OBJECT_OT_tetgen_remesh(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
+static int tetlattice_remesh_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_active_object(C);
+
+ Mesh *mesh = ob->data;
+ Mesh *new_mesh = NULL;
+
+ unsigned int *tets = NULL;
+ int numtets;
+ int subdiv = 3;
+ new_mesh = BKE_mesh_remesh_tetlattice_to_mesh_nomain(mesh, subdiv, &tets, &numtets);
+ if (tets) {
+ MEM_freeN(tets);
+ }
+
+ if (!new_mesh) {
+ BKE_report(op->reports, RPT_ERROR, "Tet Lattice remesher failed to create mesh");
+ return OPERATOR_CANCELLED;
+ }
+
+ BKE_mesh_nomain_to_mesh(new_mesh, mesh, ob, &CD_MASK_MESH, true);
+
+ if (mesh->flag & ME_REMESH_SMOOTH_NORMALS) {
+ BKE_mesh_smooth_flag_set(ob->data, true);
+ }
+
+ if (ob->mode == OB_MODE_SCULPT) {
+ ED_sculpt_undo_geometry_end(ob);
+ }
+
+ BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL);
+ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
+
+ return OPERATOR_FINISHED;
+}
+
+void OBJECT_OT_tetlattice_remesh(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Tet Lattice Remesh";
+ ot->description =
+ "Create a new tet mesh using the surface data of the current mesh. All data "
+ "layers will be lost";
+ ot->idname = "OBJECT_OT_tetlattice_remesh";
+
+ /* api callbacks */
+ ot->poll = object_remesh_poll;
+ ot->exec = tetlattice_remesh_exec;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
/** \} */
diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c
index 52a7b92217b..940ca963fc6 100644
--- a/source/blender/editors/render/render_opengl.c
+++ b/source/blender/editors/render/render_opengl.c
@@ -339,7 +339,7 @@ static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, R
GPU_offscreen_bind(oglrender->ofs, true);
GPU_clear_color(0.0f, 0.0f, 0.0f, 0.0f);
- GPU_clear(GPU_COLOR_BIT | GPU_DEPTH_BIT);
+ GPU_clear_depth(1.0f);
GPU_matrix_reset();
wmOrtho2(0, scene->r.xsch, 0, scene->r.ysch);
diff --git a/source/blender/editors/render/render_shading.c b/source/blender/editors/render/render_shading.c
index 711f89b9fda..3f31cbf7e48 100644
--- a/source/blender/editors/render/render_shading.c
+++ b/source/blender/editors/render/render_shading.c
@@ -95,6 +95,10 @@
#include "render_intern.h" // own include
+/* -------------------------------------------------------------------- */
+/** \name Local Utilities
+ * \{ */
+
/**
* Object list for material operations.
* has exception for pinned object.
@@ -127,7 +131,11 @@ static Object **object_array_for_shading(bContext *C, uint *r_objects_len)
return objects;
}
-/********************** material slot operators *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Material Slot Add Operator
+ * \{ */
static int material_slot_add_exec(bContext *C, wmOperator *UNUSED(op))
{
@@ -168,6 +176,12 @@ void OBJECT_OT_material_slot_add(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Material Slot Remove Operator
+ * \{ */
+
static int material_slot_remove_exec(bContext *C, wmOperator *op)
{
Object *ob = ED_object_context(C);
@@ -213,6 +227,12 @@ void OBJECT_OT_material_slot_remove(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Material Slot Assign Operator
+ * \{ */
+
static int material_slot_assign_exec(bContext *C, wmOperator *UNUSED(op))
{
View3D *v3d = CTX_wm_view3d(C);
@@ -316,6 +336,12 @@ void OBJECT_OT_material_slot_assign(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Material Slot (De)Select Operator
+ * \{ */
+
static int material_slot_de_select(bContext *C, bool select)
{
bool changed_multi = false;
@@ -461,6 +487,12 @@ void OBJECT_OT_material_slot_deselect(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Material Slot Copy Operator
+ * \{ */
+
static int material_slot_copy_exec(bContext *C, wmOperator *UNUSED(op))
{
Main *bmain = CTX_data_main(C);
@@ -515,6 +547,12 @@ void OBJECT_OT_material_slot_copy(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Material Slot Move Operator
+ * \{ */
+
static int material_slot_move_exec(bContext *C, wmOperator *op)
{
Object *ob = ED_object_context(C);
@@ -590,6 +628,12 @@ void OBJECT_OT_material_slot_move(wmOperatorType *ot)
"Direction to move the active material towards");
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Material Slot Remove Unused Operator
+ * \{ */
+
static int material_slot_remove_unused_exec(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
@@ -657,7 +701,11 @@ void OBJECT_OT_material_slot_remove_unused(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/********************** new material operator *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name New Material Operator
+ * \{ */
static int new_material_exec(bContext *C, wmOperator *UNUSED(op))
{
@@ -727,7 +775,11 @@ void MATERIAL_OT_new(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
}
-/********************** new texture operator *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name New Texture Operator
+ * \{ */
static int new_texture_exec(bContext *C, wmOperator *UNUSED(op))
{
@@ -776,7 +828,11 @@ void TEXTURE_OT_new(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
}
-/********************** new world operator *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name new world operator
+ * \{ */
static int new_world_exec(bContext *C, wmOperator *UNUSED(op))
{
@@ -827,7 +883,11 @@ void WORLD_OT_new(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
}
-/********************** render layer operators *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Render Layer Add Operator
+ * \{ */
static int view_layer_add_exec(bContext *C, wmOperator *op)
{
@@ -877,6 +937,12 @@ void SCENE_OT_view_layer_add(wmOperatorType *ot)
ot->prop = RNA_def_enum(ot->srna, "type", type_items, 0, "Type", "");
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Render Layer Remove Operator
+ * \{ */
+
static bool view_layer_remove_poll(bContext *C)
{
Scene *scene = CTX_data_scene(C);
@@ -913,7 +979,12 @@ void SCENE_OT_view_layer_remove(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
}
-/********************** light cache operators *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Light Cache Bake Operator
+ * \{ */
+
enum {
LIGHTCACHE_SUBSET_ALL = 0,
LIGHTCACHE_SUBSET_DIRTY,
@@ -1079,6 +1150,12 @@ void SCENE_OT_light_cache_bake(wmOperatorType *ot)
RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE);
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Light Cache Free Operator
+ * \{ */
+
static bool light_cache_free_poll(bContext *C)
{
Scene *scene = CTX_data_scene(C);
@@ -1122,7 +1199,11 @@ void SCENE_OT_light_cache_free(wmOperatorType *ot)
ot->poll = light_cache_free_poll;
}
-/********************** render view operators *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Render View Remove Operator
+ * \{ */
static bool render_view_remove_poll(bContext *C)
{
@@ -1158,6 +1239,12 @@ void SCENE_OT_render_view_add(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Render View Add Operator
+ * \{ */
+
static int render_view_remove_exec(bContext *C, wmOperator *UNUSED(op))
{
Scene *scene = CTX_data_scene(C);
@@ -1187,8 +1274,14 @@ void SCENE_OT_render_view_remove(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
+/** \} */
+
#ifdef WITH_FREESTYLE
+/* -------------------------------------------------------------------- */
+/** \name Free Style Module Add Operator
+ * \{ */
+
static bool freestyle_linestyle_check_report(FreestyleLineSet *lineset, ReportList *reports)
{
if (!lineset) {
@@ -1241,6 +1334,12 @@ void SCENE_OT_freestyle_module_add(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Free Style Module Remove Operator
+ * \{ */
+
static int freestyle_module_remove_exec(bContext *C, wmOperator *UNUSED(op))
{
Scene *scene = CTX_data_scene(C);
@@ -1287,6 +1386,12 @@ static int freestyle_module_move_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Free Style Module Move Operator
+ * \{ */
+
void SCENE_OT_freestyle_module_move(wmOperatorType *ot)
{
static const EnumPropertyItem direction_items[] = {
@@ -1316,6 +1421,12 @@ void SCENE_OT_freestyle_module_move(wmOperatorType *ot)
"Direction to move the chosen style module towards");
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Free Style Line Set Add Operator
+ * \{ */
+
static int freestyle_lineset_add_exec(bContext *C, wmOperator *UNUSED(op))
{
Main *bmain = CTX_data_main(C);
@@ -1344,6 +1455,12 @@ void SCENE_OT_freestyle_lineset_add(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Free Style Line Set Copy Operator
+ * \{ */
+
static bool freestyle_active_lineset_poll(bContext *C)
{
ViewLayer *view_layer = CTX_data_view_layer(C);
@@ -1379,6 +1496,12 @@ void SCENE_OT_freestyle_lineset_copy(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Free Style Line Set Paste Operator
+ * \{ */
+
static int freestyle_lineset_paste_exec(bContext *C, wmOperator *UNUSED(op))
{
Scene *scene = CTX_data_scene(C);
@@ -1407,6 +1530,12 @@ void SCENE_OT_freestyle_lineset_paste(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Free Style Line Set Remove Operator
+ * \{ */
+
static int freestyle_lineset_remove_exec(bContext *C, wmOperator *UNUSED(op))
{
Scene *scene = CTX_data_scene(C);
@@ -1435,6 +1564,12 @@ void SCENE_OT_freestyle_lineset_remove(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Free Style Line Set Move Operator
+ * \{ */
+
static int freestyle_lineset_move_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
@@ -1478,6 +1613,12 @@ void SCENE_OT_freestyle_lineset_move(wmOperatorType *ot)
"Direction to move the active line set towards");
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Free Style Line Set New Operator
+ * \{ */
+
static int freestyle_linestyle_new_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
@@ -1516,6 +1657,12 @@ void SCENE_OT_freestyle_linestyle_new(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Free Style Modifier Add "Color" Operator
+ * \{ */
+
static int freestyle_color_modifier_add_exec(bContext *C, wmOperator *op)
{
ViewLayer *view_layer = CTX_data_view_layer(C);
@@ -1557,6 +1704,12 @@ void SCENE_OT_freestyle_color_modifier_add(wmOperatorType *ot)
ot->srna, "type", rna_enum_linestyle_color_modifier_type_items, 0, "Type", "");
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Free Style Modifier Add "Alpha" Operator
+ * \{ */
+
static int freestyle_alpha_modifier_add_exec(bContext *C, wmOperator *op)
{
ViewLayer *view_layer = CTX_data_view_layer(C);
@@ -1598,6 +1751,12 @@ void SCENE_OT_freestyle_alpha_modifier_add(wmOperatorType *ot)
ot->srna, "type", rna_enum_linestyle_alpha_modifier_type_items, 0, "Type", "");
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Free Style Modifier Add "Thickness" Operator
+ * \{ */
+
static int freestyle_thickness_modifier_add_exec(bContext *C, wmOperator *op)
{
ViewLayer *view_layer = CTX_data_view_layer(C);
@@ -1639,6 +1798,12 @@ void SCENE_OT_freestyle_thickness_modifier_add(wmOperatorType *ot)
ot->srna, "type", rna_enum_linestyle_thickness_modifier_type_items, 0, "Type", "");
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Free Style Modifier Add "Geometry" Operator
+ * \{ */
+
static int freestyle_geometry_modifier_add_exec(bContext *C, wmOperator *op)
{
ViewLayer *view_layer = CTX_data_view_layer(C);
@@ -1680,6 +1845,12 @@ void SCENE_OT_freestyle_geometry_modifier_add(wmOperatorType *ot)
ot->srna, "type", rna_enum_linestyle_geometry_modifier_type_items, 0, "Type", "");
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Free Style Modifier Remove Operator
+ * \{ */
+
static int freestyle_get_modifier_type(PointerRNA *ptr)
{
if (RNA_struct_is_a(ptr->type, &RNA_LineStyleColorModifier)) {
@@ -1747,6 +1918,12 @@ void SCENE_OT_freestyle_modifier_remove(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Free Style Modifier Copy Operator
+ * \{ */
+
static int freestyle_modifier_copy_exec(bContext *C, wmOperator *op)
{
ViewLayer *view_layer = CTX_data_view_layer(C);
@@ -1797,6 +1974,12 @@ void SCENE_OT_freestyle_modifier_copy(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Free Style Modifier Move Operator
+ * \{ */
+
static int freestyle_modifier_move_exec(bContext *C, wmOperator *op)
{
ViewLayer *view_layer = CTX_data_view_layer(C);
@@ -1866,6 +2049,12 @@ void SCENE_OT_freestyle_modifier_move(wmOperatorType *ot)
"Direction to move the chosen modifier towards");
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Free Style Stroke Material Create Operator
+ * \{ */
+
static int freestyle_stroke_material_create_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
@@ -1898,6 +2087,12 @@ void SCENE_OT_freestyle_stroke_material_create(wmOperatorType *ot)
#endif /* WITH_FREESTYLE */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Texture Slot Move Operator
+ * \{ */
+
static int texture_slot_move_exec(bContext *C, wmOperator *op)
{
ID *id = CTX_data_pointer_get_type(C, "texture_slot", &RNA_TextureSlot).owner_id;
@@ -1966,7 +2161,11 @@ void TEXTURE_OT_slot_move(wmOperatorType *ot)
RNA_def_enum(ot->srna, "type", slot_move, 0, "Type", "");
}
-/********************** material operators *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Material Copy Operator
+ * \{ */
/* material copy/paste */
static int copy_material_exec(bContext *C, wmOperator *UNUSED(op))
@@ -1997,6 +2196,12 @@ void MATERIAL_OT_copy(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_INTERNAL;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Material Paste Operator
+ * \{ */
+
static int paste_material_exec(bContext *C, wmOperator *UNUSED(op))
{
Material *ma = CTX_data_pointer_get_type(C, "material", &RNA_Material).data;
@@ -2027,6 +2232,12 @@ void MATERIAL_OT_paste(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name #MTex Copy/Paste Utilities
+ * \{ */
+
static short mtexcopied = 0; /* must be reset on file load */
static MTex mtexcopybuf;
@@ -2093,6 +2304,12 @@ static void paste_mtex_copybuf(ID *id)
}
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Texture Slot Copy Operator
+ * \{ */
+
static int copy_mtex_exec(bContext *C, wmOperator *UNUSED(op))
{
ID *id = CTX_data_pointer_get_type(C, "texture_slot", &RNA_TextureSlot).owner_id;
@@ -2131,6 +2348,12 @@ void TEXTURE_OT_slot_copy(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_INTERNAL;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Texture Slot Paste Operator
+ * \{ */
+
static int paste_mtex_exec(bContext *C, wmOperator *UNUSED(op))
{
ID *id = CTX_data_pointer_get_type(C, "texture_slot", &RNA_TextureSlot).owner_id;
@@ -2185,3 +2408,5 @@ void TEXTURE_OT_slot_paste(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
}
+
+/** \} */
diff --git a/source/blender/editors/render/render_update.c b/source/blender/editors/render/render_update.c
index 7d0ad42c703..ce454d5eac2 100644
--- a/source/blender/editors/render/render_update.c
+++ b/source/blender/editors/render/render_update.c
@@ -194,7 +194,7 @@ void ED_render_engine_changed(Main *bmain, const bool update_scene_data)
update_ctx.scene = scene;
LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
/* TDODO(sergey): Iterate over depsgraphs instead? */
- update_ctx.depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, true);
+ update_ctx.depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer);
update_ctx.view_layer = view_layer;
ED_render_id_flush_update(&update_ctx, &scene->id);
}
diff --git a/source/blender/editors/scene/scene_edit.c b/source/blender/editors/scene/scene_edit.c
index d599c1cbcf0..47edb322701 100644
--- a/source/blender/editors/scene/scene_edit.c
+++ b/source/blender/editors/scene/scene_edit.c
@@ -116,7 +116,7 @@ bool ED_scene_delete(bContext *C, Main *bmain, Scene *scene)
/* Depsgraph updates after scene becomes active in a window. */
void ED_scene_change_update(Main *bmain, Scene *scene, ViewLayer *layer)
{
- Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, layer, true);
+ Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, layer);
BKE_scene_set_background(bmain, scene);
DEG_graph_relations_update(depsgraph);
diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c
index 5004b0132c2..921cc92299e 100644
--- a/source/blender/editors/screen/area.c
+++ b/source/blender/editors/screen/area.c
@@ -302,8 +302,6 @@ static void area_azone_tag_update(ScrArea *area)
static void region_draw_azones(ScrArea *area, ARegion *region)
{
- AZone *az;
-
if (!area) {
return;
}
@@ -314,7 +312,7 @@ static void region_draw_azones(ScrArea *area, ARegion *region)
GPU_matrix_push();
GPU_matrix_translate_2f(-region->winrct.xmin, -region->winrct.ymin);
- for (az = area->actionzones.first; az; az = az->next) {
+ LISTBASE_FOREACH (AZone *, az, &area->actionzones) {
/* test if action zone is over this region */
rcti azrct;
BLI_rcti_init(&azrct, az->x1, az->x2, az->y1, az->y2);
@@ -353,11 +351,9 @@ static void region_draw_status_text(ScrArea *area, ARegion *region)
if (overlap) {
GPU_clear_color(0.0f, 0.0f, 0.0f, 0.0f);
- GPU_clear(GPU_COLOR_BIT);
}
else {
UI_ThemeClearColor(TH_HEADER);
- GPU_clear(GPU_COLOR_BIT);
}
int fontid = BLF_set_default();
@@ -521,7 +517,6 @@ void ED_region_do_draw(bContext *C, ARegion *region)
if (area && area_is_pseudo_minimized(area)) {
UI_ThemeClearColor(TH_EDITOR_OUTLINE);
- GPU_clear(GPU_COLOR_BIT);
return;
}
/* optional header info instead? */
@@ -705,10 +700,8 @@ void ED_region_tag_redraw_partial(ARegion *region, const rcti *rct, bool rebuild
void ED_area_tag_redraw(ScrArea *area)
{
- ARegion *region;
-
if (area) {
- for (region = area->regionbase.first; region; region = region->next) {
+ LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
ED_region_tag_redraw(region);
}
}
@@ -716,10 +709,8 @@ void ED_area_tag_redraw(ScrArea *area)
void ED_area_tag_redraw_no_rebuild(ScrArea *area)
{
- ARegion *region;
-
if (area) {
- for (region = area->regionbase.first; region; region = region->next) {
+ LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
ED_region_tag_redraw_no_rebuild(region);
}
}
@@ -727,10 +718,8 @@ void ED_area_tag_redraw_no_rebuild(ScrArea *area)
void ED_area_tag_redraw_regiontype(ScrArea *area, int regiontype)
{
- ARegion *region;
-
if (area) {
- for (region = area->regionbase.first; region; region = region->next) {
+ LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
if (region->regiontype == regiontype) {
ED_region_tag_redraw(region);
}
@@ -750,14 +739,12 @@ void ED_area_tag_refresh(ScrArea *area)
/* use NULL to disable it */
void ED_area_status_text(ScrArea *area, const char *str)
{
- ARegion *region;
-
/* happens when running transform operators in background mode */
if (area == NULL) {
return;
}
- for (region = area->regionbase.first; region; region = region->next) {
+ LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
if (region->regiontype == RGN_TYPE_HEADER) {
if (str) {
if (region->headerstr == NULL) {
@@ -942,7 +929,6 @@ static void region_azone_edge(AZone *az, ARegion *region)
/* region already made zero sized, in shape of edge */
static void region_azone_tab_plus(ScrArea *area, AZone *az, ARegion *region)
{
- AZone *azt;
int tot = 0, add;
/* Edge offset multiplied by the */
@@ -950,7 +936,7 @@ static void region_azone_tab_plus(ScrArea *area, AZone *az, ARegion *region)
const float tab_size_x = 0.7f * U.widget_unit;
const float tab_size_y = 0.4f * U.widget_unit;
- for (azt = area->actionzones.first; azt; azt = azt->next) {
+ LISTBASE_FOREACH (AZone *, azt, &area->actionzones) {
if (azt->edge == az->edge) {
tot++;
}
@@ -1846,7 +1832,6 @@ void ED_area_init(wmWindowManager *wm, wmWindow *win, ScrArea *area)
WorkSpace *workspace = WM_window_get_active_workspace(win);
const bScreen *screen = BKE_workspace_active_screen_get(win->workspace_hook);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
- ARegion *region;
rcti rect, overlap_rect;
rcti window_rect;
@@ -1863,7 +1848,7 @@ void ED_area_init(wmWindowManager *wm, wmWindow *win, ScrArea *area)
area->type = BKE_spacetype_from_id(area->spacetype);
}
- for (region = area->regionbase.first; region; region = region->next) {
+ LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
region->type = BKE_regiontype_from_id_or_first(area->type, region->regiontype);
}
@@ -1887,7 +1872,7 @@ void ED_area_init(wmWindowManager *wm, wmWindow *win, ScrArea *area)
area_azone_init(win, screen, area);
/* region windows, default and own handlers */
- for (region = area->regionbase.first; region; region = region->next) {
+ LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
region_subwindow(region);
if (region->visible) {
@@ -2006,7 +1991,6 @@ void ED_region_toggle_hidden(bContext *C, ARegion *region)
void ED_area_data_copy(ScrArea *area_dst, ScrArea *area_src, const bool do_free)
{
SpaceType *st;
- ARegion *region;
const char spacetype = area_dst->spacetype;
const short flag_copy = HEADER_NO_PULLDOWN;
@@ -2026,13 +2010,13 @@ void ED_area_data_copy(ScrArea *area_dst, ScrArea *area_src, const bool do_free)
/* regions */
if (do_free) {
st = BKE_spacetype_from_id(spacetype);
- for (region = area_dst->regionbase.first; region; region = region->next) {
+ LISTBASE_FOREACH (ARegion *, region, &area_dst->regionbase) {
BKE_area_region_free(st, region);
}
BLI_freelistN(&area_dst->regionbase);
}
st = BKE_spacetype_from_id(area_src->spacetype);
- for (region = area_src->regionbase.first; region; region = region->next) {
+ LISTBASE_FOREACH (ARegion *, region, &area_src->regionbase) {
ARegion *newar = BKE_area_region_copy(st, region);
BLI_addtail(&area_dst->regionbase, newar);
}
@@ -2320,7 +2304,6 @@ void ED_area_newspace(bContext *C, ScrArea *area, int type, const bool skip_regi
if (area->spacetype != type) {
SpaceType *st;
SpaceLink *slold = area->spacedata.first;
- SpaceLink *sl;
/* store area->type->exit callback */
void *area_exit = area->type ? area->type->exit : NULL;
/* When the user switches between space-types from the type-selector,
@@ -2364,8 +2347,10 @@ void ED_area_newspace(bContext *C, ScrArea *area, int type, const bool skip_regi
* (e.g. with properties editor) until space-data is properly created */
/* check previously stored space */
- for (sl = area->spacedata.first; sl; sl = sl->next) {
- if (sl->spacetype == type) {
+ SpaceLink *sl = NULL;
+ LISTBASE_FOREACH (SpaceLink *, sl_iter, &area->spacedata) {
+ if (sl_iter->spacetype == type) {
+ sl = sl_iter;
break;
}
}
@@ -2541,11 +2526,9 @@ static void region_clear_color(const bContext *C, const ARegion *region, ThemeCo
float back[4];
UI_GetThemeColor4fv(colorid, back);
GPU_clear_color(back[3] * back[0], back[3] * back[1], back[3] * back[2], back[3]);
- GPU_clear(GPU_COLOR_BIT);
}
else {
UI_ThemeClearColor(colorid);
- GPU_clear(GPU_COLOR_BIT);
}
}
@@ -2569,14 +2552,12 @@ BLI_INLINE bool streq_array_any(const char *s, const char *arr[])
* correct old \a uiBlock, and NULL otherwise.
*/
static void ed_panel_draw(const bContext *C,
- ScrArea *area,
ARegion *region,
ListBase *lb,
PanelType *pt,
Panel *panel,
int w,
int em,
- bool vertical,
char *unique_panel_str)
{
const uiStyle *style = UI_style_get_dpi();
@@ -2592,13 +2573,13 @@ static void ed_panel_draw(const bContext *C,
uiBlock *block = UI_block_begin(C, region, block_name, UI_EMBOSS);
bool open;
- panel = UI_panel_begin(area, region, lb, block, pt, panel, &open);
+ panel = UI_panel_begin(region, lb, block, pt, panel, &open);
/* bad fixed values */
int xco, yco, h = 0;
int headerend = w - UI_UNIT_X;
- if (pt->draw_header_preset && !(pt->flag & PNL_NO_HEADER) && (open || vertical)) {
+ if (pt->draw_header_preset && !(pt->flag & PNL_NO_HEADER)) {
/* for preset menu */
panel->layout = UI_block_layout(block,
UI_LAYOUT_HORIZONTAL,
@@ -2617,7 +2598,7 @@ static void ed_panel_draw(const bContext *C,
panel->layout = NULL;
}
- if (pt->draw_header && !(pt->flag & PNL_NO_HEADER) && (open || vertical)) {
+ if (pt->draw_header && !(pt->flag & PNL_NO_HEADER)) {
int labelx, labely;
UI_panel_label_offset(block, &labelx, &labely);
@@ -2694,21 +2675,12 @@ static void ed_panel_draw(const bContext *C,
Panel *child_panel = UI_panel_find_by_type(&panel->children, child_pt);
if (child_pt->draw && (!child_pt->poll || child_pt->poll(C, child_pt))) {
- ed_panel_draw(C,
- area,
- region,
- &panel->children,
- child_pt,
- child_panel,
- w,
- em,
- vertical,
- unique_panel_str);
+ ed_panel_draw(C, region, &panel->children, child_pt, child_panel, w, em, unique_panel_str);
}
}
}
- UI_panel_end(area, region, block, w, h, open);
+ UI_panel_end(region, block, w, h, open);
}
/**
@@ -2720,14 +2692,12 @@ void ED_region_panels_layout_ex(const bContext *C,
ARegion *region,
ListBase *paneltypes,
const char *contexts[],
- int contextnr,
- const bool vertical,
const char *category_override)
{
/* collect panels to draw */
WorkSpace *workspace = CTX_wm_workspace(C);
LinkNode *panel_types_stack = NULL;
- for (PanelType *pt = paneltypes->last; pt; pt = pt->prev) {
+ LISTBASE_FOREACH_BACKWARD (PanelType *, pt, paneltypes) {
/* Only draw top level panels. */
if (pt->parent) {
continue;
@@ -2772,25 +2742,13 @@ void ED_region_panels_layout_ex(const bContext *C,
const int category_tabs_width = UI_PANEL_CATEGORY_MARGIN_WIDTH;
int margin_x = 0;
const bool region_layout_based = region->flag & RGN_FLAG_DYNAMIC_SIZE;
- const bool is_context_new = (contextnr != -1) ? UI_view2d_tab_set(v2d, contextnr) : false;
bool update_tot_size = true;
- /* before setting the view */
- if (vertical) {
- /* only allow scrolling in vertical direction */
- v2d->keepofs |= V2D_LOCKOFS_X | V2D_KEEPOFS_Y;
- v2d->keepofs &= ~(V2D_LOCKOFS_Y | V2D_KEEPOFS_X);
- v2d->scroll &= ~V2D_SCROLL_BOTTOM;
- v2d->scroll |= V2D_SCROLL_RIGHT;
- }
- else {
- /* for now, allow scrolling in both directions (since layouts are optimized for vertical,
- * they often don't fit in horizontal layout)
- */
- v2d->keepofs &= ~(V2D_LOCKOFS_X | V2D_LOCKOFS_Y | V2D_KEEPOFS_X | V2D_KEEPOFS_Y);
- v2d->scroll |= V2D_SCROLL_BOTTOM;
- v2d->scroll &= ~V2D_SCROLL_RIGHT;
- }
+ /* only allow scrolling in vertical direction */
+ v2d->keepofs |= V2D_LOCKOFS_X | V2D_KEEPOFS_Y;
+ v2d->keepofs &= ~(V2D_LOCKOFS_Y | V2D_KEEPOFS_X);
+ v2d->scroll &= ~V2D_SCROLL_BOTTOM;
+ v2d->scroll |= V2D_SCROLL_RIGHT;
/* collect categories */
if (use_category_tabs) {
@@ -2815,14 +2773,8 @@ void ED_region_panels_layout_ex(const bContext *C,
}
}
- if (vertical) {
- w = BLI_rctf_size_x(&v2d->cur);
- em = (region->type->prefsizex) ? 10 : 20; /* works out to 10*UI_UNIT_X or 20*UI_UNIT_X */
- }
- else {
- w = UI_PANEL_WIDTH;
- em = (region->type->prefsizex) ? 10 : 20;
- }
+ w = BLI_rctf_size_x(&v2d->cur);
+ em = (region->type->prefsizex) ? 10 : 20; /* works out to 10*UI_UNIT_X or 20*UI_UNIT_X */
w -= margin_x;
int w_box_panel = w - UI_PANEL_BOX_STYLE_MARGIN * 2.0f;
@@ -2855,14 +2807,12 @@ void ED_region_panels_layout_ex(const bContext *C,
}
ed_panel_draw(C,
- area,
region,
&region->panels,
pt,
panel,
(pt->flag & PNL_DRAW_BOX) ? w_box_panel : w,
em,
- vertical,
NULL);
}
@@ -2890,14 +2840,12 @@ void ED_region_panels_layout_ex(const bContext *C,
char unique_panel_str[8];
UI_list_panel_unique_str(panel, unique_panel_str);
ed_panel_draw(C,
- area,
region,
&region->panels,
panel->type,
panel,
(panel->type->flag & PNL_DRAW_BOX) ? w_box_panel : w,
em,
- vertical,
unique_panel_str);
}
}
@@ -2925,7 +2873,7 @@ void ED_region_panels_layout_ex(const bContext *C,
y = fabsf(region->sizey * UI_DPI_FAC - 1);
}
}
- else if (vertical) {
+ else {
/* We always keep the scroll offset -
* so the total view gets increased with the scrolled away part. */
if (v2d->cur.ymax < -FLT_EPSILON) {
@@ -2940,19 +2888,6 @@ void ED_region_panels_layout_ex(const bContext *C,
y = -y;
}
- else {
- /* don't jump back when panels close or hide */
- if (!is_context_new) {
- if (v2d->tot.xmax > v2d->winx) {
- x = max_ii(x, 0);
- }
- else {
- x = max_ii(x, v2d->cur.xmax);
- }
- }
-
- y = -y;
- }
if (update_tot_size) {
/* this also changes the 'cur' */
@@ -2966,8 +2901,7 @@ void ED_region_panels_layout_ex(const bContext *C,
void ED_region_panels_layout(const bContext *C, ARegion *region)
{
- bool vertical = true;
- ED_region_panels_layout_ex(C, region, &region->type->paneltypes, NULL, -1, vertical, NULL);
+ ED_region_panels_layout_ex(C, region, &region->type->paneltypes, NULL, NULL);
}
void ED_region_panels_draw(const bContext *C, ARegion *region)
@@ -3011,12 +2945,10 @@ void ED_region_panels_draw(const bContext *C, ARegion *region)
UI_view2d_scrollers_draw(v2d, mask);
}
-void ED_region_panels_ex(
- const bContext *C, ARegion *region, const char *contexts[], int contextnr, const bool vertical)
+void ED_region_panels_ex(const bContext *C, ARegion *region, const char *contexts[])
{
/* TODO: remove? */
- ED_region_panels_layout_ex(
- C, region, &region->type->paneltypes, contexts, contextnr, vertical, NULL);
+ ED_region_panels_layout_ex(C, region, &region->type->paneltypes, contexts, NULL);
ED_region_panels_draw(C, region);
}
@@ -3042,7 +2974,6 @@ void ED_region_header_layout(const bContext *C, ARegion *region)
const uiStyle *style = UI_style_get_dpi();
uiBlock *block;
uiLayout *layout;
- HeaderType *ht;
Header header = {NULL};
bool region_layout_based = region->flag & RGN_FLAG_DYNAMIC_SIZE;
@@ -3065,7 +2996,7 @@ void ED_region_header_layout(const bContext *C, ARegion *region)
UI_view2d_view_ortho(&region->v2d);
/* draw all headers types */
- for (ht = region->type->headertypes.first; ht; ht = ht->next) {
+ LISTBASE_FOREACH (HeaderType *, ht, &region->type->headertypes) {
if (ht->poll && !ht->poll(C, ht)) {
continue;
}
diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c
index c17a34f97b9..3c70bf1bfd8 100644
--- a/source/blender/editors/screen/screen_context.c
+++ b/source/blender/editors/screen/screen_context.c
@@ -204,7 +204,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
}
if (CTX_data_equals(member, "visible_bones") || CTX_data_equals(member, "editable_bones")) {
bArmature *arm = (obedit && obedit->type == OB_ARMATURE) ? obedit->data : NULL;
- EditBone *ebone, *flipbone = NULL;
+ EditBone *flipbone = NULL;
const bool editable_bones = CTX_data_equals(member, "editable_bones");
if (arm && arm->edbo) {
@@ -216,7 +216,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
arm = ob->data;
/* Attention: X-Axis Mirroring is also handled here... */
- for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
+ LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
/* first and foremost, bone must be visible and selected */
if (EBONE_VISIBLE(arm, ebone)) {
/* Get 'x-axis mirror equivalent' bone if the X-Axis Mirroring option is enabled
@@ -262,7 +262,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
if (CTX_data_equals(member, "selected_bones") ||
CTX_data_equals(member, "selected_editable_bones")) {
bArmature *arm = (obedit && obedit->type == OB_ARMATURE) ? obedit->data : NULL;
- EditBone *ebone, *flipbone = NULL;
+ EditBone *flipbone = NULL;
const bool selected_editable_bones = CTX_data_equals(member, "selected_editable_bones");
if (arm && arm->edbo) {
@@ -274,7 +274,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
arm = ob->data;
/* Attention: X-Axis Mirroring is also handled here... */
- for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
+ LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
/* first and foremost, bone must be visible and selected */
if (EBONE_VISIBLE(arm, ebone) && (ebone->flag & BONE_SELECTED)) {
/* Get 'x-axis mirror equivalent' bone if the X-Axis Mirroring option is enabled
@@ -479,8 +479,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
if (CTX_data_equals(member, "sequences")) {
Editing *ed = BKE_sequencer_editing_get(scene, false);
if (ed) {
- Sequence *seq;
- for (seq = ed->seqbasep->first; seq; seq = seq->next) {
+ LISTBASE_FOREACH (Sequence *, seq, ed->seqbasep) {
CTX_data_list_add(result, &scene->id, &RNA_Sequence, seq);
}
CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
@@ -491,8 +490,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
if (CTX_data_equals(member, "selected_sequences")) {
Editing *ed = BKE_sequencer_editing_get(scene, false);
if (ed) {
- Sequence *seq;
- for (seq = ed->seqbasep->first; seq; seq = seq->next) {
+ LISTBASE_FOREACH (Sequence *, seq, ed->seqbasep) {
if (seq->flag & SELECT) {
CTX_data_list_add(result, &scene->id, &RNA_Sequence, seq);
}
@@ -505,8 +503,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
if (CTX_data_equals(member, "selected_editable_sequences")) {
Editing *ed = BKE_sequencer_editing_get(scene, false);
if (ed) {
- Sequence *seq;
- for (seq = ed->seqbasep->first; seq; seq = seq->next) {
+ LISTBASE_FOREACH (Sequence *, seq, ed->seqbasep) {
if (seq->flag & SELECT && !(seq->flag & SEQ_LOCK)) {
CTX_data_list_add(result, &scene->id, &RNA_Sequence, seq);
}
@@ -520,16 +517,14 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
bAnimContext ac;
if (ANIM_animdata_get_context(C, &ac) != 0) {
ListBase anim_data = {NULL, NULL};
- bAnimListElem *ale;
ANIM_animdata_filter(&ac, &anim_data, ANIMFILTER_DATA_VISIBLE, ac.data, ac.datatype);
- for (ale = anim_data.first; ale; ale = ale->next) {
+ LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
if (ale->datatype != ALE_NLASTRIP) {
continue;
}
NlaTrack *nlt = (NlaTrack *)ale->data;
- NlaStrip *strip;
- for (strip = nlt->strips.first; strip; strip = strip->next) {
+ LISTBASE_FOREACH (NlaStrip *, strip, &nlt->strips) {
if (strip->flag & NLASTRIP_FLAG_SELECT) {
CTX_data_list_add(result, &scene->id, &RNA_NlaStrip, strip);
}
@@ -637,9 +632,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
bGPdata *gpd = ED_gpencil_data_get_active_direct(area, obact);
if (gpd) {
- bGPDlayer *gpl;
-
- for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
if ((gpl->flag & GP_LAYER_HIDE) == 0) {
CTX_data_list_add(result, &gpd->id, &RNA_GPencilLayer, gpl);
}
@@ -653,9 +646,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
bGPdata *gpd = ED_gpencil_data_get_active_direct(area, obact);
if (gpd) {
- bGPDlayer *gpl;
-
- for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
if (BKE_gpencil_layer_is_editable(gpl)) {
CTX_data_list_add(result, &gpd->id, &RNA_GPencilLayer, gpl);
}
@@ -670,12 +661,9 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
if (gpd) {
- bGPDlayer *gpl;
-
- for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe)) {
bGPDframe *gpf;
- bGPDstroke *gps;
bGPDframe *init_gpf = gpl->actframe;
if (is_multiedit) {
init_gpf = gpl->frames.first;
@@ -683,7 +671,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
for (gpf = init_gpf; gpf; gpf = gpf->next) {
if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
- for (gps = gpf->strokes.first; gps; gps = gps->next) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
if (ED_gpencil_stroke_can_use_direct(area, gps)) {
/* check if the color is editable */
if (ED_gpencil_stroke_color_use(obact, gpl, gps) == false) {
diff --git a/source/blender/editors/screen/screen_draw.c b/source/blender/editors/screen/screen_draw.c
index a5d3c4842e6..8ded845b008 100644
--- a/source/blender/editors/screen/screen_draw.c
+++ b/source/blender/editors/screen/screen_draw.c
@@ -379,11 +379,9 @@ void ED_screen_draw_edges(wmWindow *win)
float col[4], corner_scale, edge_thickness;
int verts_per_corner = 0;
- ScrArea *area;
-
rcti scissor_rect;
BLI_rcti_init_minmax(&scissor_rect);
- for (area = screen->areabase.first; area; area = area->next) {
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
BLI_rcti_do_minmax_v(&scissor_rect, (int[2]){area->v1->vec.x, area->v1->vec.y});
BLI_rcti_do_minmax_v(&scissor_rect, (int[2]){area->v3->vec.x, area->v3->vec.y});
}
@@ -418,7 +416,7 @@ void ED_screen_draw_edges(wmWindow *win)
GPU_batch_uniform_1f(batch, "scale", corner_scale);
GPU_batch_uniform_4fv(batch, "color", col);
- for (area = screen->areabase.first; area; area = area->next) {
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
drawscredge_area(area, winsize_x, winsize_y, edge_thickness);
}
@@ -608,8 +606,8 @@ void ED_screen_preview_render(const bScreen *screen, int size_x, int size_y, uin
GPUOffScreen *offscreen = GPU_offscreen_create(size_x, size_y, true, false, err_out);
GPU_offscreen_bind(offscreen, true);
- GPU_clear_color(0.0, 0.0, 0.0, 0.0);
- GPU_clear(GPU_COLOR_BIT | GPU_DEPTH_BIT);
+ GPU_clear_color(0.0f, 0.0f, 0.0f, 0.0f);
+ GPU_clear_depth(1.0f);
screen_preview_draw(screen, size_x, size_y);
diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c
index dbf84cad80b..f534296bd0b 100644
--- a/source/blender/editors/screen/screen_edit.c
+++ b/source/blender/editors/screen/screen_edit.c
@@ -230,8 +230,7 @@ bScreen *screen_add(Main *bmain, const char *name, const rcti *rect)
void screen_data_copy(bScreen *to, bScreen *from)
{
- ScrVert *s1, *s2;
- ScrEdge *se;
+ ScrVert *s2;
ScrArea *area, *saf;
/* free contents of 'to', is from blenkernel screen.c */
@@ -245,11 +244,11 @@ void screen_data_copy(bScreen *to, bScreen *from)
BLI_listbase_clear(&to->regionbase);
s2 = to->vertbase.first;
- for (s1 = from->vertbase.first; s1; s1 = s1->next, s2 = s2->next) {
+ for (ScrVert *s1 = from->vertbase.first; s1; s1 = s1->next, s2 = s2->next) {
s1->newv = s2;
}
- for (se = to->edgebase.first; se; se = se->next) {
+ LISTBASE_FOREACH (ScrEdge *, se, &to->edgebase) {
se->v1 = se->v1->newv;
se->v2 = se->v2->newv;
BKE_screen_sort_scrvert(&(se->v1), &(se->v2));
@@ -271,7 +270,7 @@ void screen_data_copy(bScreen *to, bScreen *from)
}
/* put at zero (needed?) */
- for (s1 = from->vertbase.first; s1; s1 = s1->next) {
+ LISTBASE_FOREACH (ScrVert *, s1, &from->vertbase) {
s1->newv = NULL;
}
}
@@ -538,9 +537,7 @@ void ED_screen_refresh(wmWindowManager *wm, wmWindow *win)
/* file read, set all screens, ... */
void ED_screens_init(Main *bmain, wmWindowManager *wm)
{
- wmWindow *win;
-
- for (win = wm->windows.first; win; win = win->next) {
+ LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
if (BKE_workspace_active_get(win->workspace_hook) == NULL) {
BKE_workspace_active_set(win->workspace_hook, bmain->workspaces.first);
}
@@ -552,7 +549,7 @@ void ED_screens_init(Main *bmain, wmWindowManager *wm)
}
if (U.uiflag & USER_HEADER_FROM_PREF) {
- for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
+ LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
BKE_screen_header_alignment_reset(screen);
}
}
@@ -614,7 +611,6 @@ void ED_area_exit(bContext *C, ScrArea *area)
wmWindowManager *wm = CTX_wm_manager(C);
wmWindow *win = CTX_wm_window(C);
ScrArea *prevsa = CTX_wm_area(C);
- ARegion *region;
if (area->type && area->type->exit) {
area->type->exit(wm, area);
@@ -622,7 +618,7 @@ void ED_area_exit(bContext *C, ScrArea *area)
CTX_wm_area_set(C, area);
- for (region = area->regionbase.first; region; region = region->next) {
+ LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
ED_region_exit(C, region);
}
@@ -683,10 +679,11 @@ static void screen_cursor_set(wmWindow *win, const int xy[2])
{
const bScreen *screen = WM_window_get_active_screen(win);
AZone *az = NULL;
- ScrArea *area;
+ ScrArea *area = NULL;
- for (area = screen->areabase.first; area; area = area->next) {
- if ((az = ED_area_actionzone_find_xy(area, xy))) {
+ LISTBASE_FOREACH (ScrArea *, area_iter, &screen->areabase) {
+ if ((az = ED_area_actionzone_find_xy(area_iter, xy))) {
+ area = area_iter;
break;
}
}
@@ -733,7 +730,6 @@ void ED_screen_set_active_region(bContext *C, wmWindow *win, const int xy[2])
}
ScrArea *area = NULL;
- ARegion *region;
ARegion *region_prev = screen->active_region;
ED_screen_areas_iter (win, screen, area_iter) {
@@ -750,7 +746,7 @@ void ED_screen_set_active_region(bContext *C, wmWindow *win, const int xy[2])
}
if (area) {
/* Make overlap active when mouse over. */
- for (region = area->regionbase.first; region; region = region->next) {
+ LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
if (ED_region_contains_xy(region, xy)) {
screen->active_region = region;
break;
@@ -767,8 +763,7 @@ void ED_screen_set_active_region(bContext *C, wmWindow *win, const int xy[2])
ED_screen_areas_iter (win, screen, area_iter) {
bool do_draw = false;
- for (region = area_iter->regionbase.first; region; region = region->next) {
-
+ LISTBASE_FOREACH (ARegion *, region, &area_iter->regionbase) {
/* Call old area's deactivate if assigned. */
if (region == region_prev && area_iter->type->deactivate) {
area_iter->type->deactivate(area_iter);
@@ -789,7 +784,7 @@ void ED_screen_set_active_region(bContext *C, wmWindow *win, const int xy[2])
}
if (do_draw) {
- for (region = area_iter->regionbase.first; region; region = region->next) {
+ LISTBASE_FOREACH (ARegion *, region, &area_iter->regionbase) {
if (ELEM(region->regiontype, RGN_TYPE_HEADER, RGN_TYPE_TOOL_HEADER)) {
ED_region_tag_redraw_no_rebuild(region);
}
@@ -826,13 +821,12 @@ int ED_screen_area_active(const bContext *C)
if (win && screen && area) {
AZone *az = ED_area_actionzone_find_xy(area, &win->eventstate->x);
- ARegion *region;
if (az && az->type == AZONE_REGION) {
return 1;
}
- for (region = area->regionbase.first; region; region = region->next) {
+ LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
if (region == screen->active_region) {
return 1;
}
@@ -883,10 +877,10 @@ static void screen_global_area_refresh(wmWindow *win,
const short height_min,
const short height_max)
{
- ScrArea *area;
-
- for (area = win->global_areas.areabase.first; area; area = area->next) {
- if (area->spacetype == space_type) {
+ ScrArea *area = NULL;
+ LISTBASE_FOREACH (ScrArea *, area_iter, &win->global_areas.areabase) {
+ if (area_iter->spacetype == space_type) {
+ area = area_iter;
break;
}
}
@@ -1081,7 +1075,6 @@ static void screen_set_3dview_camera(Scene *scene,
v3d->camera = BKE_view_layer_camera_find(view_layer);
// XXX if (screen == curscreen) handle_view3d_lock();
if (!v3d->camera) {
- ARegion *region;
ListBase *regionbase;
/* regionbase is in different place depending if space is active */
@@ -1092,7 +1085,7 @@ static void screen_set_3dview_camera(Scene *scene,
regionbase = &v3d->regionbase;
}
- for (region = regionbase->first; region; region = region->next) {
+ LISTBASE_FOREACH (ARegion *, region, regionbase) {
if (region->regiontype == RGN_TYPE_WINDOW) {
RegionView3D *rv3d = region->regiondata;
if (rv3d->persp == RV3D_CAMOB) {
@@ -1240,13 +1233,12 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *area, const
wmWindowManager *wm = CTX_wm_manager(C);
WorkSpace *workspace = WM_window_get_active_workspace(win);
bScreen *screen, *oldscreen;
- ARegion *region;
if (area) {
/* ensure we don't have a button active anymore, can crash when
* switching screens with tooltip open because region and tooltip
* are no longer in the same screen */
- for (region = area->regionbase.first; region; region = region->next) {
+ LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
UI_blocklist_free(C, &region->uiblocks);
if (region->regiontimer) {
@@ -1299,7 +1291,7 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *area, const
glob_area->global->flag &= ~GLOBAL_AREA_IS_HIDDEN;
}
/* restore the old side panels/header visibility */
- for (region = area->regionbase.first; region; region = region->next) {
+ LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
region->flag = region->flagfullscreen;
}
}
@@ -1364,7 +1356,7 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *area, const
glob_area->global->flag |= GLOBAL_AREA_IS_HIDDEN;
}
/* temporarily hide the side panels/header */
- for (region = newa->regionbase.first; region; region = region->next) {
+ LISTBASE_FOREACH (ARegion *, region, &newa->regionbase) {
region->flagfullscreen = region->flag;
if (ELEM(region->regiontype,
@@ -1537,13 +1529,11 @@ void ED_screen_animation_timer(bContext *C, int redraws, int sync, int enable)
static ARegion *time_top_left_3dwindow(bScreen *screen)
{
ARegion *aret = NULL;
- ScrArea *area;
int min = 10000;
- for (area = screen->areabase.first; area; area = area->next) {
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
if (area->spacetype == SPACE_VIEW3D) {
- ARegion *region;
- for (region = area->regionbase.first; region; region = region->next) {
+ LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
if (region->regiontype == RGN_TYPE_WINDOW) {
if (region->winrct.xmin - region->winrct.ymin < min) {
aret = region;
@@ -1576,15 +1566,14 @@ void ED_update_for_newframe(Main *bmain, Depsgraph *depsgraph)
{
Scene *scene = DEG_get_input_scene(depsgraph);
- DEG_id_tag_update_ex(bmain, &scene->id, ID_RECALC_TIME);
+ DEG_time_tag_update(bmain);
#ifdef DURIAN_CAMERA_SWITCH
void *camera = BKE_scene_camera_switch_find(scene);
if (camera && scene->camera != camera) {
- bScreen *screen;
scene->camera = camera;
/* are there cameras in the views that are not in the scene? */
- for (screen = bmain->screens.first; screen; screen = screen->id.next) {
+ LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
BKE_screen_view3d_scene_sync(screen, scene);
}
DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
@@ -1602,10 +1591,9 @@ void ED_update_for_newframe(Main *bmain, Depsgraph *depsgraph)
*/
bool ED_screen_stereo3d_required(const bScreen *screen, const Scene *scene)
{
- ScrArea *area;
const bool is_multiview = (scene->r.scemode & R_MULTIVIEW) != 0;
- for (area = screen->areabase.first; area; area = area->next) {
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
switch (area->spacetype) {
case SPACE_VIEW3D: {
View3D *v3d;
@@ -1616,8 +1604,7 @@ bool ED_screen_stereo3d_required(const bScreen *screen, const Scene *scene)
v3d = area->spacedata.first;
if (v3d->camera && v3d->stereo3d_camera == STEREO_3D_ID) {
- ARegion *region;
- for (region = area->regionbase.first; region; region = region->next) {
+ LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
if (region->regiondata && region->regiontype == RGN_TYPE_WINDOW) {
RegionView3D *rv3d = region->regiondata;
if (rv3d->persp == RV3D_CAMOB) {
diff --git a/source/blender/editors/screen/screen_geometry.c b/source/blender/editors/screen/screen_geometry.c
index 0b83a657265..4917dfa5e69 100644
--- a/source/blender/editors/screen/screen_geometry.c
+++ b/source/blender/editors/screen/screen_geometry.c
@@ -162,7 +162,6 @@ void screen_geom_vertices_scale(const wmWindow *win, bScreen *screen)
const int screen_size_x = BLI_rcti_size_x(&screen_rect);
const int screen_size_y = BLI_rcti_size_y(&screen_rect);
- ScrVert *sv = NULL;
int screen_size_x_prev, screen_size_y_prev;
float min[2], max[2];
@@ -170,7 +169,7 @@ void screen_geom_vertices_scale(const wmWindow *win, bScreen *screen)
min[0] = min[1] = 20000.0f;
max[0] = max[1] = 0.0f;
- for (sv = screen->vertbase.first; sv; sv = sv->next) {
+ LISTBASE_FOREACH (ScrVert *, sv, &screen->vertbase) {
const float fv[2] = {(float)sv->vec.x, (float)sv->vec.y};
minmax_v2v2_v2(min, max, fv);
}
@@ -183,7 +182,7 @@ void screen_geom_vertices_scale(const wmWindow *win, bScreen *screen)
const float facy = ((float)screen_size_y - 1) / ((float)screen_size_y_prev - 1);
/* make sure it fits! */
- for (sv = screen->vertbase.first; sv; sv = sv->next) {
+ LISTBASE_FOREACH (ScrVert *, sv, &screen->vertbase) {
sv->vec.x = screen_rect.xmin + round_fl_to_short((sv->vec.x - min[0]) * facx);
CLAMP(sv->vec.x, screen_rect.xmin, screen_rect.xmax - 1);
@@ -208,7 +207,7 @@ void screen_geom_vertices_scale(const wmWindow *win, bScreen *screen)
screen_geom_select_connected_edge(win, se);
/* all selected vertices get the right offset */
- for (sv = screen->vertbase.first; sv; sv = sv->next) {
+ LISTBASE_FOREACH (ScrVert *, sv, &screen->vertbase) {
/* if is a collapsed area */
if (sv != area->v1 && sv != area->v4) {
if (sv->flag) {
@@ -232,7 +231,7 @@ void screen_geom_vertices_scale(const wmWindow *win, bScreen *screen)
screen_geom_select_connected_edge(win, se);
/* all selected vertices get the right offset */
- for (sv = screen->vertbase.first; sv; sv = sv->next) {
+ LISTBASE_FOREACH (ScrVert *, sv, &screen->vertbase) {
/* if is not a collapsed area */
if (sv != area->v2 && sv != area->v3) {
if (sv->flag) {
diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c
index b002b23a7f3..aff19ddacf1 100644
--- a/source/blender/editors/screen/screen_ops.c
+++ b/source/blender/editors/screen/screen_ops.c
@@ -692,10 +692,9 @@ static bool actionzone_area_poll(bContext *C)
if (screen && win && win->eventstate) {
const int *xy = &win->eventstate->x;
- AZone *az;
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
- for (az = area->actionzones.first; az; az = az->next) {
+ LISTBASE_FOREACH (AZone *, az, &area->actionzones) {
if (BLI_rcti_isect_pt_v(&az->rect, xy)) {
return 1;
}
@@ -3068,13 +3067,12 @@ static void SCREEN_OT_keyframe_jump(wmOperatorType *ot)
static int marker_jump_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
- TimeMarker *marker;
int closest = CFRA;
const bool next = RNA_boolean_get(op->ptr, "next");
bool found = false;
/* find matching marker in the right direction */
- for (marker = scene->markers.first; marker; marker = marker->next) {
+ LISTBASE_FOREACH (TimeMarker *, marker, &scene->markers) {
if (next) {
if ((marker->frame > CFRA) && (!found || closest > marker->frame)) {
closest = marker->frame;
@@ -3170,8 +3168,9 @@ static int screen_maximize_area_exec(bContext *C, wmOperator *op)
/* search current screen for 'fullscreen' areas */
/* prevents restoring info header, when mouse is over it */
- for (area = screen->areabase.first; area; area = area->next) {
- if (area->full) {
+ LISTBASE_FOREACH (ScrArea *, area_iter, &screen->areabase) {
+ if (area_iter->full) {
+ area = area_iter;
break;
}
}
@@ -3619,12 +3618,10 @@ static void SCREEN_OT_area_options(wmOperatorType *ot)
static int spacedata_cleanup_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
- bScreen *screen;
- ScrArea *area;
int tot = 0;
- for (screen = bmain->screens.first; screen; screen = screen->id.next) {
- for (area = screen->areabase.first; area; area = area->next) {
+ LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
if (area->spacedata.first != area->spacedata.last) {
SpaceLink *sl = area->spacedata.first;
@@ -3854,7 +3851,6 @@ static int region_quadview_exec(bContext *C, wmOperator *op)
region->alignment = 0;
if (area->spacetype == SPACE_VIEW3D) {
- ARegion *region_iter;
RegionView3D *rv3d = region->regiondata;
/* if this is a locked view, use settings from 'User' view */
@@ -3878,7 +3874,7 @@ static int region_quadview_exec(bContext *C, wmOperator *op)
rv3d->rflag |= RV3D_GPULIGHT_UPDATE;
/* Accumulate locks, in case they're mixed. */
- for (region_iter = area->regionbase.first; region_iter; region_iter = region_iter->next) {
+ LISTBASE_FOREACH (ARegion *, region_iter, &area->regionbase) {
if (region_iter->regiontype == RGN_TYPE_WINDOW) {
RegionView3D *rv3d_iter = region_iter->regiondata;
rv3d->viewlock_quad |= rv3d_iter->viewlock;
@@ -4436,13 +4432,11 @@ static int screen_animation_step(bContext *C, wmOperator *UNUSED(op), const wmEv
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
- Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, false);
+ Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer);
Scene *scene_eval = (depsgraph != NULL) ? DEG_get_evaluated_scene(depsgraph) : NULL;
wmTimer *wt = screen->animtimer;
ScreenAnimData *sad = wt->customdata;
wmWindowManager *wm = CTX_wm_manager(C);
- wmWindow *window;
- ScrArea *area;
int sync;
double time;
@@ -4588,12 +4582,11 @@ static int screen_animation_step(bContext *C, wmOperator *UNUSED(op), const wmEv
ED_update_for_newframe(bmain, depsgraph);
}
- for (window = wm->windows.first; window; window = window->next) {
+ LISTBASE_FOREACH (wmWindow *, window, &wm->windows) {
const bScreen *win_screen = WM_window_get_active_screen(window);
- for (area = win_screen->areabase.first; area; area = area->next) {
- ARegion *region;
- for (region = area->regionbase.first; region; region = region->next) {
+ LISTBASE_FOREACH (ScrArea *, area, &win_screen->areabase) {
+ LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
bool redraw = false;
if (region == sad->region) {
redraw = true;
@@ -4867,8 +4860,9 @@ static int fullscreen_back_exec(bContext *C, wmOperator *op)
ScrArea *area = NULL;
/* search current screen for 'fullscreen' areas */
- for (area = screen->areabase.first; area; area = area->next) {
- if (area->full) {
+ LISTBASE_FOREACH (ScrArea *, area_iter, &screen->areabase) {
+ if (area_iter->full) {
+ area = area_iter;
break;
}
}
diff --git a/source/blender/editors/screen/workspace_edit.c b/source/blender/editors/screen/workspace_edit.c
index b20dc80d158..702c824077d 100644
--- a/source/blender/editors/screen/workspace_edit.c
+++ b/source/blender/editors/screen/workspace_edit.c
@@ -409,8 +409,7 @@ static void workspace_add_menu(bContext *UNUSED(C), uiLayout *layout, void *temp
WorkspaceConfigFileData *builtin_config = workspace_system_file_read(app_template);
if (startup_config) {
- for (WorkSpace *workspace = startup_config->workspaces.first; workspace;
- workspace = workspace->id.next) {
+ LISTBASE_FOREACH (WorkSpace *, workspace, &startup_config->workspaces) {
uiLayout *row = uiLayoutRow(layout, false);
workspace_append_button(row, ot_append, workspace, startup_config->main);
has_startup_items = true;
@@ -420,8 +419,7 @@ static void workspace_add_menu(bContext *UNUSED(C), uiLayout *layout, void *temp
if (builtin_config) {
bool has_title = false;
- for (WorkSpace *workspace = builtin_config->workspaces.first; workspace;
- workspace = workspace->id.next) {
+ LISTBASE_FOREACH (WorkSpace *, workspace, &builtin_config->workspaces) {
if (startup_config &&
BLI_findstring(&startup_config->workspaces, workspace->id.name, offsetof(ID, name))) {
continue;
diff --git a/source/blender/editors/screen/workspace_layout_edit.c b/source/blender/editors/screen/workspace_layout_edit.c
index 8a36cffa1f1..f4b076aca00 100644
--- a/source/blender/editors/screen/workspace_layout_edit.c
+++ b/source/blender/editors/screen/workspace_layout_edit.c
@@ -168,8 +168,7 @@ static bool workspace_change_find_new_layout_cb(const WorkSpaceLayout *layout, v
static bScreen *screen_fullscreen_find_associated_normal_screen(const Main *bmain, bScreen *screen)
{
- for (bScreen *screen_iter = bmain->screens.first; screen_iter;
- screen_iter = screen_iter->id.next) {
+ LISTBASE_FOREACH (bScreen *, screen_iter, &bmain->screens) {
if ((screen_iter != screen) && ELEM(screen_iter->state, SCREENMAXIMIZED, SCREENFULL)) {
ScrArea *area = screen_iter->areabase.first;
if (area && area->full == screen) {
diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c
index 0e38340d3bc..ee514fa745c 100644
--- a/source/blender/editors/sculpt_paint/paint_cursor.c
+++ b/source/blender/editors/sculpt_paint/paint_cursor.c
@@ -566,7 +566,7 @@ static bool paint_draw_tex_overlay(UnifiedPaintSettings *ups,
if (load_tex(brush, vc, zoom, col, primary)) {
GPU_color_mask(true, true, true, true);
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
if (mtex->brush_map_mode == MTEX_MAP_MODE_VIEW) {
GPU_matrix_push();
@@ -693,7 +693,7 @@ static bool paint_draw_cursor_overlay(
float center[2];
GPU_color_mask(true, true, true, true);
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
if (ups->draw_anchored) {
copy_v2_v2(center, ups->anchored_initial_mouse);
@@ -776,7 +776,7 @@ static bool paint_draw_alpha_overlay(UnifiedPaintSettings *ups,
ePaintOverlayControlFlags flags = BKE_paint_get_overlay_flags();
eGPUBlend blend_state = GPU_blend_get();
- bool depth_test = GPU_depth_test_enabled();
+ eGPUDepthTest depth_test = GPU_depth_test_get();
/* Translate to region. */
GPU_matrix_push();
@@ -1147,9 +1147,9 @@ static void sculpt_geometry_preview_lines_draw(const uint gpuattr,
immUniformColor4f(1.0f, 1.0f, 1.0f, 0.6f);
/* Cursor normally draws on top, but for this part we need depth tests. */
- const bool depth_test = GPU_depth_test_enabled();
+ const eGPUDepthTest depth_test = GPU_depth_test_get();
if (!depth_test) {
- GPU_depth_test(true);
+ GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
}
GPU_line_width(1.0f);
@@ -1163,7 +1163,7 @@ static void sculpt_geometry_preview_lines_draw(const uint gpuattr,
/* Restore depth test value. */
if (!depth_test) {
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
}
}
diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c
index 05ffb80d8a1..ab8b81a8155 100644
--- a/source/blender/editors/sculpt_paint/paint_mask.c
+++ b/source/blender/editors/sculpt_paint/paint_mask.c
@@ -31,6 +31,7 @@
#include "BLI_lasso_2d.h"
#include "BLI_math_geom.h"
#include "BLI_math_matrix.h"
+#include "BLI_rect.h"
#include "BLI_task.h"
#include "BLI_utildefines.h"
@@ -221,392 +222,383 @@ void PAINT_OT_mask_flood_fill(struct wmOperatorType *ot)
1.0f);
}
-/* Box select, operator is VIEW3D_OT_select_box, defined in view3d_select.c. */
+/* Sculpt Gesture Operators. */
-static bool is_effected(float planes[4][4], const float co[3])
-{
- return isect_point_planes_v3(planes, 4, co);
-}
+typedef enum eSculptGestureShapeType {
+ SCULPT_GESTURE_SHAPE_BOX,
+ SCULPT_GESTURE_SHAPE_LASSO,
+} eMaskGesturesShapeType;
-static void flip_plane(float out[4], const float in[4], const char symm)
-{
- if (symm & PAINT_SYMM_X) {
- out[0] = -in[0];
- }
- else {
- out[0] = in[0];
- }
- if (symm & PAINT_SYMM_Y) {
- out[1] = -in[1];
- }
- else {
- out[1] = in[1];
- }
- if (symm & PAINT_SYMM_Z) {
- out[2] = -in[2];
- }
- else {
- out[2] = in[2];
- }
+typedef struct LassoGestureData {
+ float projviewobjmat[4][4];
- out[3] = in[3];
-}
+ rcti boundbox;
+ int width;
-static void mask_box_select_task_cb(void *__restrict userdata,
- const int i,
- const TaskParallelTLS *__restrict UNUSED(tls))
-{
- MaskTaskData *data = userdata;
+ /* 2D bitmap to test if a vertex is affected by the lasso shape. */
+ BLI_bitmap *mask_px;
+} LassoGestureData;
- PBVHNode *node = data->nodes[i];
+typedef struct SculptGestureContext {
+ SculptSession *ss;
+ ViewContext vc;
- const PaintMaskFloodMode mode = data->mode;
- const float value = data->value;
- float(*clip_planes_final)[4] = data->clip_planes_final;
+ /* Enabled and currently active symmetry. */
+ ePaintSymmetryFlags symm;
+ ePaintSymmetryFlags symmpass;
- PBVHVertexIter vi;
- bool any_masked = false;
- bool redraw = false;
+ /* Operation parameters. */
+ eMaskGesturesShapeType shape_type;
+ bool front_faces_only;
- float vertex_normal[3];
+ /* Mask operation parameters. */
+ PaintMaskFloodMode mask_mode;
+ float mask_value;
- BKE_pbvh_vertex_iter_begin(data->pbvh, node, vi, PBVH_ITER_UNIQUE)
- {
- SCULPT_vertex_normal_get(data->ob->sculpt, vi.index, vertex_normal);
- float dot = dot_v3v3(data->view_normal, vertex_normal);
- const bool is_effected_front_face = !(data->front_faces_only && dot < 0.0f);
+ /* View parameters. */
+ float true_view_normal[3];
+ float view_normal[3];
- if (is_effected_front_face && is_effected(clip_planes_final, vi.co)) {
- float prevmask = *vi.mask;
- if (!any_masked) {
- any_masked = true;
+ float true_clip_planes[4][4];
+ float clip_planes[4][4];
- SCULPT_undo_push_node(data->ob, node, SCULPT_UNDO_MASK);
+ /* Lasso Gesture. */
+ LassoGestureData lasso;
- if (data->multires) {
- BKE_pbvh_node_mark_normals_update(node);
- }
- }
- mask_flood_fill_set_elem(vi.mask, mode, value);
- if (prevmask != *vi.mask) {
- redraw = true;
- }
- }
- }
- BKE_pbvh_vertex_iter_end;
+ /* Task Callback Data. */
+ PBVHNode **nodes;
+ int totnode;
+} SculptGestureContext;
- if (redraw) {
- BKE_pbvh_node_mark_update_mask(node);
- }
+static void sculpt_gesture_operator_properties(wmOperatorType *ot)
+{
+ RNA_def_boolean(ot->srna,
+ "use_front_faces_only",
+ false,
+ "Front Faces Only",
+ "Affect only faces facing towards the view");
}
-static int paint_mask_gesture_box_exec(bContext *C, wmOperator *op)
+static void sculpt_gesture_context_init_common(bContext *C,
+ wmOperator *op,
+ SculptGestureContext *sgcontext)
{
- ViewContext vc;
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- ED_view3d_viewcontext_init(C, &vc, depsgraph);
-
- Sculpt *sd = vc.scene->toolsettings->sculpt;
- BoundBox bb;
- float clip_planes[4][4];
- float clip_planes_final[4][4];
- ARegion *region = vc.region;
- Object *ob = vc.obact;
- bool multires;
- PBVH *pbvh;
- PBVHNode **nodes;
- int totnode;
- int symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL;
-
- const PaintMaskFloodMode mode = RNA_enum_get(op->ptr, "mode");
- const float value = RNA_float_get(op->ptr, "value");
- const bool front_faces_only = RNA_boolean_get(op->ptr, "use_front_faces_only");
+ ED_view3d_viewcontext_init(C, &sgcontext->vc, depsgraph);
- rcti rect;
- WM_operator_properties_border_to_rcti(op, &rect);
+ Sculpt *sd = sgcontext->vc.scene->toolsettings->sculpt;
+ Object *ob = sgcontext->vc.obact;
- /* Transform the clip planes in object space. */
- ED_view3d_clipping_calc(&bb, clip_planes, vc.region, vc.obact, &rect);
+ /* Operator properties. */
+ sgcontext->front_faces_only = RNA_boolean_get(op->ptr, "use_front_faces_only");
- BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true, false);
- pbvh = ob->sculpt->pbvh;
- multires = (BKE_pbvh_type(pbvh) == PBVH_GRIDS);
+ /* SculptSession */
+ sgcontext->ss = ob->sculpt;
- SCULPT_undo_push_begin("Mask box fill");
+ /* Symmetry. */
+ sgcontext->symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL;
- /* Calculate the view normal in object space. */
+ /* View Normal. */
float mat[3][3];
float view_dir[3] = {0.0f, 0.0f, 1.0f};
- float true_view_normal[3];
- copy_m3_m4(mat, vc.rv3d->viewinv);
+ copy_m3_m4(mat, sgcontext->vc.rv3d->viewinv);
mul_m3_v3(mat, view_dir);
copy_m3_m4(mat, ob->imat);
mul_m3_v3(mat, view_dir);
- normalize_v3_v3(true_view_normal, view_dir);
+ normalize_v3_v3(sgcontext->true_view_normal, view_dir);
+}
- for (int symmpass = 0; symmpass <= symm; symmpass++) {
- if (symmpass == 0 || (symm & symmpass && (symm != 5 || symmpass != 3) &&
- (symm != 6 || (symmpass != 3 && symmpass != 5)))) {
+static void sculpt_gesture_lasso_px_cb(int x, int x_end, int y, void *user_data)
+{
+ SculptGestureContext *mcontext = user_data;
+ LassoGestureData *lasso = &mcontext->lasso;
+ int index = (y * lasso->width) + x;
+ int index_end = (y * lasso->width) + x_end;
+ do {
+ BLI_BITMAP_ENABLE(lasso->mask_px, index);
+ } while (++index != index_end);
+}
- /* Flip the planes symmetrically as needed. */
- for (int j = 0; j < 4; j++) {
- flip_plane(clip_planes_final[j], clip_planes[j], symmpass);
- }
+static SculptGestureContext *sculpt_gesture_init_from_lasso(bContext *C, wmOperator *op)
+{
+ SculptGestureContext *sgcontext = MEM_callocN(sizeof(SculptGestureContext),
+ "sculpt gesture context lasso");
+ sgcontext->shape_type = SCULPT_GESTURE_SHAPE_LASSO;
+
+ sculpt_gesture_context_init_common(C, op, sgcontext);
- PBVHFrustumPlanes frustum = {.planes = clip_planes_final, .num_planes = 4};
- BKE_pbvh_search_gather(pbvh, BKE_pbvh_node_frustum_contain_AABB, &frustum, &nodes, &totnode);
+ int mcoords_len;
+ const int(*mcoords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcoords_len);
- negate_m4(clip_planes_final);
+ if (!mcoords) {
+ return NULL;
+ }
- MaskTaskData data = {
- .ob = ob,
- .pbvh = pbvh,
- .nodes = nodes,
- .multires = multires,
- .mode = mode,
- .value = value,
- .clip_planes_final = clip_planes_final,
- .front_faces_only = front_faces_only,
- };
+ ED_view3d_ob_project_mat_get(
+ sgcontext->vc.rv3d, sgcontext->vc.obact, sgcontext->lasso.projviewobjmat);
+ BLI_lasso_boundbox(&sgcontext->lasso.boundbox, mcoords, mcoords_len);
+ sgcontext->lasso.width = sgcontext->lasso.boundbox.xmax - sgcontext->lasso.boundbox.xmin;
+ sgcontext->lasso.mask_px = BLI_BITMAP_NEW(
+ sgcontext->lasso.width * (sgcontext->lasso.boundbox.ymax - sgcontext->lasso.boundbox.ymin),
+ __func__);
+
+ BLI_bitmap_draw_2d_poly_v2i_n(sgcontext->lasso.boundbox.xmin,
+ sgcontext->lasso.boundbox.ymin,
+ sgcontext->lasso.boundbox.xmax,
+ sgcontext->lasso.boundbox.ymax,
+ mcoords,
+ mcoords_len,
+ sculpt_gesture_lasso_px_cb,
+ sgcontext);
- flip_v3_v3(data.view_normal, true_view_normal, symmpass);
+ BoundBox bb;
+ ED_view3d_clipping_calc(&bb,
+ sgcontext->true_clip_planes,
+ sgcontext->vc.region,
+ sgcontext->vc.obact,
+ &sgcontext->lasso.boundbox);
+ MEM_freeN((void *)mcoords);
+
+ return sgcontext;
+}
- TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- BLI_task_parallel_range(0, totnode, &data, mask_box_select_task_cb, &settings);
+static SculptGestureContext *sculpt_gesture_init_from_box(bContext *C, wmOperator *op)
+{
+ SculptGestureContext *sgcontext = MEM_callocN(sizeof(SculptGestureContext),
+ "sculpt gesture context box");
+ sgcontext->shape_type = SCULPT_GESTURE_SHAPE_BOX;
- if (nodes) {
- MEM_freeN(nodes);
- }
- }
- }
+ sculpt_gesture_context_init_common(C, op, sgcontext);
- if (multires) {
- multires_mark_as_modified(depsgraph, ob, MULTIRES_COORDS_MODIFIED);
- }
+ rcti rect;
+ WM_operator_properties_border_to_rcti(op, &rect);
- BKE_pbvh_update_vertex_data(pbvh, PBVH_UpdateMask);
+ BoundBox bb;
+ ED_view3d_clipping_calc(
+ &bb, sgcontext->true_clip_planes, sgcontext->vc.region, sgcontext->vc.obact, &rect);
- SCULPT_undo_push_end();
+ return sgcontext;
+}
- ED_region_tag_redraw(region);
+static void sculpt_gesture_context_free(SculptGestureContext *sgcontext)
+{
+ MEM_SAFE_FREE(sgcontext->lasso.mask_px);
+ MEM_SAFE_FREE(sgcontext->nodes);
+ MEM_SAFE_FREE(sgcontext);
+}
- WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
+static void flip_plane(float out[4], const float in[4], const char symm)
+{
+ if (symm & PAINT_SYMM_X) {
+ out[0] = -in[0];
+ }
+ else {
+ out[0] = in[0];
+ }
+ if (symm & PAINT_SYMM_Y) {
+ out[1] = -in[1];
+ }
+ else {
+ out[1] = in[1];
+ }
+ if (symm & PAINT_SYMM_Z) {
+ out[2] = -in[2];
+ }
+ else {
+ out[2] = in[2];
+ }
- return true;
+ out[3] = in[3];
}
-typedef struct LassoMaskData {
- struct ViewContext *vc;
- float projviewobjmat[4][4];
- BLI_bitmap *px;
- int width;
- /* Bounding box for scanfilling. */
- rcti rect;
- int symmpass;
+static void sculpt_gesture_flip_for_symmetry_pass(SculptGestureContext *sgcontext,
+ const ePaintSymmetryFlags symmpass)
+{
+ sgcontext->symmpass = symmpass;
+ for (int j = 0; j < 4; j++) {
+ flip_plane(sgcontext->clip_planes[j], sgcontext->true_clip_planes[j], symmpass);
+ }
+ negate_m4(sgcontext->clip_planes);
+ flip_v3_v3(sgcontext->view_normal, sgcontext->true_view_normal, symmpass);
+}
- MaskTaskData task_data;
-} LassoMaskData;
+static void sculpt_gesture_update_effected_nodes(SculptGestureContext *sgcontext)
+{
+ SculptSession *ss = sgcontext->ss;
+ float clip_planes[4][4];
+ copy_m4_m4(clip_planes, sgcontext->clip_planes);
+ negate_m4(clip_planes);
+ PBVHFrustumPlanes frustum = {.planes = clip_planes, .num_planes = 4};
+ BKE_pbvh_search_gather(ss->pbvh,
+ BKE_pbvh_node_frustum_contain_AABB,
+ &frustum,
+ &sgcontext->nodes,
+ &sgcontext->totnode);
+}
-/**
- * Lasso select. This could be defined as part of #VIEW3D_OT_select_lasso,
- * still the shortcuts conflict, so we will use a separate operator.
- */
-static bool is_effected_lasso(LassoMaskData *data, const float co[3])
+static bool sculpt_gesture_is_effected_lasso(SculptGestureContext *sgcontext, const float co[3])
{
float scr_co_f[2];
int scr_co_s[2];
float co_final[3];
- flip_v3_v3(co_final, co, data->symmpass);
+ flip_v3_v3(co_final, co, sgcontext->symmpass);
+
/* First project point to 2d space. */
- ED_view3d_project_float_v2_m4(data->vc->region, co_final, scr_co_f, data->projviewobjmat);
+ ED_view3d_project_float_v2_m4(
+ sgcontext->vc.region, co_final, scr_co_f, sgcontext->lasso.projviewobjmat);
scr_co_s[0] = scr_co_f[0];
scr_co_s[1] = scr_co_f[1];
- /* Clip against screen, because lasso is limited to screen only. */
- if ((scr_co_s[0] < data->rect.xmin) || (scr_co_s[1] < data->rect.ymin) ||
- (scr_co_s[0] >= data->rect.xmax) || (scr_co_s[1] >= data->rect.ymax)) {
+ /* Clip against lasso boundbox. */
+ LassoGestureData *lasso = &sgcontext->lasso;
+ if (!BLI_rcti_isect_pt(&lasso->boundbox, scr_co_s[0], scr_co_s[1])) {
return false;
}
- scr_co_s[0] -= data->rect.xmin;
- scr_co_s[1] -= data->rect.ymin;
+ scr_co_s[0] -= lasso->boundbox.xmin;
+ scr_co_s[1] -= lasso->boundbox.ymin;
- return BLI_BITMAP_TEST_BOOL(data->px, scr_co_s[1] * data->width + scr_co_s[0]);
+ return BLI_BITMAP_TEST_BOOL(lasso->mask_px, scr_co_s[1] * lasso->width + scr_co_s[0]);
}
-static void mask_lasso_px_cb(int x, int x_end, int y, void *user_data)
+static bool sculpt_gesture_is_vertex_effected(SculptGestureContext *sgcontext, PBVHVertexIter *vd)
{
- LassoMaskData *data = user_data;
- int index = (y * data->width) + x;
- int index_end = (y * data->width) + x_end;
- do {
- BLI_BITMAP_ENABLE(data->px, index);
- } while (++index != index_end);
+ float vertex_normal[3];
+ SCULPT_vertex_normal_get(sgcontext->ss, vd->index, vertex_normal);
+ float dot = dot_v3v3(sgcontext->view_normal, vertex_normal);
+ const bool is_effected_front_face = !(sgcontext->front_faces_only && dot < 0.0f);
+
+ if (!is_effected_front_face) {
+ return false;
+ }
+
+ switch (sgcontext->shape_type) {
+ case SCULPT_GESTURE_SHAPE_BOX:
+ return isect_point_planes_v3(sgcontext->clip_planes, 4, vd->co);
+ case SCULPT_GESTURE_SHAPE_LASSO:
+ return sculpt_gesture_is_effected_lasso(sgcontext, vd->co);
+ }
+ return false;
}
-static void mask_gesture_lasso_task_cb(void *__restrict userdata,
+static void mask_gesture_apply_task_cb(void *__restrict userdata,
const int i,
const TaskParallelTLS *__restrict UNUSED(tls))
{
- LassoMaskData *lasso_data = userdata;
- MaskTaskData *data = &lasso_data->task_data;
-
- PBVHNode *node = data->nodes[i];
+ SculptGestureContext *sgcontext = userdata;
+ Object *ob = sgcontext->vc.obact;
+ PBVHNode *node = sgcontext->nodes[i];
- const PaintMaskFloodMode mode = data->mode;
- const float value = data->value;
+ const bool is_multires = BKE_pbvh_type(sgcontext->ss->pbvh) == PBVH_GRIDS;
- PBVHVertexIter vi;
+ PBVHVertexIter vd;
bool any_masked = false;
+ bool redraw = false;
- float vertex_normal[3];
-
- BKE_pbvh_vertex_iter_begin(data->pbvh, node, vi, PBVH_ITER_UNIQUE)
+ BKE_pbvh_vertex_iter_begin(sgcontext->ss->pbvh, node, vd, PBVH_ITER_UNIQUE)
{
- SCULPT_vertex_normal_get(data->ob->sculpt, vi.index, vertex_normal);
- float dot = dot_v3v3(lasso_data->task_data.view_normal, vertex_normal);
- const bool is_effected_front_face = !(data->front_faces_only && dot < 0.0f);
-
- if (is_effected_front_face && is_effected_lasso(lasso_data, vi.co)) {
+ if (sculpt_gesture_is_vertex_effected(sgcontext, &vd)) {
+ float prevmask = *vd.mask;
if (!any_masked) {
any_masked = true;
- SCULPT_undo_push_node(data->ob, node, SCULPT_UNDO_MASK);
+ SCULPT_undo_push_node(ob, node, SCULPT_UNDO_MASK);
- BKE_pbvh_node_mark_redraw(node);
- if (data->multires) {
+ if (is_multires) {
BKE_pbvh_node_mark_normals_update(node);
}
}
-
- mask_flood_fill_set_elem(vi.mask, mode, value);
+ mask_flood_fill_set_elem(vd.mask, sgcontext->mask_mode, sgcontext->mask_value);
+ if (prevmask != *vd.mask) {
+ redraw = true;
+ }
}
}
BKE_pbvh_vertex_iter_end;
+
+ if (redraw) {
+ BKE_pbvh_node_mark_update_mask(node);
+ }
}
-static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op)
+static void sculpt_gesture_apply(bContext *C, SculptGestureContext *mcontext)
{
- int mcoords_len;
- const int(*mcoords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcoords_len);
+ Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
+ BKE_sculpt_update_object_for_edit(depsgraph, mcontext->vc.obact, false, true, false);
- if (mcoords) {
- Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- float clip_planes[4][4], clip_planes_final[4][4];
- BoundBox bb;
- Object *ob;
- ViewContext vc;
- LassoMaskData data;
- Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
- int symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL;
- PBVH *pbvh;
- PBVHNode **nodes;
- int totnode;
- bool multires;
- PaintMaskFloodMode mode = RNA_enum_get(op->ptr, "mode");
- float value = RNA_float_get(op->ptr, "value");
- const bool front_faces_only = RNA_boolean_get(op->ptr, "use_front_faces_only");
-
- /* Calculations of individual vertices are done in 2D screen space to diminish the amount of
- * calculations done. Bounding box PBVH collision is not computed against enclosing rectangle
- * of lasso. */
- ED_view3d_viewcontext_init(C, &vc, depsgraph);
-
- /* Lasso data calculations. */
- data.vc = &vc;
- ob = vc.obact;
- ED_view3d_ob_project_mat_get(vc.rv3d, ob, data.projviewobjmat);
-
- BLI_lasso_boundbox(&data.rect, mcoords, mcoords_len);
- data.width = data.rect.xmax - data.rect.xmin;
- data.px = BLI_BITMAP_NEW(data.width * (data.rect.ymax - data.rect.ymin), __func__);
-
- BLI_bitmap_draw_2d_poly_v2i_n(data.rect.xmin,
- data.rect.ymin,
- data.rect.xmax,
- data.rect.ymax,
- mcoords,
- mcoords_len,
- mask_lasso_px_cb,
- &data);
-
- ED_view3d_clipping_calc(&bb, clip_planes, vc.region, vc.obact, &data.rect);
-
- BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true, false);
- pbvh = ob->sculpt->pbvh;
- multires = (BKE_pbvh_type(pbvh) == PBVH_GRIDS);
-
- SCULPT_undo_push_begin("Mask lasso fill");
-
- /* Calculate the view normal in object space. */
- float mat[3][3];
- float view_dir[3] = {0.0f, 0.0f, 1.0f};
- float true_view_normal[3];
- copy_m3_m4(mat, vc.rv3d->viewinv);
- mul_m3_v3(mat, view_dir);
- copy_m3_m4(mat, ob->imat);
- mul_m3_v3(mat, view_dir);
- normalize_v3_v3(true_view_normal, view_dir);
-
- for (int symmpass = 0; symmpass <= symm; symmpass++) {
- if ((symmpass == 0) || (symm & symmpass && (symm != 5 || symmpass != 3) &&
- (symm != 6 || (symmpass != 3 && symmpass != 5)))) {
-
- /* Flip the planes symmetrically as needed. */
- for (int j = 0; j < 4; j++) {
- flip_plane(clip_planes_final[j], clip_planes[j], symmpass);
- }
+ SCULPT_undo_push_begin("Sculpt Gesture Apply");
- flip_v3_v3(data.task_data.view_normal, true_view_normal, symmpass);
+ for (ePaintSymmetryFlags symmpass = 0; symmpass <= mcontext->symm; symmpass++) {
+ if (SCULPT_is_symmetry_iteration_valid(symmpass, mcontext->symm)) {
+ sculpt_gesture_flip_for_symmetry_pass(mcontext, symmpass);
+ sculpt_gesture_update_effected_nodes(mcontext);
- data.symmpass = symmpass;
-
- /* Gather nodes inside lasso's enclosing rectangle
- * (should greatly help with bigger meshes). */
- PBVHFrustumPlanes frustum = {.planes = clip_planes_final, .num_planes = 4};
- BKE_pbvh_search_gather(
- pbvh, BKE_pbvh_node_frustum_contain_AABB, &frustum, &nodes, &totnode);
-
- negate_m4(clip_planes_final);
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, mcontext->totnode);
+ BLI_task_parallel_range(
+ 0, mcontext->totnode, mcontext, mask_gesture_apply_task_cb, &settings);
- data.task_data.ob = ob;
- data.task_data.pbvh = pbvh;
- data.task_data.nodes = nodes;
- data.task_data.multires = multires;
- data.task_data.mode = mode;
- data.task_data.value = value;
- data.task_data.front_faces_only = front_faces_only;
+ MEM_SAFE_FREE(mcontext->nodes);
+ }
+ }
- TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- BLI_task_parallel_range(0, totnode, &data, mask_gesture_lasso_task_cb, &settings);
+ if (BKE_pbvh_type(mcontext->ss->pbvh) == PBVH_GRIDS) {
+ multires_mark_as_modified(depsgraph, mcontext->vc.obact, MULTIRES_COORDS_MODIFIED);
+ }
- if (nodes) {
- MEM_freeN(nodes);
- }
- }
- }
+ BKE_pbvh_update_vertex_data(mcontext->ss->pbvh, PBVH_UpdateMask);
- if (multires) {
- multires_mark_as_modified(depsgraph, ob, MULTIRES_COORDS_MODIFIED);
- }
+ SCULPT_undo_push_end();
- BKE_pbvh_update_vertex_data(pbvh, PBVH_UpdateMask);
+ ED_region_tag_redraw(mcontext->vc.region);
+ WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, mcontext->vc.obact);
+}
- SCULPT_undo_push_end();
+static void sculpt_gesture_init_mask_properties(SculptGestureContext *sgcontext, wmOperator *op)
+{
+ sgcontext->mask_mode = RNA_enum_get(op->ptr, "mode");
+ sgcontext->mask_value = RNA_float_get(op->ptr, "value");
+}
- ED_region_tag_redraw(vc.region);
- MEM_freeN((void *)mcoords);
- MEM_freeN(data.px);
+static void paint_mask_gesture_operator_properties(wmOperatorType *ot)
+{
+ RNA_def_enum(ot->srna, "mode", mode_items, PAINT_MASK_FLOOD_VALUE, "Mode", NULL);
+ RNA_def_float(
+ ot->srna,
+ "value",
+ 1.0f,
+ 0.0f,
+ 1.0f,
+ "Value",
+ "Mask level to use when mode is 'Value'; zero means no masking and one is fully masked",
+ 0.0f,
+ 1.0f);
+}
- WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
+static int paint_mask_gesture_box_exec(bContext *C, wmOperator *op)
+{
+ SculptGestureContext *sgcontext = sculpt_gesture_init_from_box(C, op);
+ if (!sgcontext) {
+ return OPERATOR_CANCELLED;
+ }
+ sculpt_gesture_init_mask_properties(sgcontext, op);
+ sculpt_gesture_apply(C, sgcontext);
+ sculpt_gesture_context_free(sgcontext);
+ return OPERATOR_FINISHED;
+}
- return OPERATOR_FINISHED;
+static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op)
+{
+ SculptGestureContext *sgcontext = sculpt_gesture_init_from_lasso(C, op);
+ if (!sgcontext) {
+ return OPERATOR_CANCELLED;
}
- return OPERATOR_PASS_THROUGH;
+ sculpt_gesture_init_mask_properties(sgcontext, op);
+ sculpt_gesture_apply(C, sgcontext);
+ sculpt_gesture_context_free(sgcontext);
+ return OPERATOR_FINISHED;
}
void PAINT_OT_mask_lasso_gesture(wmOperatorType *ot)
@@ -625,23 +617,9 @@ void PAINT_OT_mask_lasso_gesture(wmOperatorType *ot)
/* Properties. */
WM_operator_properties_gesture_lasso(ot);
+ sculpt_gesture_operator_properties(ot);
- RNA_def_enum(ot->srna, "mode", mode_items, PAINT_MASK_FLOOD_VALUE, "Mode", NULL);
- RNA_def_float(
- ot->srna,
- "value",
- 1.0f,
- 0.0f,
- 1.0f,
- "Value",
- "Mask level to use when mode is 'Value'; zero means no masking and one is fully masked",
- 0.0f,
- 1.0f);
- RNA_def_boolean(ot->srna,
- "use_front_faces_only",
- false,
- "Front Faces Only",
- "Affect only faces facing towards the view");
+ paint_mask_gesture_operator_properties(ot);
}
void PAINT_OT_mask_box_gesture(wmOperatorType *ot)
@@ -660,21 +638,7 @@ void PAINT_OT_mask_box_gesture(wmOperatorType *ot)
/* Properties. */
WM_operator_properties_border(ot);
+ sculpt_gesture_operator_properties(ot);
- RNA_def_enum(ot->srna, "mode", mode_items, PAINT_MASK_FLOOD_VALUE, "Mode", NULL);
- RNA_def_float(
- ot->srna,
- "value",
- 1.0f,
- 0.0f,
- 1.0f,
- "Value",
- "Mask level to use when mode is 'Value'; zero means no masking and one is fully masked",
- 0.0f,
- 1.0f);
- RNA_def_boolean(ot->srna,
- "use_front_faces_only",
- false,
- "Front Faces Only",
- "Affect only faces facing towards the view");
+ paint_mask_gesture_operator_properties(ot);
}
diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c
index 52cdebf3fd5..e709224f370 100644
--- a/source/blender/editors/sculpt_paint/paint_stroke.c
+++ b/source/blender/editors/sculpt_paint/paint_stroke.c
@@ -691,6 +691,14 @@ static float paint_space_stroke_spacing(bContext *C,
spacing = spacing * (1.5f - spacing_pressure);
}
+ if (SCULPT_is_cloth_deform_brush(brush)) {
+ /* The spacing in tools that use the cloth solver should not be affected by the brush radius to
+ * avoid affecting the simulation update rate when changing the radius of the brush.
+ With a value of 100 and the brush default of 10 for spacing, a simulation step runs every 2
+ pixels movement of the cursor. */
+ size_clamp = 100.0f;
+ }
+
/* stroke system is used for 2d paint too, so we need to account for
* the fact that brush can be scaled there. */
spacing *= stroke->zoom_2d;
@@ -1001,7 +1009,7 @@ bool paint_space_stroke_enabled(Brush *br, ePaintMode mode)
return false;
}
- if (br->sculpt_tool == SCULPT_TOOL_CLOTH) {
+ if (br->sculpt_tool == SCULPT_TOOL_CLOTH || SCULPT_is_cloth_deform_brush(br)) {
/* The Cloth Brush is a special case for stroke spacing. Even if it has grab modes which do
* not support dynamic size, stroke spacing needs to be enabled so it is possible to control
* whether the simulation runs constantly or only when the brush moves when using the cloth
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index e1c1b8ee5fb..7a066f35f23 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -2273,7 +2273,7 @@ static float brush_strength(const Sculpt *sd,
case SCULPT_TOOL_DISPLACEMENT_ERASER:
return alpha * pressure * overlap * feather;
case SCULPT_TOOL_CLOTH:
- if (brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_GRAB) {
+ if (ELEM(brush->cloth_deform_type, BRUSH_CLOTH_DEFORM_GRAB, BRUSH_CLOTH_DEFORM_SNAKE_HOOK)) {
/* Grab deform uses the same falloff as a regular grab brush. */
return root_alpha * feather;
}
@@ -6634,13 +6634,19 @@ static float sculpt_brush_dynamic_size_get(Brush *brush, StrokeCache *cache, flo
* generally used to create grab deformations. */
static bool sculpt_needs_delta_from_anchored_origin(Brush *brush)
{
- return ELEM(brush->sculpt_tool,
- SCULPT_TOOL_GRAB,
- SCULPT_TOOL_POSE,
- SCULPT_TOOL_BOUNDARY,
- SCULPT_TOOL_THUMB,
- SCULPT_TOOL_ELASTIC_DEFORM) ||
- SCULPT_is_cloth_deform_brush(brush);
+ if (ELEM(brush->sculpt_tool,
+ SCULPT_TOOL_GRAB,
+ SCULPT_TOOL_POSE,
+ SCULPT_TOOL_BOUNDARY,
+ SCULPT_TOOL_THUMB,
+ SCULPT_TOOL_ELASTIC_DEFORM)) {
+ return true;
+ }
+ if (brush->sculpt_tool == SCULPT_TOOL_CLOTH &&
+ brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_GRAB) {
+ return true;
+ }
+ return false;
}
/* In these brushes the grab delta is calculated from the previous stroke location, which is used
@@ -6648,7 +6654,7 @@ static bool sculpt_needs_delta_from_anchored_origin(Brush *brush)
static bool sculpt_needs_delta_for_tip_orientation(Brush *brush)
{
if (brush->sculpt_tool == SCULPT_TOOL_CLOTH) {
- return !SCULPT_is_cloth_deform_brush(brush);
+ return brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_SNAKE_HOOK;
}
return ELEM(brush->sculpt_tool,
SCULPT_TOOL_CLAY_STRIPS,
@@ -6694,7 +6700,9 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru
copy_v3_v3(cache->orig_grab_location, cache->true_location);
}
}
- else if (tool == SCULPT_TOOL_SNAKE_HOOK) {
+ else if (tool == SCULPT_TOOL_SNAKE_HOOK ||
+ (tool == SCULPT_TOOL_CLOTH &&
+ brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_SNAKE_HOOK)) {
add_v3_v3(cache->true_location, cache->grab_delta);
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c
index 9a3fbe474b8..c3666c8aaad 100644
--- a/source/blender/editors/sculpt_paint/sculpt_cloth.c
+++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c
@@ -169,6 +169,8 @@ static void cloth_brush_add_length_constraint(SculptSession *ss,
length_constraint->elem_position_a = cloth_sim->pos[v1];
length_constraint->elem_position_b = cloth_sim->pos[v2];
+ length_constraint->type = SCULPT_CLOTH_CONSTRAINT_STRUCTURAL;
+
if (use_persistent) {
length_constraint->length = len_v3v3(SCULPT_vertex_persistent_co_get(ss, v1),
SCULPT_vertex_persistent_co_get(ss, v2));
@@ -201,6 +203,8 @@ static void cloth_brush_add_softbody_constraint(SculptClothSimulation *cloth_sim
length_constraint->elem_position_a = cloth_sim->pos[v];
length_constraint->elem_position_b = cloth_sim->init_pos[v];
+ length_constraint->type = SCULPT_CLOTH_CONSTRAINT_SOFTBODY;
+
length_constraint->length = 0.0f;
length_constraint->strength = strength;
@@ -220,6 +224,8 @@ static void cloth_brush_add_deformation_constraint(SculptClothSimulation *cloth_
length_constraint->elem_index_a = v;
length_constraint->elem_index_b = v;
+ length_constraint->type = SCULPT_CLOTH_CONSTRAINT_DEFORMATION;
+
length_constraint->elem_position_a = cloth_sim->pos[v];
length_constraint->elem_position_b = cloth_sim->deformation_pos[v];
@@ -301,12 +307,18 @@ static void do_cloth_brush_build_constraints_task_cb_ex(
if (brush && brush->sculpt_tool == SCULPT_TOOL_CLOTH) {
/* The cloth brush works by applying forces in most of its modes, but some of them require
* deformation coordinates to make the simulation stable. */
- if (cloth_is_deform_brush && len_squared < radius_squared) {
- /* When a deform brush is used as part of the cloth brush, deformation constraints are
- * created with different strengths and only inside the radius of the brush. */
+ if (brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_GRAB && len_squared < radius_squared) {
+ /* When the grab brush brush is used as part of the cloth brush, deformation constraints
+ * are created with different strengths and only inside the radius of the brush. */
const float fade = BKE_brush_curve_strength(brush, sqrtf(len_squared), ss->cache->radius);
cloth_brush_add_deformation_constraint(data->cloth_sim, vd.index, fade);
}
+ else if (brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_SNAKE_HOOK) {
+ /* Cloth Snake Hook creates deformation constraint with fixed strength because the strength
+ * is controlled per iteration using cloth_sim->deformation_strength. */
+ cloth_brush_add_deformation_constraint(
+ data->cloth_sim, vd.index, CLOTH_DEFORMATION_TARGET_STRENGTH);
+ }
}
else if (data->cloth_sim->deformation_pos) {
/* Any other tool that target the cloth simulation handle the falloff in
@@ -397,7 +409,7 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata,
brush, ss->cache->radius, ss->cache->initial_location, cloth_sim->init_pos[vd.index]);
float current_vertex_location[3];
- if (SCULPT_is_cloth_deform_brush(brush)) {
+ if (brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_GRAB) {
SCULPT_orig_vert_data_update(&orig_data, &vd);
copy_v3_v3(current_vertex_location, orig_data.co);
}
@@ -456,6 +468,12 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata,
fade);
zero_v3(force);
break;
+ case BRUSH_CLOTH_DEFORM_SNAKE_HOOK:
+ copy_v3_v3(cloth_sim->deformation_pos[vd.index], cloth_sim->pos[vd.index]);
+ madd_v3_v3fl(cloth_sim->deformation_pos[vd.index], ss->cache->grab_delta_symmetry, fade);
+ cloth_sim->deformation_strength[vd.index] = fade;
+ zero_v3(force);
+ break;
case BRUSH_CLOTH_DEFORM_PINCH_POINT:
if (use_falloff_plane) {
float distance = dist_signed_to_plane_v3(vd.co, deform_plane);
@@ -718,13 +736,21 @@ static void cloth_brush_satisfy_constraints(SculptSession *ss,
cloth_sim->init_pos[v2]) :
1.0f;
+ float deformation_strength = 1.0f;
+ if (constraint->type == SCULPT_CLOTH_CONSTRAINT_DEFORMATION) {
+ deformation_strength = (cloth_sim->deformation_strength[v1] +
+ cloth_sim->deformation_strength[v2]) *
+ 0.5f;
+ }
+
madd_v3_v3fl(cloth_sim->pos[v1],
correction_vector_half,
- 1.0f * mask_v1 * sim_factor_v1 * constraint->strength);
+ 1.0f * mask_v1 * sim_factor_v1 * constraint->strength * deformation_strength);
if (v1 != v2) {
madd_v3_v3fl(cloth_sim->pos[v2],
correction_vector_half,
- -1.0f * mask_v2 * sim_factor_v2 * constraint->strength);
+ -1.0f * mask_v2 * sim_factor_v2 * constraint->strength *
+ deformation_strength);
}
}
}
@@ -824,6 +850,15 @@ static void cloth_brush_apply_brush_foces(Sculpt *sd, Object *ob, PBVHNode **nod
}
}
+ if (brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_SNAKE_HOOK) {
+ /* Set the deformation strength to 0. Snake hook will initialize the strength in the required
+ * area. */
+ const int totverts = SCULPT_vertex_count_get(ss);
+ for (int i = 0; i < totverts; i++) {
+ ss->cache->cloth_sim->deformation_strength[i] = 0.0f;
+ }
+ }
+
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(
@@ -862,6 +897,8 @@ SculptClothSimulation *SCULPT_cloth_brush_simulation_create(SculptSession *ss,
if (brush && SCULPT_is_cloth_deform_brush(brush)) {
cloth_sim->deformation_pos = MEM_calloc_arrayN(
totverts, sizeof(float[3]), "cloth sim deformation positions");
+ cloth_sim->deformation_strength = MEM_calloc_arrayN(
+ totverts, sizeof(float), "cloth sim deformation strength");
}
cloth_sim->mass = cloth_mass;
@@ -921,6 +958,7 @@ void SCULPT_cloth_brush_simulation_init(SculptSession *ss, SculptClothSimulation
copy_v3_v3(cloth_sim->prev_pos[i], SCULPT_vertex_co_get(ss, i));
if (has_deformation_pos) {
copy_v3_v3(cloth_sim->deformation_pos[i], SCULPT_vertex_co_get(ss, i));
+ cloth_sim->deformation_strength[i] = 1.0f;
}
}
}
@@ -986,6 +1024,7 @@ void SCULPT_cloth_simulation_free(struct SculptClothSimulation *cloth_sim)
MEM_SAFE_FREE(cloth_sim->length_constraint_tweak);
MEM_SAFE_FREE(cloth_sim->deformation_pos);
MEM_SAFE_FREE(cloth_sim->init_pos);
+ MEM_SAFE_FREE(cloth_sim->deformation_strength);
if (cloth_sim->collider_list) {
BKE_collider_cache_free(&cloth_sim->collider_list);
}
@@ -1071,6 +1110,25 @@ static EnumPropertyItem prop_cloth_filter_type[] = {
{0, NULL, 0, NULL, NULL},
};
+static EnumPropertyItem prop_cloth_filter_orientation_items[] = {
+ {SCULPT_FILTER_ORIENTATION_LOCAL,
+ "LOCAL",
+ 0,
+ "Local",
+ "Use the local axis to limit the force and set the gravity direction"},
+ {SCULPT_FILTER_ORIENTATION_WORLD,
+ "WORLD",
+ 0,
+ "World",
+ "Use the global axis to limit the force and set the gravity direction"},
+ {SCULPT_FILTER_ORIENTATION_VIEW,
+ "VIEW",
+ 0,
+ "View",
+ "Use the view axis to limit the force and set the gravity direction"},
+ {0, NULL, 0, NULL, NULL},
+};
+
typedef enum eClothFilterForceAxis {
CLOTH_FILTER_FORCE_X = 1 << 0,
CLOTH_FILTER_FORCE_Y = 1 << 1,
@@ -1120,7 +1178,15 @@ static void cloth_filter_apply_forces_task_cb(void *__restrict userdata,
switch (filter_type) {
case CLOTH_FILTER_GRAVITY:
- force[2] = -data->filter_strength * fade;
+ if (ss->filter_cache->orientation == SCULPT_FILTER_ORIENTATION_VIEW) {
+ /* When using the view orientation apply gravity in the -Y axis, this way objects will
+ * fall down instead of backwards. */
+ force[1] = -data->filter_strength * fade;
+ }
+ else {
+ force[2] = -data->filter_strength * fade;
+ }
+ SCULPT_filter_to_object_space(force, ss->filter_cache);
break;
case CLOTH_FILTER_INFLATE: {
float normal[3];
@@ -1138,11 +1204,13 @@ static void cloth_filter_apply_forces_task_cb(void *__restrict userdata,
break;
}
+ SCULPT_filter_to_orientation_space(force, ss->filter_cache);
for (int axis = 0; axis < 3; axis++) {
if (!ss->filter_cache->enabled_force_axis[axis]) {
force[axis] = 0.0f;
}
}
+ SCULPT_filter_to_object_space(force, ss->filter_cache);
add_v3_v3(force, sculpt_gravity);
@@ -1264,6 +1332,9 @@ static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent
ss->filter_cache->enabled_force_axis[1] = force_axis & CLOTH_FILTER_FORCE_Y;
ss->filter_cache->enabled_force_axis[2] = force_axis & CLOTH_FILTER_FORCE_Z;
+ SculptFilterOrientation orientation = RNA_enum_get(op->ptr, "orientation");
+ ss->filter_cache->orientation = orientation;
+
WM_event_add_modal_handler(C, op);
return OPERATOR_RUNNING_MODAL;
}
@@ -1297,6 +1368,12 @@ void SCULPT_OT_cloth_filter(struct wmOperatorType *ot)
CLOTH_FILTER_FORCE_X | CLOTH_FILTER_FORCE_Y | CLOTH_FILTER_FORCE_Z,
"Force axis",
"Apply the force in the selected axis");
+ RNA_def_enum(ot->srna,
+ "orientation",
+ prop_cloth_filter_orientation_items,
+ SCULPT_FILTER_ORIENTATION_LOCAL,
+ "Orientation",
+ "Orientation of the axis to limit the filter force");
RNA_def_float(ot->srna,
"cloth_mass",
1.0f,
diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
index 56e70a8d47a..619a1b975b6 100644
--- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
+++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
@@ -181,7 +181,7 @@ void SCULPT_filter_cache_free(SculptSession *ss)
MEM_SAFE_FREE(ss->filter_cache);
}
-typedef enum eSculptMeshFilterTypes {
+typedef enum eSculptMeshFilterType {
MESH_FILTER_SMOOTH = 0,
MESH_FILTER_SCALE = 1,
MESH_FILTER_INFLATE = 2,
@@ -193,7 +193,7 @@ typedef enum eSculptMeshFilterTypes {
MESH_FILTER_SHARPEN = 8,
MESH_FILTER_ENHANCE_DETAILS = 9,
MESH_FILTER_ERASE_DISPLACEMENT = 10,
-} eSculptMeshFilterTypes;
+} eSculptMeshFilterType;
static EnumPropertyItem prop_mesh_filter_types[] = {
{MESH_FILTER_SMOOTH, "SMOOTH", 0, "Smooth", "Smooth mesh"},
@@ -258,7 +258,7 @@ static EnumPropertyItem prop_mesh_filter_orientation_items[] = {
{0, NULL, 0, NULL, NULL},
};
-static bool sculpt_mesh_filter_needs_pmap(int filter_type, bool use_face_sets)
+static bool sculpt_mesh_filter_needs_pmap(eSculptMeshFilterType filter_type, bool use_face_sets)
{
return use_face_sets || ELEM(filter_type,
MESH_FILTER_SMOOTH,
@@ -277,7 +277,7 @@ static void mesh_filter_task_cb(void *__restrict userdata,
SculptSession *ss = data->ob->sculpt;
PBVHNode *node = data->nodes[i];
- const int filter_type = data->filter_type;
+ const eSculptMeshFilterType filter_type = data->filter_type;
SculptOrigVertData orig_data;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[i]);
@@ -486,49 +486,80 @@ static void mesh_filter_task_cb(void *__restrict userdata,
static void mesh_filter_enhance_details_init_directions(SculptSession *ss)
{
const int totvert = SCULPT_vertex_count_get(ss);
+ FilterCache *filter_cache = ss->filter_cache;
+
+ filter_cache->detail_directions = MEM_malloc_arrayN(
+ totvert, sizeof(float[3]), "detail directions");
for (int i = 0; i < totvert; i++) {
float avg[3];
SCULPT_neighbor_coords_average(ss, avg, i);
- sub_v3_v3v3(ss->filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, i));
+ sub_v3_v3v3(filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, i));
}
}
+static void mesh_filter_surface_smooth_init(SculptSession *ss,
+ const float shape_preservation,
+ const float current_vertex_displacement)
+{
+ const int totvert = SCULPT_vertex_count_get(ss);
+ FilterCache *filter_cache = ss->filter_cache;
+
+ filter_cache->surface_smooth_laplacian_disp = MEM_malloc_arrayN(
+ totvert, sizeof(float[3]), "surface smooth displacement");
+ filter_cache->surface_smooth_shape_preservation = shape_preservation;
+ filter_cache->surface_smooth_current_vertex = current_vertex_displacement;
+}
+
static void mesh_filter_init_limit_surface_co(SculptSession *ss)
{
const int totvert = SCULPT_vertex_count_get(ss);
- ss->filter_cache->limit_surface_co = MEM_malloc_arrayN(
- 3 * sizeof(float), totvert, "limit surface co");
+ FilterCache *filter_cache = ss->filter_cache;
+
+ filter_cache->limit_surface_co = MEM_malloc_arrayN(
+ sizeof(float[3]), totvert, "limit surface co");
for (int i = 0; i < totvert; i++) {
- SCULPT_vertex_limit_surface_get(ss, i, ss->filter_cache->limit_surface_co[i]);
+ SCULPT_vertex_limit_surface_get(ss, i, filter_cache->limit_surface_co[i]);
}
}
-static void mesh_filter_sharpen_init_factors(SculptSession *ss)
+static void mesh_filter_sharpen_init(SculptSession *ss,
+ const float smooth_ratio,
+ const float intensify_detail_strength,
+ const int curvature_smooth_iterations)
{
const int totvert = SCULPT_vertex_count_get(ss);
+ FilterCache *filter_cache = ss->filter_cache;
+
+ filter_cache->sharpen_smooth_ratio = smooth_ratio;
+ filter_cache->sharpen_intensify_detail_strength = intensify_detail_strength;
+ filter_cache->sharpen_curvature_smooth_iterations = curvature_smooth_iterations;
+ filter_cache->sharpen_factor = MEM_malloc_arrayN(sizeof(float), totvert, "sharpen factor");
+ filter_cache->detail_directions = MEM_malloc_arrayN(
+ totvert, sizeof(float[3]), "sharpen detail direction");
+
for (int i = 0; i < totvert; i++) {
float avg[3];
SCULPT_neighbor_coords_average(ss, avg, i);
- sub_v3_v3v3(ss->filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, i));
- ss->filter_cache->sharpen_factor[i] = len_v3(ss->filter_cache->detail_directions[i]);
+ sub_v3_v3v3(filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, i));
+ filter_cache->sharpen_factor[i] = len_v3(filter_cache->detail_directions[i]);
}
float max_factor = 0.0f;
for (int i = 0; i < totvert; i++) {
- if (ss->filter_cache->sharpen_factor[i] > max_factor) {
- max_factor = ss->filter_cache->sharpen_factor[i];
+ if (filter_cache->sharpen_factor[i] > max_factor) {
+ max_factor = filter_cache->sharpen_factor[i];
}
}
max_factor = 1.0f / max_factor;
for (int i = 0; i < totvert; i++) {
- ss->filter_cache->sharpen_factor[i] *= max_factor;
- ss->filter_cache->sharpen_factor[i] = 1.0f - pow2f(1.0f - ss->filter_cache->sharpen_factor[i]);
+ filter_cache->sharpen_factor[i] *= max_factor;
+ filter_cache->sharpen_factor[i] = 1.0f - pow2f(1.0f - filter_cache->sharpen_factor[i]);
}
/* Smooth the calculated factors and directions to remove high frecuency detail. */
for (int smooth_iterations = 0;
- smooth_iterations < ss->filter_cache->sharpen_curvature_smooth_iterations;
+ smooth_iterations < filter_cache->sharpen_curvature_smooth_iterations;
smooth_iterations++) {
for (int i = 0; i < totvert; i++) {
float direction_avg[3] = {0.0f, 0.0f, 0.0f};
@@ -537,15 +568,15 @@ static void mesh_filter_sharpen_init_factors(SculptSession *ss)
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) {
- add_v3_v3(direction_avg, ss->filter_cache->detail_directions[ni.index]);
- sharpen_avg += ss->filter_cache->sharpen_factor[ni.index];
+ add_v3_v3(direction_avg, filter_cache->detail_directions[ni.index]);
+ sharpen_avg += filter_cache->sharpen_factor[ni.index];
total++;
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
if (total > 0) {
- mul_v3_v3fl(ss->filter_cache->detail_directions[i], direction_avg, 1.0f / total);
- ss->filter_cache->sharpen_factor[i] = sharpen_avg / total;
+ mul_v3_v3fl(filter_cache->detail_directions[i], direction_avg, 1.0f / total);
+ filter_cache->sharpen_factor[i] = sharpen_avg / total;
}
}
}
@@ -590,7 +621,7 @@ static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent *
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
SculptSession *ss = ob->sculpt;
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
- int filter_type = RNA_enum_get(op->ptr, "type");
+ eSculptMeshFilterType filter_type = RNA_enum_get(op->ptr, "type");
float filter_strength = RNA_float_get(op->ptr, "strength");
const bool use_face_sets = RNA_boolean_get(op->ptr, "use_face_sets");
@@ -654,17 +685,21 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent
Object *ob = CTX_data_active_object(C);
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
- int filter_type = RNA_enum_get(op->ptr, "type");
SculptSession *ss = ob->sculpt;
- PBVH *pbvh = ob->sculpt->pbvh;
- int deform_axis = RNA_enum_get(op->ptr, "deform_axis");
+ const eMeshFilterDeformAxis deform_axis = RNA_enum_get(op->ptr, "deform_axis");
+ const eSculptMeshFilterType filter_type = RNA_enum_get(op->ptr, "type");
+ const bool use_face_sets = RNA_boolean_get(op->ptr, "use_face_sets");
+ const bool needs_topology_info = sculpt_mesh_filter_needs_pmap(filter_type, use_face_sets);
+
if (deform_axis == 0) {
+ /* All axis are disabled, so the filter is not going to produce any deformation. */
return OPERATOR_CANCELLED;
}
- if (RNA_boolean_get(op->ptr, "use_face_sets")) {
- /* Update the active vertex */
+ if (use_face_sets) {
+ /* Update the active face set manually as the paint cursor is not enabled when using the Mesh
+ * Filter Tool. */
float mouse[2];
SculptCursorGeometryInfo sgi;
mouse[0] = event->mval[0];
@@ -672,67 +707,48 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent
SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false);
}
- const bool use_face_sets = RNA_boolean_get(op->ptr, "use_face_sets");
-
SCULPT_vertex_random_access_ensure(ss);
-
- const bool needs_topology_info = sculpt_mesh_filter_needs_pmap(filter_type, use_face_sets);
BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_topology_info, false, false);
if (needs_topology_info) {
SCULPT_boundary_info_ensure(ob);
}
- const int totvert = SCULPT_vertex_count_get(ss);
- if (BKE_pbvh_type(pbvh) == PBVH_FACES && needs_topology_info && !ob->sculpt->pmap) {
- return OPERATOR_CANCELLED;
- }
-
- SCULPT_undo_push_begin("Mesh filter");
-
- if (ELEM(filter_type, MESH_FILTER_RELAX, MESH_FILTER_RELAX_FACE_SETS)) {
- SCULPT_boundary_info_ensure(ob);
- }
+ SCULPT_undo_push_begin("Mesh Filter");
SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS);
- if (use_face_sets) {
- ss->filter_cache->active_face_set = SCULPT_active_face_set_get(ss);
- }
- else {
- ss->filter_cache->active_face_set = SCULPT_FACE_SET_NONE;
- }
-
- if (RNA_enum_get(op->ptr, "type") == MESH_FILTER_SURFACE_SMOOTH) {
- ss->filter_cache->surface_smooth_laplacian_disp = MEM_mallocN(sizeof(float[3]) * totvert,
- "surface smooth disp");
- ss->filter_cache->surface_smooth_shape_preservation = RNA_float_get(
- op->ptr, "surface_smooth_shape_preservation");
- ss->filter_cache->surface_smooth_current_vertex = RNA_float_get(
- op->ptr, "surface_smooth_current_vertex");
- }
-
- if (RNA_enum_get(op->ptr, "type") == MESH_FILTER_SHARPEN) {
- ss->filter_cache->sharpen_smooth_ratio = RNA_float_get(op->ptr, "sharpen_smooth_ratio");
- ss->filter_cache->sharpen_intensify_detail_strength = RNA_float_get(
- op->ptr, "sharpen_intensify_detail_strength");
- ss->filter_cache->sharpen_curvature_smooth_iterations = RNA_int_get(
- op->ptr, "sharpen_curvature_smooth_iterations");
-
- ss->filter_cache->sharpen_factor = MEM_mallocN(sizeof(float) * totvert, "sharpen factor");
- ss->filter_cache->detail_directions = MEM_malloc_arrayN(
- totvert, sizeof(float[3]), "sharpen detail direction");
-
- mesh_filter_sharpen_init_factors(ss);
- }
-
- if (RNA_enum_get(op->ptr, "type") == MESH_FILTER_ENHANCE_DETAILS) {
- ss->filter_cache->detail_directions = MEM_malloc_arrayN(
- totvert, sizeof(float[3]), "detail direction");
- mesh_filter_enhance_details_init_directions(ss);
- }
+ FilterCache *filter_cache = ss->filter_cache;
+ filter_cache->active_face_set = use_face_sets ? SCULPT_active_face_set_get(ss) :
+ SCULPT_FACE_SET_NONE;
- if (RNA_enum_get(op->ptr, "type") == MESH_FILTER_ERASE_DISPLACEMENT) {
- mesh_filter_init_limit_surface_co(ss);
+ switch (filter_type) {
+ case MESH_FILTER_SURFACE_SMOOTH: {
+ const float shape_preservation = RNA_float_get(op->ptr, "surface_smooth_shape_preservation");
+ const float current_vertex_displacement = RNA_float_get(op->ptr,
+ "surface_smooth_current_vertex");
+ mesh_filter_surface_smooth_init(ss, shape_preservation, current_vertex_displacement);
+ break;
+ }
+ case MESH_FILTER_SHARPEN: {
+ const float smooth_ratio = RNA_float_get(op->ptr, "sharpen_smooth_ratio");
+ const float intensify_detail_strength = RNA_float_get(op->ptr,
+ "sharpen_intensify_detail_strength");
+ const int curvature_smooth_iterations = RNA_int_get(op->ptr,
+ "sharpen_curvature_smooth_iterations");
+ mesh_filter_sharpen_init(
+ ss, smooth_ratio, intensify_detail_strength, curvature_smooth_iterations);
+ break;
+ }
+ case MESH_FILTER_ENHANCE_DETAILS: {
+ mesh_filter_enhance_details_init_directions(ss);
+ break;
+ }
+ case MESH_FILTER_ERASE_DISPLACEMENT: {
+ mesh_filter_init_limit_surface_co(ss);
+ break;
+ }
+ default:
+ break;
}
ss->filter_cache->enabled_axis[0] = deform_axis & MESH_FILTER_DEFORM_X;
diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h
index 548ef00ad87..47a375a2318 100644
--- a/source/blender/editors/sculpt_paint/sculpt_intern.h
+++ b/source/blender/editors/sculpt_paint/sculpt_intern.h
@@ -398,8 +398,9 @@ void SCULPT_cloth_plane_falloff_preview_draw(const uint gpuattr,
BLI_INLINE bool SCULPT_is_cloth_deform_brush(const Brush *brush)
{
- return (brush->sculpt_tool == SCULPT_TOOL_CLOTH &&
- brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_GRAB) ||
+ return (brush->sculpt_tool == SCULPT_TOOL_CLOTH && ELEM(brush->cloth_deform_type,
+ BRUSH_CLOTH_DEFORM_GRAB,
+ BRUSH_CLOTH_DEFORM_SNAKE_HOOK)) ||
/* All brushes that are not the cloth brush deform the simulation using softbody
constriants instead of applying forces. */
(brush->sculpt_tool != SCULPT_TOOL_CLOTH &&
diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.c b/source/blender/editors/sculpt_paint/sculpt_smooth.c
index 63fe8643628..87ee7480c92 100644
--- a/source/blender/editors/sculpt_paint/sculpt_smooth.c
+++ b/source/blender/editors/sculpt_paint/sculpt_smooth.c
@@ -66,25 +66,40 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, float result[3],
{
float avg[3] = {0.0f, 0.0f, 0.0f};
int total = 0;
-
- if (SCULPT_vertex_is_boundary(ss, index)) {
- copy_v3_v3(result, SCULPT_vertex_co_get(ss, index));
- return;
- }
+ int neighbor_count = 0;
+ const bool is_boundary = SCULPT_vertex_is_boundary(ss, index);
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) {
- add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index));
- total++;
+ neighbor_count++;
+ if (is_boundary) {
+ /* Boundary vertices use only other boundary vertices. */
+ if (SCULPT_vertex_is_boundary(ss, ni.index)) {
+ add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index));
+ total++;
+ }
+ }
+ else {
+ /* Interior vertices use all neighbors. */
+ add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index));
+ total++;
+ }
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
- if (total > 0) {
- mul_v3_v3fl(result, avg, 1.0f / total);
+ /* Do not modify corner vertices. */
+ if (neighbor_count <= 2) {
+ copy_v3_v3(result, SCULPT_vertex_co_get(ss, index));
+ return;
}
- else {
+
+ /* Avoid division by 0 when there are no neighbors. */
+ if (total == 0) {
copy_v3_v3(result, SCULPT_vertex_co_get(ss, index));
+ return;
}
+
+ mul_v3_v3fl(result, avg, 1.0f / total);
}
/* For bmesh: Average surrounding verts based on an orthogonality measure.
@@ -316,7 +331,7 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata,
}
else {
float avg[3], val[3];
- SCULPT_neighbor_coords_average(ss, avg, vd.index);
+ SCULPT_neighbor_coords_average_interior(ss, avg, vd.index);
sub_v3_v3v3(val, avg, vd.co);
madd_v3_v3v3fl(val, vd.co, val, fade);
SCULPT_clip(sd, ss, vd.co, val);
diff --git a/source/blender/editors/sculpt_paint/sculpt_transform.c b/source/blender/editors/sculpt_paint/sculpt_transform.c
index bdada4d2565..b52b04eba3a 100644
--- a/source/blender/editors/sculpt_paint/sculpt_transform.c
+++ b/source/blender/editors/sculpt_paint/sculpt_transform.c
@@ -326,6 +326,12 @@ static int sculpt_set_pivot_position_exec(bContext *C, wmOperator *op)
MEM_SAFE_FREE(nodes);
}
+ /* Update the viewport navigation rotation origin. */
+ UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings;
+ copy_v3_v3(ups->average_stroke_accum, ss->pivot_pos);
+ ups->average_stroke_counter = 1;
+ ups->last_stroke_valid = true;
+
ED_region_tag_redraw(region);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob->data);
diff --git a/source/blender/editors/sound/sound_ops.c b/source/blender/editors/sound/sound_ops.c
index e8eddc014db..d4f5f066d48 100644
--- a/source/blender/editors/sound/sound_ops.c
+++ b/source/blender/editors/sound/sound_ops.c
@@ -257,10 +257,10 @@ static void sound_update_animation_flags(Scene *scene)
}
scene->id.tag |= LIB_TAG_DOIT;
- SEQ_BEGIN (scene->ed, seq) {
+ SEQ_ALL_BEGIN (scene->ed, seq) {
BKE_sequencer_recursive_apply(seq, sound_update_animation_flags_fn, scene);
}
- SEQ_END;
+ SEQ_ALL_END;
fcu = id_data_find_fcurve(&scene->id, scene, &RNA_Scene, "audio_volume", 0, &driven);
if (fcu || driven) {
diff --git a/source/blender/editors/space_action/space_action.c b/source/blender/editors/space_action/space_action.c
index db55eff8284..cd4197d1df8 100644
--- a/source/blender/editors/space_action/space_action.c
+++ b/source/blender/editors/space_action/space_action.c
@@ -191,7 +191,6 @@ static void action_main_region_draw(const bContext *C, ARegion *region)
/* clear and setup matrix */
UI_ThemeClearColor(TH_BACK);
- GPU_clear(GPU_COLOR_BIT);
UI_view2d_view_ortho(v2d);
@@ -278,7 +277,6 @@ static void action_channel_region_draw(const bContext *C, ARegion *region)
/* clear and setup matrix */
UI_ThemeClearColor(TH_BACK);
- GPU_clear(GPU_COLOR_BIT);
UI_view2d_view_ortho(v2d);
diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c
index 3976e18d70c..e567b3ca54c 100644
--- a/source/blender/editors/space_buttons/buttons_context.c
+++ b/source/blender/editors/space_buttons/buttons_context.c
@@ -795,8 +795,12 @@ const char *buttons_context_dir[] = {
"line_style",
"collection",
"gpencil",
+#ifdef WITH_HAIR_NODES
"hair",
+#endif
+#ifdef WITH_PARTICLE_NODES
"pointcloud",
+#endif
"volume",
NULL,
};
diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c
index dc34e56dc92..d7cf2e4d544 100644
--- a/source/blender/editors/space_buttons/space_buttons.c
+++ b/source/blender/editors/space_buttons/space_buttons.c
@@ -292,9 +292,7 @@ static void buttons_main_region_layout_properties(const bContext *C,
break;
}
- const bool vertical = true;
- ED_region_panels_layout_ex(
- C, region, &region->type->paneltypes, contexts, sbuts->mainb, vertical, NULL);
+ ED_region_panels_layout_ex(C, region, &region->type->paneltypes, contexts, NULL);
}
static void buttons_main_region_layout(const bContext *C, ARegion *region)
diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c
index d27b80efd40..18df8774e09 100644
--- a/source/blender/editors/space_clip/space_clip.c
+++ b/source/blender/editors/space_clip/space_clip.c
@@ -926,7 +926,6 @@ static void clip_main_region_draw(const bContext *C, ARegion *region)
/* clear and setup matrix */
UI_ThemeClearColor(TH_BACK);
- GPU_clear(GPU_COLOR_BIT);
/* data... */
movieclip_main_area_set_view2d(C, region);
@@ -1054,7 +1053,6 @@ static void graph_region_draw(const bContext *C, ARegion *region)
/* clear and setup matrix */
UI_ThemeClearColor(TH_BACK);
- GPU_clear(GPU_COLOR_BIT);
UI_view2d_view_ortho(v2d);
@@ -1099,7 +1097,6 @@ static void dopesheet_region_draw(const bContext *C, ARegion *region)
/* clear and setup matrix */
UI_ThemeClearColor(TH_BACK);
- GPU_clear(GPU_COLOR_BIT);
UI_view2d_view_ortho(v2d);
@@ -1172,7 +1169,6 @@ static void clip_channels_region_draw(const bContext *C, ARegion *region)
/* clear and setup matrix */
UI_ThemeClearColor(TH_BACK);
- GPU_clear(GPU_COLOR_BIT);
UI_view2d_view_ortho(v2d);
diff --git a/source/blender/editors/space_console/space_console.c b/source/blender/editors/space_console/space_console.c
index 3a0125356f7..4b554e0c5c0 100644
--- a/source/blender/editors/space_console/space_console.c
+++ b/source/blender/editors/space_console/space_console.c
@@ -215,7 +215,6 @@ static void console_main_region_draw(const bContext *C, ARegion *region)
/* clear and setup matrix */
UI_ThemeClearColor(TH_BACK);
- GPU_clear(GPU_COLOR_BIT);
/* worlks best with no view2d matrix set */
UI_view2d_view_ortho(v2d);
diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c
index 51e6c99ff39..7039eba7db1 100644
--- a/source/blender/editors/space_file/file_draw.c
+++ b/source/blender/editors/space_file/file_draw.c
@@ -441,7 +441,9 @@ static void draw_background(FileLayout *layout, View2D *v2d)
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
- immUniformThemeColorShade(TH_BACK, -7);
+ float col_alternating[4];
+ UI_GetThemeColor4fv(TH_ROW_ALTERNATE, col_alternating);
+ immUniformThemeColorBlend(TH_BACK, TH_ROW_ALTERNATE, col_alternating[3]);
/* alternating flat shade background */
for (i = 2; (i <= layout->rows + 1); i += 2) {
diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c
index e9ffd4583d7..8c4b2a1b8a6 100644
--- a/source/blender/editors/space_file/file_ops.c
+++ b/source/blender/editors/space_file/file_ops.c
@@ -1722,7 +1722,11 @@ void FILE_OT_execute(struct wmOperatorType *ot)
/* api callbacks */
ot->invoke = file_exec_invoke;
ot->exec = file_exec;
- ot->poll = file_operator_poll;
+ /* Important since handler is on window level.
+ *
+ * Avoid using #file_operator_poll since this is also used for entering directories
+ * which is used even when the file manager doesn't have an operator. */
+ ot->poll = ED_operator_file_active;
/* properties */
prop = RNA_def_boolean(ot->srna,
@@ -2293,7 +2297,7 @@ static void file_expand_directory(bContext *C)
}
#else
{
- get_default_root(sfile->params->dir);
+ BLI_windows_get_default_root_dir(sfile->params->dir);
}
/* change "C:" --> "C:\", [#28102] */
else if ((isalpha(sfile->params->dir[0]) && (sfile->params->dir[1] == ':')) &&
diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c
index 67ea22a7ef5..0ade50814e0 100644
--- a/source/blender/editors/space_file/filelist.c
+++ b/source/blender/editors/space_file/filelist.c
@@ -1127,7 +1127,7 @@ static void parent_dir_until_exists_or_default_root(char *dir)
{
if (!BLI_path_parent_dir_until_exists(dir)) {
#ifdef WIN32
- get_default_root(dir);
+ BLI_windows_get_default_root_dir(dir);
#else
strcpy(dir, "/");
#endif
diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c
index f520f91b89b..61d6d8bf678 100644
--- a/source/blender/editors/space_file/space_file.c
+++ b/source/blender/editors/space_file/space_file.c
@@ -449,7 +449,6 @@ static void file_main_region_draw(const bContext *C, ARegion *region)
FileSelectParams *params = ED_fileselect_get_params(sfile);
View2D *v2d = &region->v2d;
- float col[3];
/* Needed, because filelist is not initialized on loading */
if (!sfile->files || filelist_empty(sfile->files)) {
@@ -457,9 +456,7 @@ static void file_main_region_draw(const bContext *C, ARegion *region)
}
/* clear and setup matrix */
- UI_GetThemeColor3fv(TH_BACK, col);
- GPU_clear_color(col[0], col[1], col[2], 1.0f);
- GPU_clear(GPU_COLOR_BIT);
+ UI_ThemeClearColor(TH_BACK);
/* Allow dynamically sliders to be set, saves notifiers etc. */
diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c
index 4e0f60544e6..a1e75e2b9b2 100644
--- a/source/blender/editors/space_graph/space_graph.c
+++ b/source/blender/editors/space_graph/space_graph.c
@@ -200,12 +200,9 @@ static void graph_main_region_draw(const bContext *C, ARegion *region)
Scene *scene = CTX_data_scene(C);
bAnimContext ac;
View2D *v2d = &region->v2d;
- float col[3];
/* clear and setup matrix */
- UI_GetThemeColor3fv(TH_BACK, col);
- GPU_clear_color(col[0], col[1], col[2], 1.0f);
- GPU_clear(GPU_COLOR_BIT);
+ UI_ThemeClearColor(TH_BACK);
UI_view2d_view_ortho(v2d);
@@ -358,12 +355,9 @@ static void graph_channel_region_draw(const bContext *C, ARegion *region)
{
bAnimContext ac;
View2D *v2d = &region->v2d;
- float col[3];
/* clear and setup matrix */
- UI_GetThemeColor3fv(TH_BACK, col);
- GPU_clear_color(col[0], col[1], col[2], 1.0f);
- GPU_clear(GPU_COLOR_BIT);
+ UI_ThemeClearColor(TH_BACK);
UI_view2d_view_ortho(v2d);
diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c
index d58f5ede7d7..058436a46bf 100644
--- a/source/blender/editors/space_image/image_draw.c
+++ b/source/blender/editors/space_image/image_draw.c
@@ -523,7 +523,7 @@ static void sima_draw_zbuffloat_pixels(Scene *scene,
GPU_shader_uniform_vector(
state.shader, GPU_shader_get_uniform(state.shader, "shuffle"), 4, 1, red);
- immDrawPixelsTex(&state, x1, y1, rectx, recty, GL_R16F, false, rectf, zoomx, zoomy, NULL);
+ immDrawPixelsTex(&state, x1, y1, rectx, recty, GPU_R16F, false, rectf, zoomx, zoomy, NULL);
MEM_freeN(rectf);
}
diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c
index a64d5505ebe..a806e3e25d1 100644
--- a/source/blender/editors/space_image/space_image.c
+++ b/source/blender/editors/space_image/space_image.c
@@ -650,7 +650,6 @@ static void image_main_region_draw(const bContext *C, ARegion *region)
GPU_framebuffer_bind(framebuffer_default);
GPU_clear_color(0.0f, 0.0f, 0.0f, 0.0f);
- GPU_clear(GPU_COLOR_BIT);
GPU_framebuffer_bind(framebuffer_overlay);
@@ -661,8 +660,7 @@ static void image_main_region_draw(const bContext *C, ARegion *region)
UI_GetThemeColor3fv(TH_BACK, col);
srgb_to_linearrgb_v3_v3(col, col);
GPU_clear_color(col[0], col[1], col[2], 1.0f);
- GPU_clear(GPU_COLOR_BIT);
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
image_user_refresh_scene(C, sima);
@@ -836,9 +834,7 @@ static void image_buttons_region_layout(const bContext *C, ARegion *region)
break;
}
- const bool vertical = true;
- ED_region_panels_layout_ex(
- C, region, &region->type->paneltypes, contexts_base, -1, vertical, NULL);
+ ED_region_panels_layout_ex(C, region, &region->type->paneltypes, contexts_base, NULL);
}
static void image_buttons_region_draw(const bContext *C, ARegion *region)
diff --git a/source/blender/editors/space_info/info_draw.c b/source/blender/editors/space_info/info_draw.c
index 72533b88406..595da97c75a 100644
--- a/source/blender/editors/space_info/info_draw.c
+++ b/source/blender/editors/space_info/info_draw.c
@@ -147,7 +147,6 @@ static int report_textview_begin(TextViewContext *tvc)
tvc->iter = reports->list.last;
UI_ThemeClearColor(TH_BACK);
- GPU_clear(GPU_COLOR_BIT);
tvc->iter_tmp = 0;
if (tvc->iter && report_textview_skip__internal(tvc)) {
diff --git a/source/blender/editors/space_info/info_stats.c b/source/blender/editors/space_info/info_stats.c
index e97031736ca..301e88b0904 100644
--- a/source/blender/editors/space_info/info_stats.c
+++ b/source/blender/editors/space_info/info_stats.c
@@ -426,7 +426,7 @@ static bool format_stats(Main *bmain,
if (wm->is_interface_locked) {
return false;
}
- Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, true);
+ Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer);
stats_update(depsgraph, view_layer);
}
diff --git a/source/blender/editors/space_info/space_info.c b/source/blender/editors/space_info/space_info.c
index b9153ec0cbd..8d3f21aefeb 100644
--- a/source/blender/editors/space_info/space_info.c
+++ b/source/blender/editors/space_info/space_info.c
@@ -142,7 +142,6 @@ static void info_main_region_draw(const bContext *C, ARegion *region)
/* clear and setup matrix */
UI_ThemeClearColor(TH_BACK);
- GPU_clear(GPU_COLOR_BIT);
/* quick way to avoid drawing if not bug enough */
if (region->winy < 16) {
diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c
index bc9bd0e18f2..dc8f616c5e6 100644
--- a/source/blender/editors/space_nla/nla_edit.c
+++ b/source/blender/editors/space_nla/nla_edit.c
@@ -1847,11 +1847,7 @@ static int nlaedit_sync_actlen_exec(bContext *C, wmOperator *op)
continue;
}
- /* recalculate the length of the action */
- calc_action_range(strip->act, &strip->actstart, &strip->actend, 0);
-
- /* adjust the strip extents in response to this */
- BKE_nlastrip_recalculate_bounds(strip);
+ BKE_nlastrip_recalculate_bounds_sync_action(strip);
ale->update |= ANIM_UPDATE_DEPS;
}
diff --git a/source/blender/editors/space_nla/space_nla.c b/source/blender/editors/space_nla/space_nla.c
index 7bbfe451eed..7a0cd35ece1 100644
--- a/source/blender/editors/space_nla/space_nla.c
+++ b/source/blender/editors/space_nla/space_nla.c
@@ -194,7 +194,6 @@ static void nla_channel_region_draw(const bContext *C, ARegion *region)
/* clear and setup matrix */
UI_ThemeClearColor(TH_BACK);
- GPU_clear(GPU_COLOR_BIT);
UI_view2d_view_ortho(v2d);
@@ -238,7 +237,6 @@ static void nla_main_region_draw(const bContext *C, ARegion *region)
/* clear and setup matrix */
UI_ThemeClearColor(TH_BACK);
- GPU_clear(GPU_COLOR_BIT);
UI_view2d_view_ortho(v2d);
diff --git a/source/blender/editors/space_node/node_draw.c b/source/blender/editors/space_node/node_draw.c
index 814473b0e9a..56e53e79a8d 100644
--- a/source/blender/editors/space_node/node_draw.c
+++ b/source/blender/editors/space_node/node_draw.c
@@ -1343,8 +1343,6 @@ static void node_draw_basis(const bContext *C,
}
}
- UI_ThemeClearColor(color_id);
-
UI_block_end(C, node->block);
UI_block_draw(C, node->block);
node->block = NULL;
@@ -1737,8 +1735,8 @@ void drawnodespace(const bContext *C, ARegion *region)
UI_view2d_view_ortho(v2d);
UI_ThemeClearColor(TH_BACK);
- GPU_clear(GPU_COLOR_BIT);
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
+ GPU_scissor_test(true);
/* XXX snode->cursor set in coordspace for placing new nodes, used for drawing noodles too */
UI_view2d_region_to_view(&region->v2d,
diff --git a/source/blender/editors/space_node/node_templates.c b/source/blender/editors/space_node/node_templates.c
index 4f15cec8c84..654bb94cc78 100644
--- a/source/blender/editors/space_node/node_templates.c
+++ b/source/blender/editors/space_node/node_templates.c
@@ -281,7 +281,9 @@ static void node_socket_add_replace(const bContext *C,
/* also preserve mapping for texture nodes */
if (node_from->typeinfo->nclass == NODE_CLASS_TEXTURE &&
- node_prev->typeinfo->nclass == NODE_CLASS_TEXTURE) {
+ node_prev->typeinfo->nclass == NODE_CLASS_TEXTURE &&
+ /* White noise texture node does not have NodeTexBase. */
+ node_from->storage != NULL && node_prev->storage != NULL) {
memcpy(node_from->storage, node_prev->storage, sizeof(NodeTexBase));
}
diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c
index 1705b9dd606..fbef3aa07d7 100644
--- a/source/blender/editors/space_outliner/outliner_draw.c
+++ b/source/blender/editors/space_outliner/outliner_draw.c
@@ -182,7 +182,7 @@ static void restrictbutton_bone_visibility_fn(bContext *C, void *poin, void *UNU
{
Bone *bone = (Bone *)poin;
- if (CTX_wm_window(C)->eventstate->ctrl) {
+ if (CTX_wm_window(C)->eventstate->shift) {
restrictbutton_recursive_bone(bone, BONE_HIDDEN_P, (bone->flag & BONE_HIDDEN_P) != 0);
}
}
@@ -194,7 +194,7 @@ static void restrictbutton_bone_select_fn(bContext *C, void *UNUSED(poin), void
bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
}
- if (CTX_wm_window(C)->eventstate->ctrl) {
+ if (CTX_wm_window(C)->eventstate->shift) {
restrictbutton_recursive_bone(bone, BONE_UNSELECTABLE, (bone->flag & BONE_UNSELECTABLE) != 0);
}
@@ -209,7 +209,7 @@ static void restrictbutton_ebone_select_fn(bContext *C, void *UNUSED(poin), void
ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
}
- if (CTX_wm_window(C)->eventstate->ctrl) {
+ if (CTX_wm_window(C)->eventstate->shift) {
restrictbutton_recursive_ebone(
C, ebone, BONE_UNSELECTABLE, (ebone->flag & BONE_UNSELECTABLE) != 0);
}
@@ -224,7 +224,7 @@ static void restrictbutton_ebone_visibility_fn(bContext *C, void *UNUSED(poin),
ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
}
- if (CTX_wm_window(C)->eventstate->ctrl) {
+ if (CTX_wm_window(C)->eventstate->shift) {
restrictbutton_recursive_ebone(C, ebone, BONE_HIDDEN_A, (ebone->flag & BONE_HIDDEN_A) != 0);
}
@@ -1300,7 +1300,8 @@ static void outliner_draw_restrictbuts(uiBlock *block,
0,
-1,
-1,
- TIP_("Restrict visibility in the 3D View"));
+ TIP_("Restrict visibility in the 3D View\n"
+ "* Shift to set children"));
UI_but_func_set(bt, restrictbutton_bone_visibility_fn, bone, NULL);
UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE);
@@ -1321,7 +1322,8 @@ static void outliner_draw_restrictbuts(uiBlock *block,
0,
0,
0,
- TIP_("Restrict selection in the 3D View"));
+ TIP_("Restrict selection in the 3D View\n"
+ "* Shift to set children"));
UI_but_func_set(bt, restrictbutton_bone_select_fn, ob->data, bone);
UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE);
@@ -1345,7 +1347,8 @@ static void outliner_draw_restrictbuts(uiBlock *block,
0,
0,
0,
- TIP_("Restrict visibility in the 3D View"));
+ TIP_("Restrict visibility in the 3D View\n"
+ "* Shift to set children"));
UI_but_func_set(bt, restrictbutton_ebone_visibility_fn, NULL, ebone);
UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE);
@@ -1366,7 +1369,8 @@ static void outliner_draw_restrictbuts(uiBlock *block,
0,
0,
0,
- TIP_("Restrict selection in the 3D View"));
+ TIP_("Restrict selection in the 3D View\n"
+ "* Shift to set children"));
UI_but_func_set(bt, restrictbutton_ebone_select_fn, NULL, ebone);
UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE);
@@ -2188,7 +2192,7 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te)
GpencilModifierData *md = BLI_findlink(&ob->greasepencil_modifiers, tselem->nr);
switch ((GpencilModifierType)md->type) {
case eGpencilModifierType_Noise:
- data.icon = ICON_RNDCURVE;
+ data.icon = ICON_MOD_NOISE;
break;
case eGpencilModifierType_Subdiv:
data.icon = ICON_MOD_SUBSURF;
@@ -2232,6 +2236,15 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te)
case eGpencilModifierType_Armature:
data.icon = ICON_MOD_ARMATURE;
break;
+ case eGpencilModifierType_Multiply:
+ data.icon = ICON_GP_MULTIFRAME_EDITING;
+ break;
+ case eGpencilModifierType_Time:
+ data.icon = ICON_MOD_TIME;
+ break;
+ case eGpencilModifierType_Texture:
+ data.icon = ICON_TEXTURE;
+ break;
/* Default */
default:
@@ -2354,6 +2367,11 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te)
data.icon = ICON_OUTLINER_DATA_GP_LAYER;
break;
}
+ case TSE_GPENCIL_EFFECT_BASE:
+ case TSE_GPENCIL_EFFECT:
+ data.drag_id = tselem->id;
+ data.icon = ICON_SHADERFX;
+ break;
default:
data.icon = ICON_DOT;
break;
@@ -2743,6 +2761,23 @@ static void outliner_icon_background_colors(float icon_color[4], float icon_bord
icon_border[3] = 0.2f;
}
+/* Draw a rounded rectangle behind icons of active elements. */
+static void outliner_draw_active_indicator(const float minx,
+ const float miny,
+ const float maxx,
+ const float maxy,
+ const float icon_color[4],
+ const float icon_border[4])
+{
+ const float ufac = UI_UNIT_X / 20.0f;
+ const float radius = UI_UNIT_Y / 4.0f;
+
+ UI_draw_roundbox_corner_set(UI_CNR_ALL);
+ UI_draw_roundbox_aa(true, minx, miny + ufac, maxx, maxy - ufac, radius, icon_color);
+ UI_draw_roundbox_aa(false, minx, miny + ufac, maxx, maxy - ufac, radius, icon_border);
+ GPU_blend(GPU_BLEND_ALPHA); /* Roundbox disables. */
+}
+
static void outliner_draw_iconrow_doit(uiBlock *block,
TreeElement *te,
const uiFontStyle *fstyle,
@@ -2756,31 +2791,19 @@ static void outliner_draw_iconrow_doit(uiBlock *block,
TreeStoreElem *tselem = TREESTORE(te);
if (active != OL_DRAWSEL_NONE) {
- float ufac = UI_UNIT_X / 20.0f;
float icon_color[4], icon_border[4];
outliner_icon_background_colors(icon_color, icon_border);
if (active == OL_DRAWSEL_ACTIVE) {
UI_GetThemeColor4fv(TH_EDITED_OBJECT, icon_color);
icon_border[3] = 0.3f;
}
- UI_draw_roundbox_corner_set(UI_CNR_ALL);
-
- UI_draw_roundbox_aa(true,
- (float)*offsx,
- (float)ys + ufac,
- (float)*offsx + UI_UNIT_X,
- (float)ys + UI_UNIT_Y - ufac,
- (float)UI_UNIT_Y / 4.0f,
- icon_color);
- /* border around it */
- UI_draw_roundbox_aa(false,
- (float)*offsx,
- (float)ys + ufac,
- (float)*offsx + UI_UNIT_X,
- (float)ys + UI_UNIT_Y - ufac,
- (float)UI_UNIT_Y / 4.0f,
- icon_border);
- GPU_blend(GPU_BLEND_ALPHA); /* Roundbox disables. */
+
+ outliner_draw_active_indicator((float)*offsx,
+ (float)ys,
+ (float)*offsx + UI_UNIT_X,
+ (float)ys + UI_UNIT_Y,
+ icon_color,
+ icon_border);
}
if (tselem->flag & TSE_HIGHLIGHTED) {
@@ -3052,23 +3075,12 @@ static void outliner_draw_tree_element(bContext *C,
/* active circle */
if (active != OL_DRAWSEL_NONE) {
- UI_draw_roundbox_corner_set(UI_CNR_ALL);
- UI_draw_roundbox_aa(true,
- (float)startx + offsx + UI_UNIT_X,
- (float)*starty + ufac,
- (float)startx + offsx + 2.0f * UI_UNIT_X,
- (float)*starty + UI_UNIT_Y - ufac,
- UI_UNIT_Y / 4.0f,
- icon_bgcolor);
- /* border around it */
- UI_draw_roundbox_aa(false,
- (float)startx + offsx + UI_UNIT_X,
- (float)*starty + ufac,
- (float)startx + offsx + 2.0f * UI_UNIT_X,
- (float)*starty + UI_UNIT_Y - ufac,
- UI_UNIT_Y / 4.0f,
- icon_border);
- GPU_blend(GPU_BLEND_ALPHA); /* roundbox disables it */
+ outliner_draw_active_indicator((float)startx + offsx + UI_UNIT_X,
+ (float)*starty,
+ (float)startx + offsx + 2.0f * UI_UNIT_X,
+ (float)*starty + UI_UNIT_Y,
+ icon_bgcolor,
+ icon_border);
te->flag |= TE_ACTIVE; /* For lookup in display hierarchies. */
}
diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c
index cd2fcd8e2cf..ad7346a5651 100644
--- a/source/blender/editors/space_outliner/outliner_edit.c
+++ b/source/blender/editors/space_outliner/outliner_edit.c
@@ -150,9 +150,22 @@ void OUTLINER_OT_highlight_update(wmOperatorType *ot)
* \{ */
/* Open or close a tree element, optionally toggling all children recursively */
-void outliner_item_openclose(TreeElement *te, bool open, bool toggle_all)
-{
+void outliner_item_openclose(SpaceOutliner *space_outliner,
+ TreeElement *te,
+ bool open,
+ bool toggle_all)
+{
+ /* Prevent opening leaf elements in the tree unless in the Data API display mode because in that
+ * mode subtrees are empty unless expanded. */
+ if (space_outliner->outlinevis != SO_DATA_API && BLI_listbase_is_empty(&te->subtree)) {
+ return;
+ }
+
+ /* Don't allow collapsing the scene collection. */
TreeStoreElem *tselem = TREESTORE(te);
+ if (tselem->type == TSE_VIEW_COLLECTION_BASE) {
+ return;
+ }
if (open) {
tselem->flag &= ~TSE_CLOSED;
@@ -191,15 +204,9 @@ static int outliner_item_openclose_modal(bContext *C, wmOperator *op, const wmEv
/* Only toggle openclose on the same level as the first clicked element */
if (te->xs == data->x_location) {
- outliner_item_openclose(te, data->open, false);
+ outliner_item_openclose(space_outliner, te, data->open, false);
- /* Avoid rebuild if possible. */
- if (outliner_element_needs_rebuild_on_open_change(TREESTORE(te))) {
- ED_region_tag_redraw(region);
- }
- else {
- ED_region_tag_redraw_no_rebuild(region);
- }
+ outliner_tag_redraw_avoid_rebuild_on_open_change(space_outliner, region);
}
}
@@ -238,14 +245,8 @@ static int outliner_item_openclose_invoke(bContext *C, wmOperator *op, const wmE
const bool open = (tselem->flag & TSE_CLOSED) ||
(toggle_all && (outliner_flag_is_any_test(&te->subtree, TSE_CLOSED, 1)));
- outliner_item_openclose(te, open, toggle_all);
- /* Avoid rebuild if possible. */
- if (outliner_element_needs_rebuild_on_open_change(TREESTORE(te))) {
- ED_region_tag_redraw(region);
- }
- else {
- ED_region_tag_redraw_no_rebuild(region);
- }
+ outliner_item_openclose(space_outliner, te, open, toggle_all);
+ outliner_tag_redraw_avoid_rebuild_on_open_change(space_outliner, region);
/* Only toggle once for single click toggling */
if (event->type == LEFTMOUSE) {
diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h
index 33dbbb274c0..bd283777397 100644
--- a/source/blender/editors/space_outliner/outliner_intern.h
+++ b/source/blender/editors/space_outliner/outliner_intern.h
@@ -237,7 +237,7 @@ void outliner_build_tree(struct Main *mainvar,
struct SpaceOutliner *space_outliner,
struct ARegion *region);
-bool outliner_element_needs_rebuild_on_open_change(const TreeStoreElem *tselem);
+bool outliner_mode_requires_always_rebuild(const struct SpaceOutliner *space_outliner);
typedef struct IDsSelectedData {
struct ListBase selected_array;
@@ -374,7 +374,10 @@ void item_object_mode_exit_fn(struct bContext *C,
void outliner_set_coordinates(struct ARegion *region, struct SpaceOutliner *space_outliner);
-void outliner_item_openclose(TreeElement *te, bool open, bool toggle_all);
+void outliner_item_openclose(struct SpaceOutliner *space_outliner,
+ TreeElement *te,
+ bool open,
+ bool toggle_all);
/* outliner_dragdrop.c */
void outliner_dropboxes(void);
@@ -515,6 +518,8 @@ float outliner_restrict_columns_width(const struct SpaceOutliner *space_outliner
TreeElement *outliner_find_element_with_flag(const ListBase *lb, short flag);
bool outliner_is_element_visible(const TreeElement *te);
void outliner_scroll_view(struct ARegion *region, int delta_y);
+void outliner_tag_redraw_avoid_rebuild_on_open_change(const struct SpaceOutliner *space_outliner,
+ struct ARegion *region);
/* outliner_sync.c ---------------------------------------------- */
diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c
index 1ac1b46f0d1..266ea293d43 100644
--- a/source/blender/editors/space_outliner/outliner_select.c
+++ b/source/blender/editors/space_outliner/outliner_select.c
@@ -46,6 +46,7 @@
#include "BKE_main.h"
#include "BKE_object.h"
#include "BKE_paint.h"
+#include "BKE_report.h"
#include "BKE_scene.h"
#include "BKE_sequencer.h"
#include "BKE_workspace.h"
@@ -193,12 +194,17 @@ static void do_outliner_item_posemode_toggle(
}
else {
bool ok = false;
- if (ob->mode & OB_MODE_POSE) {
+
+ if (ID_IS_LINKED(ob)) {
+ BKE_report(CTX_wm_reports(C), RPT_WARNING, "Cannot pose libdata");
+ }
+ else if (ob->mode & OB_MODE_POSE) {
ok = ED_object_posemode_exit_ex(bmain, ob);
}
else {
ok = ED_object_posemode_enter_ex(bmain, ob);
}
+
if (ok) {
ED_object_base_select(base, (ob->mode & OB_MODE_POSE) ? BA_SELECT : BA_DESELECT);
@@ -1000,7 +1006,9 @@ static eOLDrawState tree_element_active_master_collection(bContext *C,
ViewLayer *view_layer = CTX_data_view_layer(C);
LayerCollection *layer_collection = view_layer->layer_collections.first;
BKE_layer_collection_activate(view_layer, layer_collection);
- WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL);
+ /* A very precise notifier - ND_LAYER alone is quite vague, we want to avoid unnecessary work
+ * when only the active collection changes. */
+ WM_main_add_notifier(NC_SCENE | ND_LAYER | NS_LAYER_COLLECTION | NA_ACTIVATED, NULL);
}
return OL_DRAWSEL_NONE;
@@ -1022,7 +1030,9 @@ static eOLDrawState tree_element_active_layer_collection(bContext *C,
LayerCollection *layer_collection = te->directdata;
ViewLayer *view_layer = BKE_view_layer_find_from_collection(scene, layer_collection);
BKE_layer_collection_activate(view_layer, layer_collection);
- WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL);
+ /* A very precise notifier - ND_LAYER alone is quite vague, we want to avoid unnecessary work
+ * when only the active collection changes. */
+ WM_main_add_notifier(NC_SCENE | ND_LAYER | NS_LAYER_COLLECTION | NA_ACTIVATED, NULL);
}
return OL_DRAWSEL_NONE;
@@ -1507,7 +1517,7 @@ static int outliner_box_select_exec(bContext *C, wmOperator *op)
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
- ED_region_tag_redraw(region);
+ ED_region_tag_redraw_no_rebuild(region);
ED_outliner_select_sync_from_outliner(C, space_outliner);
@@ -1630,6 +1640,40 @@ static TreeElement *outliner_find_next_element(SpaceOutliner *space_outliner, Tr
return te;
}
+static TreeElement *outliner_walk_left(SpaceOutliner *space_outliner,
+ TreeElement *te,
+ bool toggle_all)
+{
+ TreeStoreElem *tselem = TREESTORE(te);
+
+ if (TSELEM_OPEN(tselem, space_outliner)) {
+ outliner_item_openclose(space_outliner, te, false, toggle_all);
+ }
+ /* Only walk up a level if the element is closed and not toggling expand */
+ else if (!toggle_all && te->parent) {
+ te = te->parent;
+ }
+
+ return te;
+}
+
+static TreeElement *outliner_walk_right(SpaceOutliner *space_outliner,
+ TreeElement *te,
+ bool toggle_all)
+{
+ TreeStoreElem *tselem = TREESTORE(te);
+
+ /* Only walk down a level if the element is open and not toggling expand */
+ if (!toggle_all && TSELEM_OPEN(tselem, space_outliner) && !BLI_listbase_is_empty(&te->subtree)) {
+ te = te->subtree.first;
+ }
+ else {
+ outliner_item_openclose(space_outliner, te, true, toggle_all);
+ }
+
+ return te;
+}
+
static TreeElement *do_outliner_select_walk(SpaceOutliner *space_outliner,
TreeElement *te,
const int direction,
@@ -1646,10 +1690,10 @@ static TreeElement *do_outliner_select_walk(SpaceOutliner *space_outliner,
te = outliner_find_next_element(space_outliner, te);
break;
case UI_SELECT_WALK_LEFT:
- outliner_item_openclose(te, false, toggle_all);
+ te = outliner_walk_left(space_outliner, te, toggle_all);
break;
case UI_SELECT_WALK_RIGHT:
- outliner_item_openclose(te, true, toggle_all);
+ te = outliner_walk_right(space_outliner, te, toggle_all);
break;
}
@@ -1729,7 +1773,7 @@ static int outliner_walk_select_invoke(bContext *C, wmOperator *op, const wmEven
outliner_walk_scroll(region, active_te);
ED_outliner_select_sync_from_outliner(C, space_outliner);
- ED_region_tag_redraw(region);
+ outliner_tag_redraw_avoid_rebuild_on_open_change(space_outliner, region);
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c
index 6532ff189b5..2a13f9d6a66 100644
--- a/source/blender/editors/space_outliner/outliner_tools.c
+++ b/source/blender/editors/space_outliner/outliner_tools.c
@@ -792,10 +792,11 @@ static void id_override_library_create_fn(bContext *C,
}
else if (ID_IS_OVERRIDABLE_LIBRARY(id_root)) {
BKE_lib_override_library_create_from_id(bmain, id_root, true);
- }
- BKE_main_id_clear_newpoins(bmain);
- BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
+ /* Cleanup. */
+ BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
+ }
}
}
@@ -827,6 +828,68 @@ static void id_override_library_reset_fn(bContext *C,
}
}
+static void id_override_library_resync_fn(bContext *C,
+ ReportList *UNUSED(reports),
+ Scene *scene,
+ TreeElement *te,
+ TreeStoreElem *UNUSED(tsep),
+ TreeStoreElem *tselem,
+ void *UNUSED(user_data))
+{
+ BLI_assert(TSE_IS_REAL_ID(tselem));
+ ID *id_root = tselem->id;
+
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(id_root)) {
+ Main *bmain = CTX_data_main(C);
+
+ id_root->tag |= LIB_TAG_DOIT;
+
+ /* Tag all linked parents in tree hierarchy to be also overridden. */
+ while ((te = te->parent) != NULL) {
+ if (!TSE_IS_REAL_ID(te->store_elem)) {
+ continue;
+ }
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(te->store_elem->id)) {
+ break;
+ }
+ te->store_elem->id->tag |= LIB_TAG_DOIT;
+ }
+
+ BKE_lib_override_library_resync(bmain, scene, CTX_data_view_layer(C), id_root);
+ }
+}
+
+static void id_override_library_delete_fn(bContext *C,
+ ReportList *UNUSED(reports),
+ Scene *UNUSED(scene),
+ TreeElement *te,
+ TreeStoreElem *UNUSED(tsep),
+ TreeStoreElem *tselem,
+ void *UNUSED(user_data))
+{
+ BLI_assert(TSE_IS_REAL_ID(tselem));
+ ID *id_root = tselem->id;
+
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(id_root)) {
+ Main *bmain = CTX_data_main(C);
+
+ id_root->tag |= LIB_TAG_DOIT;
+
+ /* Tag all linked parents in tree hierarchy to be also overridden. */
+ while ((te = te->parent) != NULL) {
+ if (!TSE_IS_REAL_ID(te->store_elem)) {
+ continue;
+ }
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(te->store_elem->id)) {
+ break;
+ }
+ te->store_elem->id->tag |= LIB_TAG_DOIT;
+ }
+
+ BKE_lib_override_library_delete(bmain, id_root);
+ }
+}
+
static void id_fake_user_set_fn(bContext *UNUSED(C),
ReportList *UNUSED(reports),
Scene *UNUSED(scene),
@@ -1567,6 +1630,7 @@ static int outliner_delete_exec(bContext *C, wmOperator *op)
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
+ WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene);
ED_outliner_select_sync_from_object_tag(C);
return OPERATOR_FINISHED;
@@ -1607,6 +1671,8 @@ typedef enum eOutlinerIdOpTypes {
OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY,
OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET,
OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY,
+ OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY,
+ OUTLINER_IDOP_OVERRIDE_LIBRARY_DELETE_HIERARCHY,
OUTLINER_IDOP_SINGLE,
OUTLINER_IDOP_DELETE,
OUTLINER_IDOP_REMAP,
@@ -1653,6 +1719,18 @@ static const EnumPropertyItem prop_id_op_types[] = {
0,
"Reset Library Override Hierarchy",
"Reset this local override to its linked values, as well as its hierarchy of dependencies"},
+ {OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY,
+ "OVERRIDE_LIBRARY_RESYNC_HIERARCHY",
+ 0,
+ "Resync Library Override Hierarchy",
+ "Rebuild this local override from its linked reference, as well as its hierarchy of "
+ "dependencies"},
+ {OUTLINER_IDOP_OVERRIDE_LIBRARY_DELETE_HIERARCHY,
+ "OVERRIDE_LIBRARY_DELETE_HIERARCHY",
+ 0,
+ "Delete Library Override Hierarchy",
+ "Delete this local override (including its hierarchy of override dependencies) and relink "
+ "its usages to the linked data-blocks"},
{0, "", 0, NULL, NULL},
{OUTLINER_IDOP_COPY, "COPY", ICON_COPYDOWN, "Copy", ""},
{OUTLINER_IDOP_PASTE, "PASTE", ICON_PASTEDOWN, "Paste", ""},
@@ -1683,6 +1761,10 @@ static bool outliner_id_operation_item_poll(bContext *C,
case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET:
case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY:
return true;
+ case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY:
+ return true;
+ case OUTLINER_IDOP_OVERRIDE_LIBRARY_DELETE_HIERARCHY:
+ return true;
case OUTLINER_IDOP_SINGLE:
if (!space_outliner || ELEM(space_outliner->outlinevis, SO_SCENES, SO_VIEW_LAYER)) {
return true;
@@ -1818,7 +1900,6 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
break;
}
case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE: {
- /* make local */
outliner_do_libdata_operation(C,
op->reports,
scene,
@@ -1830,7 +1911,6 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
break;
}
case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY: {
- /* make local */
outliner_do_libdata_operation(C,
op->reports,
scene,
@@ -1842,7 +1922,6 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
break;
}
case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET: {
- /* make local */
outliner_do_libdata_operation(C,
op->reports,
scene,
@@ -1854,7 +1933,6 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
break;
}
case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY: {
- /* make local */
outliner_do_libdata_operation(C,
op->reports,
scene,
@@ -1865,6 +1943,28 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
ED_undo_push(C, "Reset Overridden Data Hierarchy");
break;
}
+ case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY: {
+ outliner_do_libdata_operation(C,
+ op->reports,
+ scene,
+ space_outliner,
+ &space_outliner->tree,
+ id_override_library_resync_fn,
+ &(OutlinerLibOverrideData){.do_hierarchy = true});
+ ED_undo_push(C, "Resync Overridden Data Hierarchy");
+ break;
+ }
+ case OUTLINER_IDOP_OVERRIDE_LIBRARY_DELETE_HIERARCHY: {
+ outliner_do_libdata_operation(C,
+ op->reports,
+ scene,
+ space_outliner,
+ &space_outliner->tree,
+ id_override_library_delete_fn,
+ &(OutlinerLibOverrideData){.do_hierarchy = true});
+ ED_undo_push(C, "Delete Overridden Data Hierarchy");
+ break;
+ }
case OUTLINER_IDOP_SINGLE: {
/* make single user */
switch (idlevel) {
diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c
index 60058c82283..22a7019ab91 100644
--- a/source/blender/editors/space_outliner/outliner_tree.c
+++ b/source/blender/editors/space_outliner/outliner_tree.c
@@ -32,6 +32,7 @@
#include "DNA_camera_types.h"
#include "DNA_collection_types.h"
#include "DNA_constraint_types.h"
+#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_hair_types.h"
#include "DNA_key_types.h"
@@ -46,6 +47,7 @@
#include "DNA_pointcloud_types.h"
#include "DNA_scene_types.h"
#include "DNA_sequence_types.h"
+#include "DNA_shader_fx_types.h"
#include "DNA_simulation_types.h"
#include "DNA_speaker_types.h"
#include "DNA_volume_types.h"
@@ -244,14 +246,12 @@ static TreeElement *outliner_add_element(SpaceOutliner *space_outliner,
/* -------------------------------------------------------- */
/**
- * Check if an element type needs a full rebuild if the open/collapsed state changes.
- * These element types don't add children if collapsed.
- *
- * This current check isn't great really. A per element-type flag would be preferable.
+ * Check if a display mode needs a full rebuild if the open/collapsed state changes.
+ * Element types in these modes don't actually add children if collapsed, so the rebuild is needed.
*/
-bool outliner_element_needs_rebuild_on_open_change(const TreeStoreElem *tselem)
+bool outliner_mode_requires_always_rebuild(const SpaceOutliner *space_outliner)
{
- return ELEM(tselem->type, TSE_RNA_STRUCT, TSE_RNA_PROPERTY, TSE_KEYMAP);
+ return ELEM(space_outliner->outlinevis, SO_DATA_API);
}
/* special handling of hierarchical non-lib data */
@@ -552,6 +552,70 @@ static void outliner_add_object_contents(SpaceOutliner *space_outliner,
}
}
+ /* Grease Pencil modifiers. */
+ if (!BLI_listbase_is_empty(&ob->greasepencil_modifiers)) {
+ TreeElement *ten_mod = outliner_add_element(
+ space_outliner, &te->subtree, ob, te, TSE_MODIFIER_BASE, 0);
+
+ ten_mod->name = IFACE_("Modifiers");
+ int index;
+ LISTBASE_FOREACH_INDEX (GpencilModifierData *, md, &ob->greasepencil_modifiers, index) {
+ TreeElement *ten = outliner_add_element(
+ space_outliner, &ten_mod->subtree, ob, ten_mod, TSE_MODIFIER, index);
+ ten->name = md->name;
+ ten->directdata = md;
+
+ if (md->type == eGpencilModifierType_Armature) {
+ outliner_add_element(space_outliner,
+ &ten->subtree,
+ ((ArmatureGpencilModifierData *)md)->object,
+ ten,
+ TSE_LINKED_OB,
+ 0);
+ }
+ else if (md->type == eGpencilModifierType_Hook) {
+ outliner_add_element(space_outliner,
+ &ten->subtree,
+ ((HookGpencilModifierData *)md)->object,
+ ten,
+ TSE_LINKED_OB,
+ 0);
+ }
+ else if (md->type == eGpencilModifierType_Lattice) {
+ outliner_add_element(space_outliner,
+ &ten->subtree,
+ ((LatticeGpencilModifierData *)md)->object,
+ ten,
+ TSE_LINKED_OB,
+ 0);
+ }
+ }
+ }
+
+ /* Grease Pencil effects. */
+ if (!BLI_listbase_is_empty(&ob->shader_fx)) {
+ TreeElement *ten_fx = outliner_add_element(
+ space_outliner, &te->subtree, ob, te, TSE_GPENCIL_EFFECT_BASE, 0);
+
+ ten_fx->name = IFACE_("Effects");
+ int index;
+ LISTBASE_FOREACH_INDEX (ShaderFxData *, fx, &ob->shader_fx, index) {
+ TreeElement *ten = outliner_add_element(
+ space_outliner, &ten_fx->subtree, ob, ten_fx, TSE_GPENCIL_EFFECT, index);
+ ten->name = fx->name;
+ ten->directdata = fx;
+
+ if (fx->type == eShaderFxType_Swirl) {
+ outliner_add_element(space_outliner,
+ &ten->subtree,
+ ((SwirlShaderFxData *)fx)->object,
+ ten,
+ TSE_LINKED_OB,
+ 0);
+ }
+ }
+ }
+
/* vertex groups */
if (ob->defbase.first) {
bDeformGroup *defgroup;
diff --git a/source/blender/editors/space_outliner/outliner_utils.c b/source/blender/editors/space_outliner/outliner_utils.c
index 1da44b5e51e..25dc7bc271e 100644
--- a/source/blender/editors/space_outliner/outliner_utils.c
+++ b/source/blender/editors/space_outliner/outliner_utils.c
@@ -37,6 +37,7 @@
#include "ED_armature.h"
#include "ED_outliner.h"
+#include "ED_screen.h"
#include "UI_interface.h"
#include "UI_view2d.h"
@@ -455,6 +456,23 @@ void outliner_scroll_view(ARegion *region, int delta_y)
}
}
+/**
+ * The outliner should generally use #ED_region_tag_redraw_no_rebuild() to avoid unnecessary tree
+ * rebuilds. If elements are open or closed, we may still have to rebuild.
+ * Upon changing the open/closed state, call this to avoid rebuilds if possible.
+ */
+void outliner_tag_redraw_avoid_rebuild_on_open_change(const SpaceOutliner *space_outliner,
+ ARegion *region)
+{
+ /* Avoid rebuild if possible. */
+ if (outliner_mode_requires_always_rebuild(space_outliner)) {
+ ED_region_tag_redraw(region);
+ }
+ else {
+ ED_region_tag_redraw_no_rebuild(region);
+ }
+}
+
/* Get base of object under cursor. Used for eyedropper tool */
Base *ED_outliner_give_base_under_cursor(bContext *C, const int mval[2])
{
diff --git a/source/blender/editors/space_outliner/space_outliner.c b/source/blender/editors/space_outliner/space_outliner.c
index b14afed81dd..6a63c3c65c3 100644
--- a/source/blender/editors/space_outliner/space_outliner.c
+++ b/source/blender/editors/space_outliner/space_outliner.c
@@ -87,7 +87,6 @@ static void outliner_main_region_draw(const bContext *C, ARegion *region)
/* clear */
UI_ThemeClearColor(TH_BACK);
- GPU_clear(GPU_COLOR_BIT);
draw_outliner(C);
@@ -114,6 +113,8 @@ static void outliner_main_region_listener(wmWindow *UNUSED(win),
switch (wmn->data) {
case ND_OB_ACTIVE:
case ND_OB_SELECT:
+ ED_region_tag_redraw_no_rebuild(region);
+ break;
case ND_OB_VISIBLE:
case ND_OB_RENDER:
case ND_MODE:
@@ -121,15 +122,23 @@ static void outliner_main_region_listener(wmWindow *UNUSED(win),
case ND_FRAME:
case ND_RENDER_OPTIONS:
case ND_SEQUENCER:
- case ND_LAYER:
case ND_LAYER_CONTENT:
case ND_WORLD:
case ND_SCENEBROWSE:
ED_region_tag_redraw(region);
break;
+ case ND_LAYER:
+ /* Avoid rebuild if only the active collection changes */
+ if ((wmn->subtype == NS_LAYER_COLLECTION) && (wmn->action == NA_ACTIVATED)) {
+ ED_region_tag_redraw_no_rebuild(region);
+ break;
+ }
+
+ ED_region_tag_redraw(region);
+ break;
}
- if (wmn->action & NA_EDITED) {
- ED_region_tag_redraw(region);
+ if (wmn->action == NA_EDITED) {
+ ED_region_tag_redraw_no_rebuild(region);
}
break;
case NC_OBJECT:
@@ -181,7 +190,7 @@ static void outliner_main_region_listener(wmWindow *UNUSED(win),
case NC_MATERIAL:
switch (wmn->data) {
case ND_SHADING_LINKS:
- ED_region_tag_redraw(region);
+ ED_region_tag_redraw_no_rebuild(region);
break;
}
break;
diff --git a/source/blender/editors/space_script/space_script.c b/source/blender/editors/space_script/space_script.c
index 4d0c2b658c6..3c3f7dc1e8e 100644
--- a/source/blender/editors/space_script/space_script.c
+++ b/source/blender/editors/space_script/space_script.c
@@ -127,7 +127,6 @@ static void script_main_region_draw(const bContext *C, ARegion *region)
/* clear and setup matrix */
UI_ThemeClearColor(TH_BACK);
- GPU_clear(GPU_COLOR_BIT);
UI_view2d_view_ortho(v2d);
diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c
index 8a6b97b3834..bd4503dbe54 100644
--- a/source/blender/editors/space_sequencer/sequencer_draw.c
+++ b/source/blender/editors/space_sequencer/sequencer_draw.c
@@ -89,11 +89,12 @@
#define SEQ_SCROLLER_TEXT_OFFSET 8
#define MUTE_ALPHA 120
-/* Note, Don't use SEQ_BEGIN/SEQ_END while drawing!
+/* Note, Don't use SEQ_ALL_BEGIN/SEQ_ALL_END while drawing!
* it messes up transform. */
-#undef SEQ_BEGIN
-#undef SEQP_BEGIN
-#undef SEQ_END
+#undef SEQ_ALL_BEGIN
+#undef SEQ_ALL_END
+#undef SEQ_CURRENT_BEGIN
+#undef SEQ_CURRENT_END
static Sequence *special_seq_update = NULL;
@@ -357,7 +358,7 @@ static void draw_seq_waveform(View2D *v2d,
static void drawmeta_contents(Scene *scene, Sequence *seqm, float x1, float y1, float x2, float y2)
{
- /* Don't use SEQ_BEGIN/SEQ_END here,
+ /* Don't use SEQ_ALL_BEGIN/SEQ_ALL_END here,
* because it changes seq->depth, which is needed for transform. */
Sequence *seq;
uchar col[4];
@@ -1528,11 +1529,7 @@ static void sequencer_stop_running_jobs(const bContext *C, Scene *scene)
static void sequencer_preview_clear(void)
{
- float col[3];
-
- UI_GetThemeColor3fv(TH_SEQ_PREVIEW, col);
- GPU_clear_color(col[0], col[1], col[2], 1.0f);
- GPU_clear(GPU_COLOR_BIT);
+ UI_ThemeClearColor(TH_SEQ_PREVIEW);
}
static void sequencer_preview_get_rect(rctf *preview,
@@ -1772,7 +1769,7 @@ void sequencer_draw_preview(const bContext *C,
GPUFrameBuffer *framebuffer_overlay = GPU_viewport_framebuffer_overlay_get(viewport);
GPU_framebuffer_bind_no_srgb(framebuffer_overlay);
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
if (sseq->render_size == SEQ_PROXY_RENDER_SIZE_NONE) {
sequencer_preview_clear();
@@ -2265,7 +2262,6 @@ void draw_timeline_seq(const bContext *C, ARegion *region)
else {
GPU_clear_color(col[0], col[1], col[2], 0.0f);
}
- GPU_clear(GPU_COLOR_BIT);
UI_view2d_view_ortho(v2d);
/* Get timeline boundbox, needed for the scrollers. */
diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c
index 78ca2832c55..f175c2a7419 100644
--- a/source/blender/editors/space_sequencer/sequencer_edit.c
+++ b/source/blender/editors/space_sequencer/sequencer_edit.c
@@ -58,6 +58,7 @@
#include "ED_sequencer.h"
#include "UI_interface.h"
+#include "UI_resources.h"
#include "UI_view2d.h"
#include "DEG_depsgraph.h"
@@ -216,7 +217,7 @@ static void seq_proxy_build_job(const bContext *C, ReportList *reports)
file_list = BLI_gset_new(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, "file list");
bool selected = false; /* Check for no selected strips */
- SEQP_BEGIN (ed, seq) {
+ SEQ_CURRENT_BEGIN (ed, seq) {
if (!ELEM(seq->type, SEQ_TYPE_MOVIE, SEQ_TYPE_IMAGE, SEQ_TYPE_META) ||
(seq->flag & SELECT) == 0) {
continue;
@@ -239,7 +240,7 @@ static void seq_proxy_build_job(const bContext *C, ReportList *reports)
BKE_reportf(reports, RPT_WARNING, "Overwrite is not checked for %s, skipping", seq->name);
}
}
- SEQ_END;
+ SEQ_CURRENT_END;
if (!selected) {
BKE_reportf(reports, RPT_WARNING, "Select movie or image strips");
@@ -482,10 +483,10 @@ void ED_sequencer_deselect_all(Scene *scene)
return;
}
- SEQP_BEGIN (ed, seq) {
+ SEQ_CURRENT_BEGIN (ed, seq) {
seq->flag &= ~SEQ_ALLSEL;
}
- SEQ_END;
+ SEQ_CURRENT_END;
}
void recurs_sel_seq(Sequence *seqm)
@@ -1030,7 +1031,7 @@ static void set_filter_seq(Scene *scene)
return;
}
- SEQP_BEGIN (ed, seq) {
+ SEQ_CURRENT_BEGIN (ed, seq) {
if (seq->flag & SELECT) {
if (seq->type == SEQ_TYPE_MOVIE) {
seq->flag |= SEQ_FILTERY;
@@ -1039,7 +1040,7 @@ static void set_filter_seq(Scene *scene)
}
}
}
- SEQ_END;
+ SEQ_CURRENT_END;
}
#endif
@@ -1065,7 +1066,7 @@ static void UNUSED_FUNCTION(seq_remap_paths)(Scene *scene)
return;
}
- SEQP_BEGIN (ed, seq) {
+ SEQ_CURRENT_BEGIN (ed, seq) {
if (seq->flag & SELECT) {
if (STREQLEN(seq->strip->dir, from, strlen(from))) {
printf("found %s\n", seq->strip->dir);
@@ -1080,7 +1081,7 @@ static void UNUSED_FUNCTION(seq_remap_paths)(Scene *scene)
}
}
}
- SEQ_END;
+ SEQ_CURRENT_END;
}
/** \} */
@@ -1308,7 +1309,7 @@ static int sequencer_snap_exec(bContext *C, wmOperator *op)
}
/* Test for effects and overlap.
- * Don't use SEQP_BEGIN since that would be recursive. */
+ * Don't use SEQ_CURRENT_BEGIN since that would be recursive. */
for (seq = ed->seqbasep->first; seq; seq = seq->next) {
if (seq->flag & SELECT && !(seq->depth == 0 && seq->flag & SEQ_LOCK)) {
seq->flag &= ~SEQ_OVERLAP;
@@ -2294,25 +2295,25 @@ static int sequencer_split_exec(bContext *C, wmOperator *op)
Sequence *seq;
if (ignore_selection) {
if (use_cursor_position) {
- SEQP_BEGIN (ed, seq) {
+ SEQ_CURRENT_BEGIN (ed, seq) {
if (seq->enddisp == split_frame && seq->machine == split_channel) {
seq_selected = seq->flag & SEQ_ALLSEL;
}
}
- SEQ_END;
+ SEQ_CURRENT_END;
if (!seq_selected) {
- SEQP_BEGIN (ed, seq) {
+ SEQ_CURRENT_BEGIN (ed, seq) {
if (seq->startdisp == split_frame && seq->machine == split_channel) {
seq->flag &= ~SEQ_ALLSEL;
}
}
- SEQ_END;
+ SEQ_CURRENT_END;
}
}
}
else {
if (split_side != SEQ_SIDE_BOTH) {
- SEQP_BEGIN (ed, seq) {
+ SEQ_CURRENT_BEGIN (ed, seq) {
if (split_side == SEQ_SIDE_LEFT) {
if (seq->startdisp >= split_frame) {
seq->flag &= ~SEQ_ALLSEL;
@@ -2324,15 +2325,15 @@ static int sequencer_split_exec(bContext *C, wmOperator *op)
}
}
}
- SEQ_END;
+ SEQ_CURRENT_END;
}
}
- SEQP_BEGIN (ed, seq) {
+ SEQ_CURRENT_BEGIN (ed, seq) {
if (seq->seq1 || seq->seq2 || seq->seq3) {
BKE_sequence_calc(scene, seq);
}
}
- SEQ_END;
+ SEQ_CURRENT_END;
BKE_sequencer_sort(scene);
}
@@ -2376,6 +2377,28 @@ static int sequencer_split_invoke(bContext *C, wmOperator *op, const wmEvent *ev
return sequencer_split_exec(C, op);
}
+static void sequencer_split_ui(bContext *UNUSED(C), wmOperator *op)
+{
+ uiLayout *layout = op->layout;
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+
+ PointerRNA ptr;
+ RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
+
+ uiLayout *row = uiLayoutRow(layout, false);
+ uiItemR(row, &ptr, "type", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "frame", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "side", 0, NULL, ICON_NONE);
+
+ uiItemS(layout);
+
+ uiItemR(layout, &ptr, "use_cursor_position", 0, NULL, ICON_NONE);
+ if (RNA_boolean_get(&ptr, "use_cursor_position")) {
+ uiItemR(layout, &ptr, "channel", 0, NULL, ICON_NONE);
+ }
+}
+
void SEQUENCER_OT_split(struct wmOperatorType *ot)
{
/* Identifiers. */
@@ -2387,6 +2410,7 @@ void SEQUENCER_OT_split(struct wmOperatorType *ot)
ot->invoke = sequencer_split_invoke;
ot->exec = sequencer_split_exec;
ot->poll = sequencer_edit_poll;
+ ot->ui = sequencer_split_ui;
/* Flags. */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -2521,12 +2545,12 @@ static int sequencer_delete_exec(bContext *C, wmOperator *UNUSED(op))
BKE_sequencer_prefetch_stop(scene);
- SEQP_BEGIN (scene->ed, seq) {
+ SEQ_CURRENT_BEGIN (scene->ed, seq) {
if (seq->flag & SELECT) {
BKE_sequencer_flag_for_removal(scene, ed->seqbasep, seq);
}
}
- SEQ_END;
+ SEQ_CURRENT_END;
BKE_sequencer_remove_flagged_sequences(scene, ed->seqbasep);
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
@@ -2952,7 +2976,7 @@ static int sequencer_meta_separate_exec(bContext *C, wmOperator *UNUSED(op))
recurs_del_seq_flag(scene, ed->seqbasep, SEQ_FLAG_DELETE, 0);
/* Test for effects and overlap
- * don't use SEQP_BEGIN since that would be recursive. */
+ * don't use SEQ_CURRENT_BEGIN since that would be recursive. */
for (seq = ed->seqbasep->first; seq; seq = seq->next) {
if (seq->flag & SELECT) {
seq->flag &= ~SEQ_OVERLAP;
@@ -3476,7 +3500,7 @@ static int sequencer_rebuild_proxy_exec(bContext *C, wmOperator *UNUSED(op))
file_list = BLI_gset_new(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, "file list");
- SEQP_BEGIN (ed, seq) {
+ SEQ_CURRENT_BEGIN (ed, seq) {
if ((seq->flag & SELECT)) {
ListBase queue = {NULL, NULL};
LinkData *link;
@@ -3493,7 +3517,7 @@ static int sequencer_rebuild_proxy_exec(bContext *C, wmOperator *UNUSED(op))
BKE_sequencer_free_imbuf(scene, &ed->seqbase, false);
}
}
- SEQ_END;
+ SEQ_CURRENT_END;
BLI_gset_free(file_list, MEM_freeN);
@@ -3544,7 +3568,7 @@ static int sequencer_enable_proxies_exec(bContext *C, wmOperator *op)
turnon = false;
}
- SEQP_BEGIN (ed, seq) {
+ SEQ_CURRENT_BEGIN (ed, seq) {
if ((seq->flag & SELECT)) {
if (ELEM(seq->type, SEQ_TYPE_MOVIE, SEQ_TYPE_IMAGE, SEQ_TYPE_META)) {
BKE_sequencer_proxy_set(seq, turnon);
@@ -3589,7 +3613,7 @@ static int sequencer_enable_proxies_exec(bContext *C, wmOperator *op)
}
}
}
- SEQ_END;
+ SEQ_CURRENT_END;
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
@@ -3966,12 +3990,12 @@ static int sequencer_export_subtitles_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- SEQ_BEGIN (ed, seq) {
+ SEQ_ALL_BEGIN (ed, seq) {
if (seq->type == SEQ_TYPE_TEXT) {
BLI_addtail(&text_seq, MEM_dupallocN(seq));
}
}
- SEQ_END;
+ SEQ_ALL_END;
if (BLI_listbase_is_empty(&text_seq)) {
BKE_report(op->reports, RPT_ERROR, "No subtitles (text strips) to export");
diff --git a/source/blender/editors/space_sequencer/sequencer_modifier.c b/source/blender/editors/space_sequencer/sequencer_modifier.c
index e0f7179c3f9..50f2a5084ef 100644
--- a/source/blender/editors/space_sequencer/sequencer_modifier.c
+++ b/source/blender/editors/space_sequencer/sequencer_modifier.c
@@ -232,7 +232,7 @@ static int strip_modifier_copy_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- SEQP_BEGIN (ed, seq_iter) {
+ SEQ_CURRENT_BEGIN (ed, seq_iter) {
if (seq_iter->flag & SELECT) {
if (seq_iter == seq) {
continue;
@@ -254,7 +254,7 @@ static int strip_modifier_copy_exec(bContext *C, wmOperator *op)
BKE_sequence_modifier_list_copy(seq_iter, seq);
}
}
- SEQ_END;
+ SEQ_CURRENT_END;
BKE_sequence_invalidate_cache_preprocessed(scene, seq);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c
index 85b70354ab3..955b4dba5e8 100644
--- a/source/blender/editors/space_sequencer/sequencer_select.c
+++ b/source/blender/editors/space_sequencer/sequencer_select.c
@@ -415,14 +415,14 @@ static int sequencer_select_exec(bContext *C, wmOperator *op)
const float x = UI_view2d_region_to_view_x(v2d, mval[0]);
- SEQP_BEGIN (ed, seq) {
+ SEQ_CURRENT_BEGIN (ed, seq) {
if (((x < CFRA) && (seq->enddisp <= CFRA)) || ((x >= CFRA) && (seq->startdisp >= CFRA))) {
/* Select left or right. */
seq->flag |= SELECT;
recurs_sel_seq(seq);
}
}
- SEQ_END;
+ SEQ_CURRENT_END;
{
SpaceSeq *sseq = CTX_wm_space_seq(C);
@@ -975,7 +975,7 @@ static int sequencer_select_side_of_frame_exec(bContext *C, wmOperator *op)
ED_sequencer_deselect_all(scene);
}
const int cfra = CFRA;
- SEQP_BEGIN (ed, seq) {
+ SEQ_CURRENT_BEGIN (ed, seq) {
bool test = false;
switch (side) {
case -1:
@@ -994,7 +994,7 @@ static int sequencer_select_side_of_frame_exec(bContext *C, wmOperator *op)
recurs_sel_seq(seq);
}
}
- SEQ_END;
+ SEQ_CURRENT_END;
ED_outliner_select_sync_from_sequence_tag(C);
@@ -1282,13 +1282,13 @@ static bool select_grouped_type(Editing *ed, Sequence *actseq, const int channel
Sequence *seq;
bool changed = false;
- SEQP_BEGIN (ed, seq) {
+ SEQ_CURRENT_BEGIN (ed, seq) {
if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == actseq->type) {
seq->flag |= SELECT;
changed = true;
}
}
- SEQ_END;
+ SEQ_CURRENT_END;
return changed;
}
@@ -1299,13 +1299,13 @@ static bool select_grouped_type_basic(Editing *ed, Sequence *actseq, const int c
bool changed = false;
const bool is_sound = SEQ_IS_SOUND(actseq);
- SEQP_BEGIN (ed, seq) {
+ SEQ_CURRENT_BEGIN (ed, seq) {
if (SEQ_CHANNEL_CHECK(seq, channel) && (is_sound ? SEQ_IS_SOUND(seq) : !SEQ_IS_SOUND(seq))) {
seq->flag |= SELECT;
changed = true;
}
}
- SEQ_END;
+ SEQ_CURRENT_END;
return changed;
}
@@ -1316,14 +1316,14 @@ static bool select_grouped_type_effect(Editing *ed, Sequence *actseq, const int
bool changed = false;
const bool is_effect = SEQ_IS_EFFECT(actseq);
- SEQP_BEGIN (ed, seq) {
+ SEQ_CURRENT_BEGIN (ed, seq) {
if (SEQ_CHANNEL_CHECK(seq, channel) &&
(is_effect ? SEQ_IS_EFFECT(seq) : !SEQ_IS_EFFECT(seq))) {
seq->flag |= SELECT;
changed = true;
}
}
- SEQ_END;
+ SEQ_CURRENT_END;
return changed;
}
@@ -1339,45 +1339,45 @@ static bool select_grouped_data(Editing *ed, Sequence *actseq, const int channel
}
if (SEQ_HAS_PATH(actseq) && dir) {
- SEQP_BEGIN (ed, seq) {
+ SEQ_CURRENT_BEGIN (ed, seq) {
if (SEQ_CHANNEL_CHECK(seq, channel) && SEQ_HAS_PATH(seq) && seq->strip &&
STREQ(seq->strip->dir, dir)) {
seq->flag |= SELECT;
changed = true;
}
}
- SEQ_END;
+ SEQ_CURRENT_END;
}
else if (actseq->type == SEQ_TYPE_SCENE) {
Scene *sce = actseq->scene;
- SEQP_BEGIN (ed, seq) {
+ SEQ_CURRENT_BEGIN (ed, seq) {
if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == SEQ_TYPE_SCENE && seq->scene == sce) {
seq->flag |= SELECT;
changed = true;
}
}
- SEQ_END;
+ SEQ_CURRENT_END;
}
else if (actseq->type == SEQ_TYPE_MOVIECLIP) {
MovieClip *clip = actseq->clip;
- SEQP_BEGIN (ed, seq) {
+ SEQ_CURRENT_BEGIN (ed, seq) {
if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == SEQ_TYPE_MOVIECLIP &&
seq->clip == clip) {
seq->flag |= SELECT;
changed = true;
}
}
- SEQ_END;
+ SEQ_CURRENT_END;
}
else if (actseq->type == SEQ_TYPE_MASK) {
struct Mask *mask = actseq->mask;
- SEQP_BEGIN (ed, seq) {
+ SEQ_CURRENT_BEGIN (ed, seq) {
if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == SEQ_TYPE_MASK && seq->mask == mask) {
seq->flag |= SELECT;
changed = true;
}
}
- SEQ_END;
+ SEQ_CURRENT_END;
}
return changed;
@@ -1394,15 +1394,15 @@ static bool select_grouped_effect(Editing *ed, Sequence *actseq, const int chann
effects[i] = false;
}
- SEQP_BEGIN (ed, seq) {
+ SEQ_CURRENT_BEGIN (ed, seq) {
if (SEQ_CHANNEL_CHECK(seq, channel) && (seq->type & SEQ_TYPE_EFFECT) &&
ELEM(actseq, seq->seq1, seq->seq2, seq->seq3)) {
effects[seq->type] = true;
}
}
- SEQ_END;
+ SEQ_CURRENT_END;
- SEQP_BEGIN (ed, seq) {
+ SEQ_CURRENT_BEGIN (ed, seq) {
if (SEQ_CHANNEL_CHECK(seq, channel) && effects[seq->type]) {
if (seq->seq1) {
seq->seq1->flag |= SELECT;
@@ -1416,7 +1416,7 @@ static bool select_grouped_effect(Editing *ed, Sequence *actseq, const int chann
changed = true;
}
}
- SEQ_END;
+ SEQ_CURRENT_END;
return changed;
}
@@ -1426,13 +1426,13 @@ static bool select_grouped_time_overlap(Editing *ed, Sequence *actseq)
Sequence *seq;
bool changed = false;
- SEQP_BEGIN (ed, seq) {
+ SEQ_CURRENT_BEGIN (ed, seq) {
if (seq->startdisp < actseq->enddisp && seq->enddisp > actseq->startdisp) {
seq->flag |= SELECT;
changed = true;
}
}
- SEQ_END;
+ SEQ_CURRENT_END;
return changed;
}
@@ -1447,10 +1447,10 @@ static bool select_grouped_effect_link(Editing *ed, Sequence *actseq, const int
int machine = actseq->machine;
SeqIterator iter;
- SEQP_BEGIN (ed, seq) {
+ SEQ_CURRENT_BEGIN (ed, seq) {
seq->tmp = NULL;
}
- SEQ_END;
+ SEQ_CURRENT_END;
actseq->tmp = POINTER_FROM_INT(true);
@@ -1523,11 +1523,11 @@ static int sequencer_select_grouped_exec(bContext *C, wmOperator *op)
bool changed = false;
if (!extend) {
- SEQP_BEGIN (ed, seq) {
+ SEQ_CURRENT_BEGIN (ed, seq) {
seq->flag &= ~SELECT;
changed = true;
}
- SEQ_END;
+ SEQ_CURRENT_END;
}
switch (type) {
diff --git a/source/blender/editors/space_text/space_text.c b/source/blender/editors/space_text/space_text.c
index f6d00ec94bf..300f63761c0 100644
--- a/source/blender/editors/space_text/space_text.c
+++ b/source/blender/editors/space_text/space_text.c
@@ -296,7 +296,6 @@ static void text_main_region_draw(const bContext *C, ARegion *region)
/* clear and setup matrix */
UI_ThemeClearColor(TH_BACK);
- GPU_clear(GPU_COLOR_BIT);
// UI_view2d_view_ortho(v2d);
diff --git a/source/blender/editors/space_text/text_draw.c b/source/blender/editors/space_text/text_draw.c
index f1b1d6760f3..ec5175eae51 100644
--- a/source/blender/editors/space_text/text_draw.c
+++ b/source/blender/editors/space_text/text_draw.c
@@ -1660,25 +1660,16 @@ void draw_text_main(SpaceText *st, ARegion *region)
}
if (st->showlinenrs && !wrap_skip) {
- /* draw line number */
- if (tmp == text->curl) {
- UI_FontThemeColor(tdc.font_id, TH_HILITE);
- }
- else {
- UI_FontThemeColor(tdc.font_id, TH_LINENUMBERS);
- }
-
+ /* Draw line number. */
+ UI_FontThemeColor(tdc.font_id, (tmp == text->curl) ? TH_HILITE : TH_LINENUMBERS);
BLI_snprintf(linenr,
sizeof(linenr),
"%*d",
st->runtime.line_number_display_digits,
i + linecount + 1);
- /* itoa(i + linecount + 1, linenr, 10); */ /* not ansi-c :/ */
text_font_draw(&tdc, TXT_NUMCOL_PAD * st->runtime.cwidth_px, y, linenr);
-
- if (tmp == text->curl) {
- UI_FontThemeColor(tdc.font_id, TH_TEXT);
- }
+ /* Change back to text color. */
+ UI_FontThemeColor(tdc.font_id, TH_TEXT);
}
if (st->wordwrap) {
diff --git a/source/blender/editors/space_userpref/space_userpref.c b/source/blender/editors/space_userpref/space_userpref.c
index 0242bb4fe24..3efdee9cec9 100644
--- a/source/blender/editors/space_userpref/space_userpref.c
+++ b/source/blender/editors/space_userpref/space_userpref.c
@@ -141,8 +141,7 @@ static void userpref_main_region_layout(const bContext *C, ARegion *region)
BLI_str_tolower_ascii(id_lower, strlen(id_lower));
}
- ED_region_panels_layout_ex(
- C, region, &region->type->paneltypes, contexts, U.space_data.section_active, true, NULL);
+ ED_region_panels_layout_ex(C, region, &region->type->paneltypes, contexts, NULL);
}
static void userpref_operatortypes(void)
diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c
index e5ba27cef07..de0b420a3b5 100644
--- a/source/blender/editors/space_view3d/space_view3d.c
+++ b/source/blender/editors/space_view3d/space_view3d.c
@@ -1323,9 +1323,7 @@ void ED_view3d_buttons_region_layout_ex(const bContext *C,
paneltypes = &art->paneltypes;
}
- const bool vertical = true;
- ED_region_panels_layout_ex(
- C, region, paneltypes, contexts_base, -1, vertical, category_override);
+ ED_region_panels_layout_ex(C, region, paneltypes, contexts_base, category_override);
}
static void view3d_buttons_region_layout(const bContext *C, ARegion *region)
@@ -1453,7 +1451,7 @@ static void view3d_tools_region_init(wmWindowManager *wm, ARegion *region)
static void view3d_tools_region_draw(const bContext *C, ARegion *region)
{
- ED_region_panels_ex(C, region, (const char *[]){CTX_data_mode_string(C), NULL}, -1, true);
+ ED_region_panels_ex(C, region, (const char *[]){CTX_data_mode_string(C), NULL});
}
/* area (not region) level listener */
diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c
index 33b365b45aa..195199c45cd 100644
--- a/source/blender/editors/space_view3d/view3d_draw.c
+++ b/source/blender/editors/space_view3d/view3d_draw.c
@@ -1618,7 +1618,7 @@ void view3d_main_region_draw(const bContext *C, ARegion *region)
GPU_pass_cache_garbage_collect();
/* No depth test for drawing action zones afterwards. */
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
v3d->flag |= V3D_INVALID_BACKBUF;
}
@@ -2319,14 +2319,14 @@ void ED_view3d_draw_depth_gpencil(Depsgraph *depsgraph, Scene *scene, ARegion *r
/* Setup view matrix. */
ED_view3d_draw_setup_view(NULL, NULL, depsgraph, scene, region, v3d, NULL, NULL, NULL);
- GPU_clear(GPU_DEPTH_BIT);
+ GPU_clear_depth(1.0f);
- GPU_depth_test(true);
+ GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
GPUViewport *viewport = WM_draw_region_get_viewport(region);
DRW_draw_depth_loop_gpencil(depsgraph, region, v3d, viewport);
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
}
/* *********************** customdata **************** */
diff --git a/source/blender/editors/space_view3d/view3d_placement.c b/source/blender/editors/space_view3d/view3d_placement.c
index 6c61c83731d..6c2f4df7004 100644
--- a/source/blender/editors/space_view3d/view3d_placement.c
+++ b/source/blender/editors/space_view3d/view3d_placement.c
@@ -586,23 +586,23 @@ static void draw_primitive_view(const struct bContext *C, ARegion *UNUSED(region
UI_GetThemeColor3fv(TH_GIZMO_PRIMARY, color);
const bool use_depth = !XRAY_ENABLED(ipd->v3d);
- const bool depth_test_enabled = GPU_depth_test_enabled();
+ const eGPUDepthTest depth_test_enabled = GPU_depth_test_get();
if (use_depth) {
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
color[3] = 0.15f;
draw_primitive_view_impl(C, ipd, color);
}
if (use_depth) {
- GPU_depth_test(true);
+ GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
}
color[3] = 1.0f;
draw_primitive_view_impl(C, ipd, color);
if (use_depth) {
if (depth_test_enabled == false) {
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
}
}
}
diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c
index 9490c807989..f99301371d4 100644
--- a/source/blender/editors/space_view3d/view3d_select.c
+++ b/source/blender/editors/space_view3d/view3d_select.c
@@ -970,7 +970,7 @@ static bool do_lasso_select_curve(ViewContext *vc,
/* Deselect items that were not added to selection (indicated by temp flag). */
if (deselect_all) {
- BKE_nurbList_flag_set_from_flag(nurbs, BEZT_FLAG_TEMP_TAG, SELECT);
+ data.is_changed |= BKE_nurbList_flag_set_from_flag(nurbs, BEZT_FLAG_TEMP_TAG, SELECT);
}
if (data.is_changed) {
@@ -2772,7 +2772,7 @@ static bool do_nurbs_box_select(ViewContext *vc, rcti *rect, const eSelectOp sel
/* Deselect items that were not added to selection (indicated by temp flag). */
if (deselect_all) {
- BKE_nurbList_flag_set_from_flag(nurbs, BEZT_FLAG_TEMP_TAG, SELECT);
+ data.is_changed |= BKE_nurbList_flag_set_from_flag(nurbs, BEZT_FLAG_TEMP_TAG, SELECT);
}
BKE_curve_nurb_vert_active_validate(vc->obedit->data);
@@ -3693,7 +3693,6 @@ static bool nurbscurve_circle_select(ViewContext *vc,
const bool select = (sel_op != SEL_OP_SUB);
const bool deselect_all = (sel_op == SEL_OP_SET);
CircleSelectUserData data;
- bool changed = false;
view3d_userdata_circleselect_init(&data, vc, select, mval, rad);
@@ -3711,12 +3710,12 @@ static bool nurbscurve_circle_select(ViewContext *vc,
/* Deselect items that were not added to selection (indicated by temp flag). */
if (deselect_all) {
- BKE_nurbList_flag_set_from_flag(nurbs, BEZT_FLAG_TEMP_TAG, SELECT);
+ data.is_changed |= BKE_nurbList_flag_set_from_flag(nurbs, BEZT_FLAG_TEMP_TAG, SELECT);
}
BKE_curve_nurb_vert_active_validate(vc->obedit->data);
- return changed || data.is_changed;
+ return data.is_changed;
}
static void latticecurve_circle_doSelect(void *userData, BPoint *bp, const float screen_co[2])
diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c
index b986ebb75b6..d015b5dcc89 100644
--- a/source/blender/editors/space_view3d/view3d_view.c
+++ b/source/blender/editors/space_view3d/view3d_view.c
@@ -1100,7 +1100,7 @@ int view3d_opengl_select(ViewContext *vc,
wm, vc->win, depsgraph, scene, region, v3d, vc->rv3d->viewmat, NULL, &rect);
if (!XRAY_ACTIVE(v3d)) {
- GPU_depth_test(true);
+ GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
}
/* If in xray mode, we select the wires in priority. */
@@ -1165,7 +1165,7 @@ int view3d_opengl_select(ViewContext *vc,
wm, vc->win, depsgraph, scene, region, v3d, vc->rv3d->viewmat, NULL, NULL);
if (!XRAY_ACTIVE(v3d)) {
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
}
DRW_opengl_context_disable();
diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c
index 0aa6b4f6131..4e5eaf4bf51 100644
--- a/source/blender/editors/transform/transform_constraints.c
+++ b/source/blender/editors/transform/transform_constraints.c
@@ -785,7 +785,6 @@ void drawConstraint(TransInfo *t)
else {
if (tc->mode & CON_SELECT) {
float vec[3];
- int depth_test_enabled;
convertViewVec(t, vec, (t->mval[0] - t->con.imval[0]), (t->mval[1] - t->con.imval[1]));
add_v3_v3(vec, t->center_global);
@@ -794,9 +793,9 @@ void drawConstraint(TransInfo *t)
drawLine(t, t->center_global, t->spacemtx[1], 'Y', 0);
drawLine(t, t->center_global, t->spacemtx[2], 'Z', 0);
- depth_test_enabled = GPU_depth_test_enabled();
+ eGPUDepthTest depth_test_enabled = GPU_depth_test_get();
if (depth_test_enabled) {
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
}
const uint shdr_pos = GPU_vertformat_attr_add(
@@ -821,7 +820,7 @@ void drawConstraint(TransInfo *t)
immUnbindProgram();
if (depth_test_enabled) {
- GPU_depth_test(true);
+ GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
}
}
@@ -843,7 +842,6 @@ void drawPropCircle(const struct bContext *C, TransInfo *t)
if (t->flag & T_PROP_EDIT) {
RegionView3D *rv3d = CTX_wm_region_view3d(C);
float tmat[4][4], imat[4][4];
- int depth_test_enabled;
if (t->spacetype == SPACE_VIEW3D && rv3d != NULL) {
copy_m4_m4(tmat, rv3d->viewmat);
@@ -873,9 +871,9 @@ void drawPropCircle(const struct bContext *C, TransInfo *t)
GPU_matrix_scale_2f(1.0f, (ysize / xsize) * (xmask / ymask));
}
- depth_test_enabled = GPU_depth_test_enabled();
+ eGPUDepthTest depth_test_enabled = GPU_depth_test_get();
if (depth_test_enabled) {
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
}
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
@@ -899,7 +897,7 @@ void drawPropCircle(const struct bContext *C, TransInfo *t)
immUnbindProgram();
if (depth_test_enabled) {
- GPU_depth_test(true);
+ GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
}
GPU_matrix_pop();
diff --git a/source/blender/editors/transform/transform_convert_object.c b/source/blender/editors/transform/transform_convert_object.c
index 61af4ebbe46..4bf0f842f2f 100644
--- a/source/blender/editors/transform/transform_convert_object.c
+++ b/source/blender/editors/transform/transform_convert_object.c
@@ -346,7 +346,7 @@ static void set_trans_object_base_flags(TransInfo *t)
ViewLayer *view_layer = t->view_layer;
View3D *v3d = t->view;
Scene *scene = t->scene;
- Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, true);
+ Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer);
/* NOTE: if Base selected and has parent selected:
* base->flag_legacy = BA_WAS_SEL
*/
@@ -421,7 +421,7 @@ static int count_proportional_objects(TransInfo *t)
View3D *v3d = t->view;
struct Main *bmain = CTX_data_main(t->context);
Scene *scene = t->scene;
- Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, true);
+ Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer);
/* Clear all flags we need. It will be used to detect dependencies. */
trans_object_base_deps_flag_prepare(view_layer);
/* Rotations around local centers are allowed to propagate, so we take all objects. */
diff --git a/source/blender/editors/transform/transform_gizmo_3d.c b/source/blender/editors/transform/transform_gizmo_3d.c
index dffee72205b..14ef5e87534 100644
--- a/source/blender/editors/transform/transform_gizmo_3d.c
+++ b/source/blender/editors/transform/transform_gizmo_3d.c
@@ -1343,7 +1343,7 @@ void drawDial3d(const TransInfo *t)
BLI_assert(axis_idx >= MAN_AXIS_RANGE_ROT_START && axis_idx < MAN_AXIS_RANGE_ROT_END);
gizmo_get_axis_color(axis_idx, NULL, color, color);
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
GPU_blend(GPU_BLEND_ALPHA);
GPU_line_smooth(true);
@@ -1359,7 +1359,7 @@ void drawDial3d(const TransInfo *t)
});
GPU_line_smooth(false);
- GPU_depth_test(true);
+ GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
GPU_blend(GPU_BLEND_NONE);
}
}
diff --git a/source/blender/editors/transform/transform_mode_edge_slide.c b/source/blender/editors/transform/transform_mode_edge_slide.c
index 45debe964f4..fe97a9fba87 100644
--- a/source/blender/editors/transform/transform_mode_edge_slide.c
+++ b/source/blender/editors/transform/transform_mode_edge_slide.c
@@ -1147,7 +1147,7 @@ void drawEdgeSlide(TransInfo *t)
const float line_size = UI_GetThemeValuef(TH_OUTLINE_WIDTH) + 0.5f;
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
GPU_blend(GPU_BLEND_ALPHA);
@@ -1266,7 +1266,7 @@ void drawEdgeSlide(TransInfo *t)
GPU_blend(GPU_BLEND_NONE);
- GPU_depth_test(true);
+ GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
}
static void edge_slide_snap_apply(TransInfo *t, float *value)
diff --git a/source/blender/editors/transform/transform_mode_vert_slide.c b/source/blender/editors/transform/transform_mode_vert_slide.c
index 11d0b375e6f..4367dd5ee92 100644
--- a/source/blender/editors/transform/transform_mode_vert_slide.c
+++ b/source/blender/editors/transform/transform_mode_vert_slide.c
@@ -390,7 +390,7 @@ void drawVertSlide(TransInfo *t)
const int alpha_shade = -160;
int i;
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
GPU_blend(GPU_BLEND_ALPHA);
@@ -485,7 +485,7 @@ void drawVertSlide(TransInfo *t)
GPU_matrix_pop();
- GPU_depth_test(true);
+ GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
}
}
}
diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c
index 09b5df82c2b..5db41570e00 100644
--- a/source/blender/editors/transform/transform_snap.c
+++ b/source/blender/editors/transform/transform_snap.c
@@ -184,7 +184,7 @@ void drawSnapping(const struct bContext *C, TransInfo *t)
const float *loc_prev = NULL;
const float *normal = NULL;
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
RegionView3D *rv3d = CTX_wm_region_view3d(C);
if (!BLI_listbase_is_empty(&t->tsnap.points)) {
@@ -228,7 +228,7 @@ void drawSnapping(const struct bContext *C, TransInfo *t)
ED_gizmotypes_snap_3d_draw_util(
rv3d, loc_prev, loc_cur, normal, col, activeCol, t->tsnap.snapElem);
- GPU_depth_test(true);
+ GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
}
}
else if (t->spacetype == SPACE_IMAGE) {
diff --git a/source/blender/editors/uvedit/uvedit_draw.c b/source/blender/editors/uvedit/uvedit_draw.c
index faeefcb989e..044fca2310c 100644
--- a/source/blender/editors/uvedit/uvedit_draw.c
+++ b/source/blender/editors/uvedit/uvedit_draw.c
@@ -451,13 +451,13 @@ static void draw_uvs(SpaceImage *sima,
GPU_batch_program_set_builtin(batch->edges, shader);
/* Inner Line. Use depth test to insure selection is drawn on top. */
- GPU_depth_test(true);
+ GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
GPU_line_width(1.0f);
GPU_batch_uniform_4fv(batch->edges, "edgeColor", col1);
GPU_batch_uniform_4fv(batch->edges, "selectColor", col2);
GPU_batch_uniform_1f(batch->edges, "dashWidth", dash_width);
GPU_batch_draw(batch->edges);
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
GPU_provoking_vertex(GPU_VERTEX_LAST);
}
@@ -486,7 +486,12 @@ static void draw_uvs(SpaceImage *sima,
GPU_batch_uniform_4fv(batch->verts, "pinnedColor", pinned_col);
GPU_batch_uniform_1f(batch->verts, "pointSize", (point_size + 1.5f) * M_SQRT2);
GPU_batch_uniform_1f(batch->verts, "outlineWidth", 0.75f);
- GPU_batch_draw(batch->verts);
+
+ /* #GPU_batch_draw_advanced is needed as unbinding the shader and redrawing
+ * causes the vertices not to draw at the right size. */
+ GPU_shader_bind(batch->verts->shader);
+
+ GPU_batch_draw_advanced(batch->verts, 0, 0, 0, 0);
/* We have problem in this mode when face order make some verts
* appear unselected because an adjacent face is not selected and
@@ -495,7 +500,11 @@ static void draw_uvs(SpaceImage *sima,
* on top. A bit overkill but it's simple. */
GPU_batch_uniform_4fv(batch->verts, "vertColor", transparent);
GPU_batch_uniform_4fv(batch->verts, "selectColor", col2);
- GPU_batch_draw(batch->verts);
+
+ GPU_batch_draw_advanced(batch->verts, 0, 0, 0, 0);
+
+ GPU_shader_unbind();
+ /* Finish #GPU_batch_draw_advanced drawing. */
GPU_blend(GPU_BLEND_NONE);
GPU_program_point_size(false);
@@ -552,8 +561,8 @@ void ED_uvedit_draw_main(SpaceImage *sima,
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
view_layer, ((View3D *)NULL), &objects_len);
if (objects_len > 0) {
+ GPU_depth_mask(true);
GPU_clear_depth(1.0f);
- GPU_clear(GPU_DEPTH_BIT);
}
/* go over all objects and create the batches + add their areas to the total */
diff --git a/source/blender/editors/uvedit/uvedit_intern.h b/source/blender/editors/uvedit/uvedit_intern.h
index 5a510aaf945..306f8a2c561 100644
--- a/source/blender/editors/uvedit/uvedit_intern.h
+++ b/source/blender/editors/uvedit/uvedit_intern.h
@@ -42,9 +42,6 @@ typedef struct UvNearestHit {
/** Always set if we have a hit. */
struct BMFace *efa;
struct BMLoop *l;
- struct MLoopUV *luv, *luv_next;
- /** Index of loop within face. */
- int lindex;
/** Needs to be set before calling nearest functions. */
float dist_sq;
} UvNearestHit;
diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c
index 149c5cf1f96..2ea78ca5377 100644
--- a/source/blender/editors/uvedit/uvedit_select.c
+++ b/source/blender/editors/uvedit/uvedit_select.c
@@ -71,10 +71,14 @@
#include "uvedit_intern.h"
static void uv_select_all_perform(Scene *scene, Object *obedit, int action);
+
+static void uv_select_all_perform_multi_ex(
+ Scene *scene, Object **objects, const uint objects_len, int action, const Object *ob_exclude);
static void uv_select_all_perform_multi(Scene *scene,
Object **objects,
const uint objects_len,
int action);
+
static void uv_select_flush_from_tag_face(SpaceImage *sima,
Scene *scene,
Object *obedit,
@@ -612,7 +616,7 @@ void uvedit_uv_select_disable(const Scene *scene,
}
}
-static BMLoop *uvedit_loop_find_other_radial_loop_with_visible_face(Scene *scene,
+static BMLoop *uvedit_loop_find_other_radial_loop_with_visible_face(const Scene *scene,
BMLoop *l_src,
const int cd_loop_uv_offset)
{
@@ -637,6 +641,37 @@ static BMLoop *uvedit_loop_find_other_radial_loop_with_visible_face(Scene *scene
return l_other;
}
+static BMLoop *uvedit_loop_find_other_boundary_loop_with_visible_face(const Scene *scene,
+ BMLoop *l_edge,
+ BMVert *v_pivot,
+ const int cd_loop_uv_offset)
+{
+ BLI_assert(uvedit_loop_find_other_radial_loop_with_visible_face(
+ scene, l_edge, cd_loop_uv_offset) == NULL);
+
+ BMLoop *l_step = l_edge;
+ l_step = (l_step->v == v_pivot) ? l_step->prev : l_step->next;
+ BMLoop *l_step_last = NULL;
+ do {
+ BLI_assert(BM_vert_in_edge(l_step->e, v_pivot));
+ l_step_last = l_step;
+ l_step = uvedit_loop_find_other_radial_loop_with_visible_face(
+ scene, l_step, cd_loop_uv_offset);
+ if (l_step) {
+ l_step = (l_step->v == v_pivot) ? l_step->prev : l_step->next;
+ }
+ } while (l_step != NULL);
+
+ BM_elem_flag_set(l_step_last->e, BM_ELEM_SMOOTH, false);
+
+ if (l_step_last != NULL) {
+ BLI_assert(uvedit_loop_find_other_radial_loop_with_visible_face(
+ scene, l_step_last, cd_loop_uv_offset) == NULL);
+ }
+
+ return l_step_last;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -671,9 +706,6 @@ bool uv_find_nearest_edge(Scene *scene, Object *obedit, const float co[2], UvNea
hit->efa = efa;
hit->l = l;
- hit->luv = luv;
- hit->luv_next = luv_next;
- hit->lindex = i;
hit->dist_sq = dist_test_sq;
found = true;
@@ -713,7 +745,6 @@ bool uv_find_nearest_face(Scene *scene, Object *obedit, const float co[2], UvNea
if (uv_find_nearest_edge(scene, obedit, co, &hit)) {
hit.dist_sq = dist_sq_init;
hit.l = NULL;
- hit.luv = hit.luv_next = NULL;
BMIter iter;
BMFace *efa;
@@ -783,7 +814,6 @@ bool uv_find_nearest_vert(Scene *scene,
hit.dist_sq = dist_sq_init;
hit.l = NULL;
- hit.luv = hit.luv_next = NULL;
BMEditMesh *em = BKE_editmesh_from_object(obedit);
BMFace *efa;
@@ -822,10 +852,7 @@ bool uv_find_nearest_vert(Scene *scene,
hit.dist_sq = dist_test_sq;
hit.l = l;
- hit.luv = luv;
- hit.luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
hit.efa = efa;
- hit.lindex = i;
found = true;
}
}
@@ -979,200 +1006,235 @@ BMLoop *uv_find_nearest_loop_from_edge(struct Scene *scene,
/** \name Edge Loop Select
* \{ */
-static void uv_select_edgeloop_vertex_loop_flag(UvMapVert *first)
-{
- UvMapVert *iterv;
- int count = 0;
+/** Mode for selecting edge loops at boundaries. */
+enum eUVEdgeLoopBoundaryMode {
+ /** Delimit at face corners (don't walk over multiple edges in the same face). */
+ UV_EDGE_LOOP_BOUNDARY_LOOP = 1,
+ /** Don't delimit, walk over the all connected boundary loops. */
+ UV_EDGE_LOOP_BOUNDARY_ALL = 2,
+};
- for (iterv = first; iterv; iterv = iterv->next) {
- if (iterv->separate && iterv != first) {
- break;
+static BMLoop *bm_select_edgeloop_double_side_next(const Scene *scene,
+ BMLoop *l_step,
+ BMVert *v_from,
+ const int cd_loop_uv_offset)
+{
+ if (l_step->f->len == 4) {
+ BMVert *v_from_next = BM_edge_other_vert(l_step->e, v_from);
+ BMLoop *l_step_over = (v_from == l_step->v) ? l_step->next : l_step->prev;
+ l_step_over = uvedit_loop_find_other_radial_loop_with_visible_face(
+ scene, l_step_over, cd_loop_uv_offset);
+ if (l_step_over) {
+ return (l_step_over->v == v_from_next) ? l_step_over->prev : l_step_over->next;
}
-
- count++;
- }
-
- if (count < 5) {
- first->flag = 1;
}
+ return NULL;
}
-static UvMapVert *uv_select_edgeloop_vertex_map_get(UvVertMap *vmap, BMFace *efa, BMLoop *l)
+static BMLoop *bm_select_edgeloop_single_side_next(const Scene *scene,
+ BMLoop *l_step,
+ BMVert *v_from,
+ const int cd_loop_uv_offset)
{
- UvMapVert *iterv, *first;
- first = BM_uv_vert_map_at_index(vmap, BM_elem_index_get(l->v));
+ BMVert *v_from_next = BM_edge_other_vert(l_step->e, v_from);
+ return uvedit_loop_find_other_boundary_loop_with_visible_face(
+ scene, l_step, v_from_next, cd_loop_uv_offset);
+}
- for (iterv = first; iterv; iterv = iterv->next) {
- if (iterv->separate) {
- first = iterv;
- }
- if (iterv->poly_index == BM_elem_index_get(efa)) {
- return first;
+/* TODO(campbell): support this in the BMesh API, as we have for clearing other types. */
+static void bm_loop_tags_clear(BMesh *bm)
+{
+ BMIter iter;
+ BMFace *f;
+ BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+ BMIter liter;
+ BMLoop *l_iter;
+ BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
+ BM_elem_flag_disable(l_iter, BM_ELEM_TAG);
}
}
-
- return NULL;
}
-static bool uv_select_edgeloop_edge_tag_faces(BMEditMesh *em,
- UvMapVert *first1,
- UvMapVert *first2,
- int *totface)
+/**
+ * Tag all loops which should be selected, the caller must select.
+ */
+static void uv_select_edgeloop_double_side_tag(const Scene *scene,
+ BMEditMesh *em,
+ BMLoop *l_init_pair[2],
+ const int cd_loop_uv_offset)
{
- UvMapVert *iterv1, *iterv2;
- BMFace *efa;
- int tot = 0;
-
- /* count number of faces this edge has */
- for (iterv1 = first1; iterv1; iterv1 = iterv1->next) {
- if (iterv1->separate && iterv1 != first1) {
- break;
- }
+ bm_loop_tags_clear(em->bm);
- for (iterv2 = first2; iterv2; iterv2 = iterv2->next) {
- if (iterv2->separate && iterv2 != first2) {
+ for (int side = 0; side < 2; side++) {
+ BMLoop *l_step_pair[2] = {l_init_pair[0], l_init_pair[1]};
+ BMVert *v_from = side ? l_step_pair[0]->e->v1 : l_step_pair[0]->e->v2;
+ /* Disable since we start from the same edge. */
+ BM_elem_flag_disable(l_step_pair[0], BM_ELEM_TAG);
+ BM_elem_flag_disable(l_step_pair[1], BM_ELEM_TAG);
+ while ((l_step_pair[0] != NULL) && (l_step_pair[1] != NULL)) {
+ if (!uvedit_face_visible_test(scene, l_step_pair[0]->f) ||
+ !uvedit_face_visible_test(scene, l_step_pair[1]->f) ||
+ /* Check loops have not diverged. */
+ (uvedit_loop_find_other_radial_loop_with_visible_face(
+ scene, l_step_pair[0], cd_loop_uv_offset) != l_step_pair[1])) {
break;
}
- if (iterv1->poly_index == iterv2->poly_index) {
- /* if face already tagged, don't do this edge */
- efa = BM_face_at_index(em->bm, iterv1->poly_index);
- if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
- return false;
- }
+ BLI_assert(l_step_pair[0]->e == l_step_pair[1]->e);
+
+ BM_elem_flag_enable(l_step_pair[0], BM_ELEM_TAG);
+ BM_elem_flag_enable(l_step_pair[1], BM_ELEM_TAG);
+
+ BMVert *v_from_next = BM_edge_other_vert(l_step_pair[0]->e, v_from);
+ /* Walk over both sides, ensure they keep on the same edge. */
+ for (int i = 0; i < ARRAY_SIZE(l_step_pair); i++) {
+ l_step_pair[i] = bm_select_edgeloop_double_side_next(
+ scene, l_step_pair[i], v_from, cd_loop_uv_offset);
+ }
- tot++;
+ if ((l_step_pair[0] && BM_elem_flag_test(l_step_pair[0], BM_ELEM_TAG)) ||
+ (l_step_pair[1] && BM_elem_flag_test(l_step_pair[1], BM_ELEM_TAG))) {
break;
}
+ v_from = v_from_next;
}
}
+}
- if (*totface == 0) { /* start edge */
- *totface = tot;
- }
- else if (tot != *totface) { /* check for same number of faces as start edge */
- return false;
+/**
+ * Tag all loops which should be selected, the caller must select.
+ *
+ * \param r_count_by_select: Count the number of unselected and selected loops,
+ * this is needed to implement cycling between #eUVEdgeLoopBoundaryMode.
+ */
+static void uv_select_edgeloop_single_side_tag(const Scene *scene,
+ BMEditMesh *em,
+ BMLoop *l_init,
+ const int cd_loop_uv_offset,
+ enum eUVEdgeLoopBoundaryMode boundary_mode,
+ int r_count_by_select[2])
+{
+ if (r_count_by_select) {
+ r_count_by_select[0] = r_count_by_select[1] = 0;
}
- /* tag the faces */
- for (iterv1 = first1; iterv1; iterv1 = iterv1->next) {
- if (iterv1->separate && iterv1 != first1) {
- break;
- }
+ bm_loop_tags_clear(em->bm);
- for (iterv2 = first2; iterv2; iterv2 = iterv2->next) {
- if (iterv2->separate && iterv2 != first2) {
+ for (int side = 0; side < 2; side++) {
+ BMLoop *l_step = l_init;
+ BMVert *v_from = side ? l_step->e->v1 : l_step->e->v2;
+ /* Disable since we start from the same edge. */
+ BM_elem_flag_disable(l_step, BM_ELEM_TAG);
+ while (l_step != NULL) {
+
+ if (!uvedit_face_visible_test(scene, l_step->f) ||
+ /* Check the boundary is still a boundary. */
+ (uvedit_loop_find_other_radial_loop_with_visible_face(
+ scene, l_step, cd_loop_uv_offset) != NULL)) {
break;
}
- if (iterv1->poly_index == iterv2->poly_index) {
- efa = BM_face_at_index(em->bm, iterv1->poly_index);
- BM_elem_flag_enable(efa, BM_ELEM_TAG);
+ if (r_count_by_select != NULL) {
+ r_count_by_select[uvedit_edge_select_test(scene, l_step, cd_loop_uv_offset)] += 1;
+ /* Early exit when mixed could be optional if needed. */
+ if (r_count_by_select[0] && r_count_by_select[1]) {
+ r_count_by_select[0] = r_count_by_select[1] = -1;
+ break;
+ }
+ }
+
+ BM_elem_flag_enable(l_step, BM_ELEM_TAG);
+
+ BMVert *v_from_next = BM_edge_other_vert(l_step->e, v_from);
+ BMFace *f_step_prev = l_step->f;
+
+ l_step = bm_select_edgeloop_single_side_next(scene, l_step, v_from, cd_loop_uv_offset);
+
+ if (l_step && BM_elem_flag_test(l_step, BM_ELEM_TAG)) {
break;
}
+ if (boundary_mode == UV_EDGE_LOOP_BOUNDARY_LOOP) {
+ /* Don't allow walking over the the face. */
+ if (f_step_prev == l_step->f) {
+ break;
+ }
+ }
+ v_from = v_from_next;
}
}
-
- return true;
}
-static int uv_select_edgeloop(Scene *scene, Object *obedit, UvNearestHit *hit, const bool extend)
+static int uv_select_edgeloop(
+ SpaceImage *sima, Scene *scene, Object *obedit, UvNearestHit *hit, const bool extend)
{
BMEditMesh *em = BKE_editmesh_from_object(obedit);
- BMFace *efa;
- BMIter iter, liter;
- BMLoop *l;
- UvVertMap *vmap;
- UvMapVert *iterv_curr;
- UvMapVert *iterv_next;
- int starttotf;
- bool looking, select;
+ bool select;
const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
- /* setup */
- BM_mesh_elem_table_ensure(em->bm, BM_FACE);
- vmap = BM_uv_vert_map_create(em->bm, false, false);
-
- BM_mesh_elem_index_ensure(em->bm, BM_VERT | BM_FACE);
-
- if (!extend) {
- uv_select_all_perform(scene, obedit, SEL_DESELECT);
+ if (extend) {
+ select = !(uvedit_uv_select_test(scene, hit->l, cd_loop_uv_offset));
+ }
+ else {
+ select = true;
}
- BM_mesh_elem_hflag_disable_all(em->bm, BM_FACE, BM_ELEM_TAG, false);
-
- /* set flags for first face and verts */
- iterv_curr = uv_select_edgeloop_vertex_map_get(vmap, hit->efa, hit->l);
- iterv_next = uv_select_edgeloop_vertex_map_get(vmap, hit->efa, hit->l->next);
- uv_select_edgeloop_vertex_loop_flag(iterv_curr);
- uv_select_edgeloop_vertex_loop_flag(iterv_next);
-
- starttotf = 0;
- uv_select_edgeloop_edge_tag_faces(em, iterv_curr, iterv_next, &starttotf);
-
- /* sorry, first edge isn't even ok */
- looking = !(iterv_curr->flag == 0 && iterv_next->flag == 0);
-
- /* iterate */
- while (looking) {
- looking = false;
-
- /* find correct valence edges which are not tagged yet, but connect to tagged one */
+ BMLoop *l_init_pair[2] = {
+ hit->l,
+ uvedit_loop_find_other_radial_loop_with_visible_face(scene, hit->l, cd_loop_uv_offset),
+ };
- BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
- if (!BM_elem_flag_test(efa, BM_ELEM_TAG) && uvedit_face_visible_test(scene, efa)) {
- BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
- /* check face not hidden and not tagged */
- if (!(iterv_curr = uv_select_edgeloop_vertex_map_get(vmap, efa, l))) {
- continue;
- }
- if (!(iterv_next = uv_select_edgeloop_vertex_map_get(vmap, efa, l->next))) {
- continue;
- }
+ /* When selecting boundaries, support cycling between selection modes. */
+ enum eUVEdgeLoopBoundaryMode boundary_mode = UV_EDGE_LOOP_BOUNDARY_LOOP;
- /* check if vertex is tagged and has right valence */
- if (iterv_curr->flag || iterv_next->flag) {
- if (uv_select_edgeloop_edge_tag_faces(em, iterv_curr, iterv_next, &starttotf)) {
- looking = true;
- BM_elem_flag_enable(efa, BM_ELEM_TAG);
+ /* Tag all loops that are part of the edge loop (select after).
+ * This is done so we can */
+ if (l_init_pair[1] == NULL) {
+ int count_by_select[2];
+ /* If the loops selected toggle the boundaries. */
+ uv_select_edgeloop_single_side_tag(
+ scene, em, l_init_pair[0], cd_loop_uv_offset, boundary_mode, count_by_select);
+ if (count_by_select[!select] == 0) {
+ boundary_mode = UV_EDGE_LOOP_BOUNDARY_ALL;
- uv_select_edgeloop_vertex_loop_flag(iterv_curr);
- uv_select_edgeloop_vertex_loop_flag(iterv_next);
- break;
- }
- }
- }
+ /* If the boundary is selected, toggle back to the loop. */
+ uv_select_edgeloop_single_side_tag(
+ scene, em, l_init_pair[0], cd_loop_uv_offset, boundary_mode, count_by_select);
+ if (count_by_select[!select] == 0) {
+ boundary_mode = UV_EDGE_LOOP_BOUNDARY_LOOP;
}
}
}
- /* do the actual select/deselect */
- iterv_curr = uv_select_edgeloop_vertex_map_get(vmap, hit->efa, hit->l);
- iterv_next = uv_select_edgeloop_vertex_map_get(vmap, hit->efa, hit->l->next);
- iterv_curr->flag = 1;
- iterv_next->flag = 1;
-
- if (extend) {
- select = !(uvedit_uv_select_test(scene, hit->l, cd_loop_uv_offset));
+ if (l_init_pair[1] == NULL) {
+ uv_select_edgeloop_single_side_tag(
+ scene, em, l_init_pair[0], cd_loop_uv_offset, boundary_mode, NULL);
}
else {
- select = true;
+ uv_select_edgeloop_double_side_tag(scene, em, l_init_pair, cd_loop_uv_offset);
}
- BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
- BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
- iterv_curr = uv_select_edgeloop_vertex_map_get(vmap, efa, l);
+ /* Apply the selection. */
+ if (!extend) {
+ uv_select_all_perform(scene, obedit, SEL_DESELECT);
+ }
- if (iterv_curr->flag) {
- uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset);
+ /* Select all tagged loops. */
+ {
+ BMIter iter;
+ BMFace *f;
+ BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) {
+ BMIter liter;
+ BMLoop *l_iter;
+ BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
+ if (BM_elem_flag_test(l_iter, BM_ELEM_TAG)) {
+ uvedit_edge_select_set_with_sticky(
+ sima, scene, em, l_iter, select, false, cd_loop_uv_offset);
+ }
}
}
}
- /* cleanup */
- BM_uv_vert_map_free(vmap);
-
return (select) ? 1 : -1;
}
@@ -1770,10 +1832,8 @@ static void uv_select_all_perform(Scene *scene, Object *obedit, int action)
}
}
-static void uv_select_all_perform_multi(Scene *scene,
- Object **objects,
- const uint objects_len,
- int action)
+static void uv_select_all_perform_multi_ex(
+ Scene *scene, Object **objects, const uint objects_len, int action, const Object *ob_exclude)
{
if (action == SEL_TOGGLE) {
action = uvedit_select_is_any_selected_multi(scene, objects, objects_len) ? SEL_DESELECT :
@@ -1782,10 +1842,21 @@ static void uv_select_all_perform_multi(Scene *scene,
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
+ if (ob_exclude && (obedit == ob_exclude)) {
+ continue;
+ }
uv_select_all_perform(scene, obedit, action);
}
}
+static void uv_select_all_perform_multi(Scene *scene,
+ Object **objects,
+ const uint objects_len,
+ int action)
+{
+ uv_select_all_perform_multi_ex(scene, objects, objects_len, action, NULL);
+}
+
static int uv_select_all_exec(bContext *C, wmOperator *op)
{
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
@@ -1931,8 +2002,7 @@ static int uv_mouse_select_multi(bContext *C,
/* do selection */
if (selectmode == UV_SELECT_ISLAND) {
if (!extend) {
- /* TODO(MULTI_EDIT): We only need to de-select non-active */
- uv_select_all_perform_multi(scene, objects, objects_len, SEL_DESELECT);
+ uv_select_all_perform_multi_ex(scene, objects, objects_len, SEL_DESELECT, obedit);
}
/* Current behavior of 'extend'
* is actually toggling, so pass extend flag as 'toggle' here */
@@ -2122,12 +2192,11 @@ static int uv_mouse_select_loop_generic_multi(bContext *C,
/* Do selection. */
if (!extend) {
- /* TODO(MULTI_EDIT): We only need to de-select non-active */
- uv_select_all_perform_multi(scene, objects, objects_len, SEL_DESELECT);
+ uv_select_all_perform_multi_ex(scene, objects, objects_len, SEL_DESELECT, obedit);
}
if (loop_type == UV_LOOP_SELECT) {
- flush = uv_select_edgeloop(scene, obedit, &hit, extend);
+ flush = uv_select_edgeloop(sima, scene, obedit, &hit, extend);
}
else if (loop_type == UV_RING_SELECT) {
flush = uv_select_edgering(sima, scene, obedit, &hit, extend);
diff --git a/source/blender/functions/tests/FN_array_spans_test.cc b/source/blender/functions/tests/FN_array_spans_test.cc
index 9a632b58be8..af2bc0aad91 100644
--- a/source/blender/functions/tests/FN_array_spans_test.cc
+++ b/source/blender/functions/tests/FN_array_spans_test.cc
@@ -50,7 +50,9 @@ TEST(virtual_array_span, MultipleArrayConstructor)
std::array<int, 2> values1 = {6, 7};
std::array<int, 1> values2 = {8};
std::array<const int *, 3> starts = {values0.data(), values1.data(), values2.data()};
- std::array<int64_t, 3> sizes{values0.size(), values1.size(), values2.size()};
+ std::array<int64_t, 3> sizes{static_cast<int64_t>(values0.size()),
+ static_cast<int64_t>(values1.size()),
+ static_cast<int64_t>(values2.size())};
VArraySpan<int> span{starts, sizes};
EXPECT_EQ(span.size(), 3);
diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt
index 45b379c5e0a..b319a9d91a3 100644
--- a/source/blender/gpu/CMakeLists.txt
+++ b/source/blender/gpu/CMakeLists.txt
@@ -84,7 +84,7 @@ set(SRC
intern/gpu_shader_interface.cc
intern/gpu_state.cc
intern/gpu_texture.cc
- intern/gpu_uniformbuffer.cc
+ intern/gpu_uniform_buffer.cc
intern/gpu_vertex_buffer.cc
intern/gpu_vertex_format.cc
intern/gpu_viewport.c
@@ -93,7 +93,9 @@ set(SRC
opengl/gl_context.cc
opengl/gl_drawlist.cc
opengl/gl_shader.cc
+ opengl/gl_shader_interface.cc
opengl/gl_state.cc
+ opengl/gl_uniform_buffer.cc
opengl/gl_vertex_array.cc
GPU_attr_binding.h
@@ -119,10 +121,9 @@ set(SRC
GPU_primitive.h
GPU_select.h
GPU_shader.h
- GPU_shader_interface.h
GPU_state.h
GPU_texture.h
- GPU_uniformbuffer.h
+ GPU_uniform_buffer.h
GPU_vertex_buffer.h
GPU_vertex_format.h
GPU_viewport.h
@@ -140,7 +141,9 @@ set(SRC
intern/gpu_private.h
intern/gpu_select_private.h
intern/gpu_shader_private.hh
+ intern/gpu_shader_interface.hh
intern/gpu_state_private.hh
+ intern/gpu_uniform_buffer_private.hh
intern/gpu_vertex_format_private.h
opengl/gl_backend.hh
@@ -148,7 +151,9 @@ set(SRC
opengl/gl_context.hh
opengl/gl_drawlist.hh
opengl/gl_shader.hh
+ opengl/gl_shader_interface.hh
opengl/gl_state.hh
+ opengl/gl_uniform_buffer.hh
opengl/gl_vertex_array.hh
)
diff --git a/source/blender/gpu/GPU_context.h b/source/blender/gpu/GPU_context.h
index e3d47cfe084..be7e604fb96 100644
--- a/source/blender/gpu/GPU_context.h
+++ b/source/blender/gpu/GPU_context.h
@@ -27,7 +27,6 @@
#include "GPU_batch.h"
#include "GPU_common.h"
-#include "GPU_shader_interface.h"
#ifdef __cplusplus
extern "C" {
diff --git a/source/blender/gpu/GPU_extensions.h b/source/blender/gpu/GPU_extensions.h
index 2ce6e458378..18ac2265cc4 100644
--- a/source/blender/gpu/GPU_extensions.h
+++ b/source/blender/gpu/GPU_extensions.h
@@ -40,7 +40,6 @@ int GPU_max_color_texture_samples(void);
int GPU_max_cube_map_size(void);
int GPU_max_ubo_binds(void);
int GPU_max_ubo_size(void);
-float GPU_max_line_width(void);
void GPU_get_dfdy_factors(float fac[2]);
bool GPU_arb_base_instance_is_supported(void);
bool GPU_arb_texture_cube_map_array_is_supported(void);
diff --git a/source/blender/gpu/GPU_framebuffer.h b/source/blender/gpu/GPU_framebuffer.h
index 7103317e4d6..db25bbb7998 100644
--- a/source/blender/gpu/GPU_framebuffer.h
+++ b/source/blender/gpu/GPU_framebuffer.h
@@ -225,7 +225,6 @@ void GPU_offscreen_viewport_data_get(GPUOffScreen *ofs,
void GPU_clear_color(float red, float green, float blue, float alpha);
void GPU_clear_depth(float depth);
-void GPU_clear(eGPUFrameBufferBits flags);
void GPU_frontbuffer_read_pixels(
int x, int y, int w, int h, int channels, eGPUDataFormat format, void *data);
diff --git a/source/blender/gpu/GPU_immediate.h b/source/blender/gpu/GPU_immediate.h
index 41d4f5d28d3..6057770d2d9 100644
--- a/source/blender/gpu/GPU_immediate.h
+++ b/source/blender/gpu/GPU_immediate.h
@@ -29,7 +29,6 @@
#include "GPU_immediate_util.h"
#include "GPU_primitive.h"
#include "GPU_shader.h"
-#include "GPU_shader_interface.h"
#include "GPU_texture.h"
#include "GPU_vertex_format.h"
@@ -103,13 +102,11 @@ void immVertex2iv(uint attr_id, const int data[2]);
/* Provide uniform values that don't change for the entire draw call. */
void immUniform1i(const char *name, int x);
-void immUniform4iv(const char *name, const int data[4]);
void immUniform1f(const char *name, float x);
void immUniform2f(const char *name, float x, float y);
void immUniform2fv(const char *name, const float data[2]);
void immUniform3f(const char *name, float x, float y, float z);
void immUniform3fv(const char *name, const float data[3]);
-void immUniformArray3fv(const char *name, const float *data, int count);
void immUniform4f(const char *name, float x, float y, float z, float w);
void immUniform4fv(const char *name, const float data[4]);
void immUniformArray4fv(const char *bare_name, const float *data, int count);
diff --git a/source/blender/gpu/GPU_material.h b/source/blender/gpu/GPU_material.h
index 9dcf9b7d5bb..680e717e615 100644
--- a/source/blender/gpu/GPU_material.h
+++ b/source/blender/gpu/GPU_material.h
@@ -39,7 +39,7 @@ struct GPUNode;
struct GPUNodeLink;
struct GPUNodeStack;
struct GPUTexture;
-struct GPUUniformBuffer;
+struct GPUUniformBuf;
struct Image;
struct ImageUser;
struct ListBase;
@@ -158,10 +158,10 @@ bool GPU_stack_link(GPUMaterial *mat,
GPUNodeStack *in,
GPUNodeStack *out,
...);
-GPUNodeLink *GPU_uniformbuffer_link_out(struct GPUMaterial *mat,
- struct bNode *node,
- struct GPUNodeStack *stack,
- const int index);
+GPUNodeLink *GPU_uniformbuf_link_out(struct GPUMaterial *mat,
+ struct bNode *node,
+ struct GPUNodeStack *stack,
+ const int index);
void GPU_material_output_link(GPUMaterial *material, GPUNodeLink *link);
@@ -169,9 +169,9 @@ void GPU_material_sss_profile_create(GPUMaterial *material,
float radii[3],
const short *falloff_type,
const float *sharpness);
-struct GPUUniformBuffer *GPU_material_sss_profile_get(GPUMaterial *material,
- int sample_len,
- struct GPUTexture **tex_profile);
+struct GPUUniformBuf *GPU_material_sss_profile_get(GPUMaterial *material,
+ int sample_len,
+ struct GPUTexture **tex_profile);
/* High level functions to create and use GPU materials */
GPUMaterial *GPU_material_from_nodetree_find(struct ListBase *gpumaterials,
@@ -201,9 +201,9 @@ struct GPUShader *GPU_material_get_shader(GPUMaterial *material);
struct Material *GPU_material_get_material(GPUMaterial *material);
eGPUMaterialStatus GPU_material_status(GPUMaterial *mat);
-struct GPUUniformBuffer *GPU_material_uniform_buffer_get(GPUMaterial *material);
+struct GPUUniformBuf *GPU_material_uniform_buffer_get(GPUMaterial *material);
void GPU_material_uniform_buffer_create(GPUMaterial *material, ListBase *inputs);
-struct GPUUniformBuffer *GPU_material_create_sss_profile_ubo(void);
+struct GPUUniformBuf *GPU_material_create_sss_profile_ubo(void);
bool GPU_material_has_surface_output(GPUMaterial *mat);
bool GPU_material_has_volume_output(GPUMaterial *mat);
diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h
index 99fcae19984..33fef266c42 100644
--- a/source/blender/gpu/GPU_shader.h
+++ b/source/blender/gpu/GPU_shader.h
@@ -27,19 +27,12 @@
extern "C" {
#endif
-struct GPUShaderInterface;
struct GPUTexture;
-struct GPUUniformBuffer;
+struct GPUUniformBuf;
struct GPUVertBuf;
-/* TODO(fclem) These members should be private and the
- * whole struct should just be an opaque pointer. */
-typedef struct GPUShader {
- /** Uniform & attribute locations for shader. */
- struct GPUShaderInterface *interface;
- /** For debugging purpose. */
- char name[64];
-} GPUShader;
+/** Opaque type hidding blender::gpu::Shader */
+typedef struct GPUShader GPUShader;
typedef enum eGPUShaderTFBType {
GPU_SHADER_TFB_NONE = 0, /* Transform feedback unsupported. */
@@ -90,6 +83,41 @@ void GPU_shader_transform_feedback_disable(GPUShader *shader);
int GPU_shader_get_program(GPUShader *shader);
+typedef enum {
+ GPU_UNIFORM_MODEL = 0, /* mat4 ModelMatrix */
+ GPU_UNIFORM_VIEW, /* mat4 ViewMatrix */
+ GPU_UNIFORM_MODELVIEW, /* mat4 ModelViewMatrix */
+ GPU_UNIFORM_PROJECTION, /* mat4 ProjectionMatrix */
+ GPU_UNIFORM_VIEWPROJECTION, /* mat4 ViewProjectionMatrix */
+ GPU_UNIFORM_MVP, /* mat4 ModelViewProjectionMatrix */
+
+ GPU_UNIFORM_MODEL_INV, /* mat4 ModelMatrixInverse */
+ GPU_UNIFORM_VIEW_INV, /* mat4 ViewMatrixInverse */
+ GPU_UNIFORM_MODELVIEW_INV, /* mat4 ModelViewMatrixInverse */
+ GPU_UNIFORM_PROJECTION_INV, /* mat4 ProjectionMatrixInverse */
+ GPU_UNIFORM_VIEWPROJECTION_INV, /* mat4 ViewProjectionMatrixInverse */
+
+ GPU_UNIFORM_NORMAL, /* mat3 NormalMatrix */
+ GPU_UNIFORM_ORCO, /* vec4 OrcoTexCoFactors[] */
+ GPU_UNIFORM_CLIPPLANES, /* vec4 WorldClipPlanes[] */
+
+ GPU_UNIFORM_COLOR, /* vec4 color */
+ GPU_UNIFORM_BASE_INSTANCE, /* int baseInstance */
+ GPU_UNIFORM_RESOURCE_CHUNK, /* int resourceChunk */
+ GPU_UNIFORM_RESOURCE_ID, /* int resourceId */
+ GPU_UNIFORM_SRGB_TRANSFORM, /* bool srgbTarget */
+
+ GPU_NUM_UNIFORMS, /* Special value, denotes number of builtin uniforms. */
+} GPUUniformBuiltin;
+
+typedef enum {
+ GPU_UNIFORM_BLOCK_VIEW = 0, /* viewBlock */
+ GPU_UNIFORM_BLOCK_MODEL, /* modelBlock */
+ GPU_UNIFORM_BLOCK_INFO, /* infoBlock */
+
+ GPU_NUM_UNIFORM_BLOCKS, /* Special value, denotes number of builtin uniforms block. */
+} GPUUniformBlockBuiltin;
+
void GPU_shader_set_srgb_uniform(GPUShader *shader);
int GPU_shader_get_uniform(GPUShader *shader, const char *name);
@@ -123,8 +151,6 @@ void GPU_shader_uniform_4fv_array(GPUShader *sh, const char *name, int len, cons
int GPU_shader_get_attribute(GPUShader *shader, const char *name);
-char *GPU_shader_get_binary(GPUShader *shader, uint *r_binary_format, int *r_binary_len);
-
void GPU_shader_set_framebuffer_srgb_target(int use_srgb_to_linear);
/* Builtin/Non-generated shaders */
diff --git a/source/blender/gpu/GPU_shader_interface.h b/source/blender/gpu/GPU_shader_interface.h
deleted file mode 100644
index 47e4e432d66..00000000000
--- a/source/blender/gpu/GPU_shader_interface.h
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * The Original Code is Copyright (C) 2016 by Mike Erwin.
- * All rights reserved.
- */
-
-/** \file
- * \ingroup gpu
- *
- * GPU shader interface (C --> GLSL)
- */
-
-#pragma once
-
-#include "GPU_common.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef enum {
- GPU_UNIFORM_MODEL = 0, /* mat4 ModelMatrix */
- GPU_UNIFORM_VIEW, /* mat4 ViewMatrix */
- GPU_UNIFORM_MODELVIEW, /* mat4 ModelViewMatrix */
- GPU_UNIFORM_PROJECTION, /* mat4 ProjectionMatrix */
- GPU_UNIFORM_VIEWPROJECTION, /* mat4 ViewProjectionMatrix */
- GPU_UNIFORM_MVP, /* mat4 ModelViewProjectionMatrix */
-
- GPU_UNIFORM_MODEL_INV, /* mat4 ModelMatrixInverse */
- GPU_UNIFORM_VIEW_INV, /* mat4 ViewMatrixInverse */
- GPU_UNIFORM_MODELVIEW_INV, /* mat4 ModelViewMatrixInverse */
- GPU_UNIFORM_PROJECTION_INV, /* mat4 ProjectionMatrixInverse */
- GPU_UNIFORM_VIEWPROJECTION_INV, /* mat4 ViewProjectionMatrixInverse */
-
- GPU_UNIFORM_NORMAL, /* mat3 NormalMatrix */
- GPU_UNIFORM_ORCO, /* vec4 OrcoTexCoFactors[] */
- GPU_UNIFORM_CLIPPLANES, /* vec4 WorldClipPlanes[] */
-
- GPU_UNIFORM_COLOR, /* vec4 color */
- GPU_UNIFORM_BASE_INSTANCE, /* int baseInstance */
- GPU_UNIFORM_RESOURCE_CHUNK, /* int resourceChunk */
- GPU_UNIFORM_RESOURCE_ID, /* int resourceId */
- GPU_UNIFORM_SRGB_TRANSFORM, /* bool srgbTarget */
-
- GPU_NUM_UNIFORMS, /* Special value, denotes number of builtin uniforms. */
-} GPUUniformBuiltin;
-
-typedef enum {
- GPU_UNIFORM_BLOCK_VIEW = 0, /* viewBlock */
- GPU_UNIFORM_BLOCK_MODEL, /* modelBlock */
- GPU_UNIFORM_BLOCK_INFO, /* infoBlock */
-
- GPU_NUM_UNIFORM_BLOCKS, /* Special value, denotes number of builtin uniforms block. */
-} GPUUniformBlockBuiltin;
-
-typedef struct GPUShaderInput {
- uint32_t name_offset;
- uint32_t name_hash;
- int32_t location;
- /** Defined at interface creation or in shader. Only for Samplers, UBOs and Vertex Attribs. */
- int32_t binding;
-} GPUShaderInput;
-
-#define GPU_SHADERINTERFACE_REF_ALLOC_COUNT 16
-
-typedef struct GPUShaderInterface {
- /** Buffer containing all inputs names separated by '\0'. */
- char *name_buffer;
- /** Reference to GPUBatches using this interface */
- void **batches;
- uint batches_len;
- /** Input counts. */
- uint attribute_len;
- uint ubo_len;
- uint uniform_len;
- /** Enabled bindpoints that needs to be fed with data. */
- uint16_t enabled_attr_mask;
- uint16_t enabled_ubo_mask;
- uint64_t enabled_tex_mask;
- /** Opengl Location of builtin uniforms. Fast access, no lookup needed. */
- int32_t builtins[GPU_NUM_UNIFORMS];
- int32_t builtin_blocks[GPU_NUM_UNIFORM_BLOCKS];
- /** Flat array. In this order: Attributes, Ubos, Uniforms. */
- GPUShaderInput inputs[0];
-} GPUShaderInterface;
-
-GPUShaderInterface *GPU_shaderinterface_create(int32_t program_id);
-void GPU_shaderinterface_discard(GPUShaderInterface *);
-
-const GPUShaderInput *GPU_shaderinterface_uniform(const GPUShaderInterface *, const char *name);
-int32_t GPU_shaderinterface_uniform_builtin(const GPUShaderInterface *shaderface,
- GPUUniformBuiltin builtin);
-int32_t GPU_shaderinterface_block_builtin(const GPUShaderInterface *shaderface,
- GPUUniformBlockBuiltin builtin);
-const GPUShaderInput *GPU_shaderinterface_ubo(const GPUShaderInterface *, const char *name);
-const GPUShaderInput *GPU_shaderinterface_attr(const GPUShaderInterface *, const char *name);
-
-/* keep track of batches using this interface */
-void GPU_shaderinterface_add_batch_ref(GPUShaderInterface *interface, void *cache);
-void GPU_shaderinterface_remove_batch_ref(GPUShaderInterface *interface, void *cache);
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/source/blender/gpu/GPU_state.h b/source/blender/gpu/GPU_state.h
index be3250f6654..253877bcca0 100644
--- a/source/blender/gpu/GPU_state.h
+++ b/source/blender/gpu/GPU_state.h
@@ -29,6 +29,7 @@ typedef enum eGPUWriteMask {
GPU_WRITE_BLUE = (1 << 2),
GPU_WRITE_ALPHA = (1 << 3),
GPU_WRITE_DEPTH = (1 << 4),
+ GPU_WRITE_STENCIL = (1 << 5),
GPU_WRITE_COLOR = (GPU_WRITE_RED | GPU_WRITE_GREEN | GPU_WRITE_BLUE | GPU_WRITE_ALPHA),
} eGPUWriteMask;
@@ -66,9 +67,9 @@ typedef enum eGPUBlend {
typedef enum eGPUDepthTest {
GPU_DEPTH_NONE = 0,
- GPU_DEPTH_ALWAYS,
+ GPU_DEPTH_ALWAYS, /* Used to draw to the depth buffer without really testing. */
GPU_DEPTH_LESS,
- GPU_DEPTH_LESS_EQUAL,
+ GPU_DEPTH_LESS_EQUAL, /* Default. */
GPU_DEPTH_EQUAL,
GPU_DEPTH_GREATER,
GPU_DEPTH_GREATER_EQUAL,
@@ -106,11 +107,11 @@ extern "C" {
void GPU_blend(eGPUBlend blend);
void GPU_face_culling(eGPUFaceCullTest culling);
-void GPU_front_facing(bool invert);
+void GPU_depth_test(eGPUDepthTest test);
+void GPU_stencil_test(eGPUStencilTest test);
void GPU_provoking_vertex(eGPUProvokingVertex vert);
+void GPU_front_facing(bool invert);
void GPU_depth_range(float near, float far);
-void GPU_depth_test(bool enable);
-bool GPU_depth_test_enabled(void);
void GPU_scissor_test(bool enable);
void GPU_line_smooth(bool enable);
void GPU_line_width(float width);
@@ -144,7 +145,10 @@ void GPU_stencil_write_mask_set(uint write_mask);
void GPU_stencil_compare_mask_set(uint compare_mask);
eGPUBlend GPU_blend_get(void);
+eGPUDepthTest GPU_depth_test_get(void);
eGPUWriteMask GPU_write_mask_get(void);
+uint GPU_stencil_mask_get(void);
+eGPUStencilTest GPU_stencil_test_get(void);
void GPU_flush(void);
void GPU_finish(void);
diff --git a/source/blender/gpu/GPU_uniform_buffer.h b/source/blender/gpu/GPU_uniform_buffer.h
new file mode 100644
index 00000000000..4a00dda634d
--- /dev/null
+++ b/source/blender/gpu/GPU_uniform_buffer.h
@@ -0,0 +1,61 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2005 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup gpu
+ *
+ * Uniform buffers API. Used to handle many uniforms update at once.
+ * Make sure that the data structure is compatible with what the implementation expect.
+ * (see "7.6.2.2 Standard Uniform Block Layout" from the OpenGL spec for more info about std140
+ * layout)
+ * Rule of thumb: Padding to 16bytes, don't use vec3, don't use arrays of anything that is not vec4
+ * aligned .
+ */
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ListBase;
+
+/** Opaque pointer hiding blender::gpu::UniformBuf. */
+typedef struct GPUUniformBuf {
+ void *dummy;
+} GPUUniformBuf;
+
+GPUUniformBuf *GPU_uniformbuf_create_ex(size_t size, const void *data, const char *name);
+GPUUniformBuf *GPU_uniformbuf_create_from_list(struct ListBase *inputs, const char *name);
+
+#define GPU_uniformbuf_create(size) GPU_uniformbuf_create_ex(size, NULL, __func__);
+
+void GPU_uniformbuf_free(GPUUniformBuf *ubo);
+
+void GPU_uniformbuf_update(GPUUniformBuf *ubo, const void *data);
+
+void GPU_uniformbuf_bind(GPUUniformBuf *ubo, int number);
+void GPU_uniformbuf_unbind(GPUUniformBuf *ubo);
+void GPU_uniformbuf_unbind_all(void);
+
+#define GPU_UBO_BLOCK_NAME "nodeTree"
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/gpu/GPU_uniformbuffer.h b/source/blender/gpu/GPU_uniformbuffer.h
deleted file mode 100644
index e2b2a757fb9..00000000000
--- a/source/blender/gpu/GPU_uniformbuffer.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * The Original Code is Copyright (C) 2005 Blender Foundation.
- * All rights reserved.
- */
-
-/** \file
- * \ingroup gpu
- */
-
-#pragma once
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-struct ListBase;
-
-typedef struct GPUUniformBuffer GPUUniformBuffer;
-
-GPUUniformBuffer *GPU_uniformbuffer_create(int size, const void *data, char err_out[256]);
-GPUUniformBuffer *GPU_uniformbuffer_dynamic_create(struct ListBase *inputs, char err_out[256]);
-
-void GPU_uniformbuffer_free(GPUUniformBuffer *ubo);
-
-void GPU_uniformbuffer_update(GPUUniformBuffer *ubo, const void *data);
-void GPU_uniformbuffer_dynamic_update(GPUUniformBuffer *ubo_);
-
-void GPU_uniformbuffer_bind(GPUUniformBuffer *ubo, int number);
-void GPU_uniformbuffer_unbind(GPUUniformBuffer *ubo);
-void GPU_uniformbuffer_unbind_all(void);
-
-bool GPU_uniformbuffer_is_empty(GPUUniformBuffer *ubo);
-bool GPU_uniformbuffer_is_dirty(GPUUniformBuffer *ubo);
-
-#define GPU_UBO_BLOCK_NAME "nodeTree"
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/source/blender/gpu/GPU_viewport.h b/source/blender/gpu/GPU_viewport.h
index 60b78ecd59b..c3e2f1788b4 100644
--- a/source/blender/gpu/GPU_viewport.h
+++ b/source/blender/gpu/GPU_viewport.h
@@ -55,8 +55,8 @@ typedef struct ViewportMemoryPool {
struct BLI_memblock *views;
struct BLI_memblock *passes;
struct BLI_memblock *images;
- struct GPUUniformBuffer **matrices_ubo;
- struct GPUUniformBuffer **obinfos_ubo;
+ struct GPUUniformBuf **matrices_ubo;
+ struct GPUUniformBuf **obinfos_ubo;
uint ubo_len;
} ViewportMemoryPool;
diff --git a/source/blender/gpu/intern/gpu_attr_binding.cc b/source/blender/gpu/intern/gpu_attr_binding.cc
index 6cb60884620..2a48107e190 100644
--- a/source/blender/gpu/intern/gpu_attr_binding.cc
+++ b/source/blender/gpu/intern/gpu_attr_binding.cc
@@ -61,9 +61,7 @@ static void write_attr_location(GPUAttrBinding *binding, uint a_idx, uint locati
binding->enabled_bits |= 1 << a_idx;
}
-void get_attr_locations(const GPUVertFormat *format,
- GPUAttrBinding *binding,
- const GPUShaderInterface *shaderface)
+void get_attr_locations(const GPUVertFormat *format, GPUAttrBinding *binding, GPUShader *shader)
{
AttrBinding_clear(binding);
@@ -71,13 +69,12 @@ void get_attr_locations(const GPUVertFormat *format,
const GPUVertAttr *a = &format->attrs[a_idx];
for (uint n_idx = 0; n_idx < a->name_len; n_idx++) {
const char *name = GPU_vertformat_attr_name_get(format, a, n_idx);
- const GPUShaderInput *input = GPU_shaderinterface_attr(shaderface, name);
-#if TRUST_NO_ONE
- assert(input != NULL);
+ int loc = GPU_shader_get_attribute(shader, name);
/* TODO: make this a recoverable runtime error?
* indicates mismatch between vertex format and program. */
-#endif
- write_attr_location(binding, a_idx, input->location);
+ BLI_assert(loc != -1);
+
+ write_attr_location(binding, a_idx, loc);
}
}
}
diff --git a/source/blender/gpu/intern/gpu_attr_binding_private.h b/source/blender/gpu/intern/gpu_attr_binding_private.h
index 4d359343c38..cd67a51a822 100644
--- a/source/blender/gpu/intern/gpu_attr_binding_private.h
+++ b/source/blender/gpu/intern/gpu_attr_binding_private.h
@@ -25,8 +25,8 @@
#pragma once
-#include "GPU_shader_interface.h"
#include "GPU_vertex_format.h"
+#include "gpu_shader_interface.hh"
#ifdef __cplusplus
extern "C" {
@@ -35,9 +35,7 @@ extern "C" {
/* TODO(fclem) remove, use shaderface directly. */
void AttrBinding_clear(GPUAttrBinding *binding);
-void get_attr_locations(const GPUVertFormat *format,
- GPUAttrBinding *binding,
- const GPUShaderInterface *shaderface);
+void get_attr_locations(const GPUVertFormat *format, GPUAttrBinding *binding, GPUShader *shader);
uint read_attr_location(const GPUAttrBinding *binding, uint a_idx);
#ifdef __cplusplus
diff --git a/source/blender/gpu/intern/gpu_backend.hh b/source/blender/gpu/intern/gpu_backend.hh
index 6ab0e32a754..f63f3cead2b 100644
--- a/source/blender/gpu/intern/gpu_backend.hh
+++ b/source/blender/gpu/intern/gpu_backend.hh
@@ -25,14 +25,16 @@
#pragma once
-#include "gpu_batch_private.hh"
-#include "gpu_context_private.hh"
-#include "gpu_drawlist_private.hh"
-#include "gpu_shader_private.hh"
+struct GPUContext;
namespace blender {
namespace gpu {
+class Batch;
+class DrawList;
+class Shader;
+class UniformBuf;
+
class GPUBackend {
public:
virtual ~GPUBackend(){};
@@ -46,6 +48,7 @@ class GPUBackend {
// virtual FrameBuffer *framebuffer_alloc(void) = 0;
virtual Shader *shader_alloc(const char *name) = 0;
// virtual Texture *texture_alloc(void) = 0;
+ virtual UniformBuf *uniformbuf_alloc(int size, const char *name) = 0;
};
} // namespace gpu
diff --git a/source/blender/gpu/intern/gpu_batch_private.hh b/source/blender/gpu/intern/gpu_batch_private.hh
index 3a8044efc1d..c0444647fe1 100644
--- a/source/blender/gpu/intern/gpu_batch_private.hh
+++ b/source/blender/gpu/intern/gpu_batch_private.hh
@@ -28,11 +28,14 @@
#include "GPU_batch.h"
#include "GPU_context.h"
-#include "GPU_shader_interface.h"
namespace blender {
namespace gpu {
+/**
+ * Base class which is then specialized for each implementation (GL, VK, ...).
+ * NOTE: Extends GPUBatch as we still needs to expose some of the internals to the outside C code.
+ **/
class Batch : public GPUBatch {
public:
Batch(){};
diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c
index b051d4fe59a..1629584e841 100644
--- a/source/blender/gpu/intern/gpu_codegen.c
+++ b/source/blender/gpu/intern/gpu_codegen.c
@@ -43,7 +43,7 @@
#include "GPU_extensions.h"
#include "GPU_material.h"
#include "GPU_shader.h"
-#include "GPU_uniformbuffer.h"
+#include "GPU_uniform_buffer.h"
#include "GPU_vertex_format.h"
#include "BLI_sys_types.h" /* for intptr_t support */
diff --git a/source/blender/gpu/intern/gpu_context_private.hh b/source/blender/gpu/intern/gpu_context_private.hh
index b774d6b0995..e8c9c976e9a 100644
--- a/source/blender/gpu/intern/gpu_context_private.hh
+++ b/source/blender/gpu/intern/gpu_context_private.hh
@@ -29,6 +29,7 @@
#include "GPU_context.h"
+#include "gpu_shader_private.hh"
#include "gpu_state_private.hh"
#include <mutex>
@@ -43,7 +44,7 @@ struct GPUMatrixState;
struct GPUContext {
public:
/** State managment */
- GPUShader *shader = NULL;
+ blender::gpu::Shader *shader = NULL;
GPUFrameBuffer *current_fbo = NULL;
GPUMatrixState *matrix_state = NULL;
blender::gpu::GPUStateManager *state_manager = NULL;
diff --git a/source/blender/gpu/intern/gpu_drawlist_private.hh b/source/blender/gpu/intern/gpu_drawlist_private.hh
index 04cc18a5ffd..ddb09fb0c89 100644
--- a/source/blender/gpu/intern/gpu_drawlist_private.hh
+++ b/source/blender/gpu/intern/gpu_drawlist_private.hh
@@ -28,6 +28,10 @@
namespace blender {
namespace gpu {
+/**
+ * Implementation of Multi Draw Indirect.
+ * Base class which is then specialized for each implementation (GL, VK, ...).
+ **/
class DrawList {
public:
virtual ~DrawList(){};
diff --git a/source/blender/gpu/intern/gpu_extensions.cc b/source/blender/gpu/intern/gpu_extensions.cc
index 8074e4b64f0..1d607d79b01 100644
--- a/source/blender/gpu/intern/gpu_extensions.cc
+++ b/source/blender/gpu/intern/gpu_extensions.cc
@@ -71,12 +71,10 @@ static struct GPUGlobal {
GLint maxubosize;
GLint maxubobinds;
int samples_color_texture_max;
- float line_width_range[2];
/* workaround for different calculation of dfdy factors on GPUs. Some GPUs/drivers
* calculate dfdy in shader differently when drawing to an off-screen buffer. First
* number is factor on screen and second is off-screen */
float dfdyfactors[2];
- float max_anisotropy;
/* Some Intel drivers have limited support for `GLEW_ARB_base_instance` so in
* these cases it is best to indicate that it is not supported. See T67951 */
bool glew_arb_base_instance_is_supported;
@@ -164,11 +162,6 @@ int GPU_max_textures_vert(void)
return GG.maxtexturesvert;
}
-float GPU_max_texture_anisotropy(void)
-{
- return GG.max_anisotropy;
-}
-
int GPU_max_color_texture_samples(void)
{
return GG.samples_color_texture_max;
@@ -189,11 +182,6 @@ int GPU_max_ubo_size(void)
return GG.maxubosize;
}
-float GPU_max_line_width(void)
-{
- return GG.line_width_range[1];
-}
-
void GPU_get_dfdy_factors(float fac[2])
{
copy_v2_v2(fac, GG.dfdyfactors);
@@ -264,18 +252,9 @@ void gpu_extensions_init(void)
glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &GG.maxtexlayers);
glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &GG.maxcubemapsize);
- if (GLEW_EXT_texture_filter_anisotropic) {
- glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &GG.max_anisotropy);
- }
- else {
- GG.max_anisotropy = 1.0f;
- }
-
glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_BLOCKS, &GG.maxubobinds);
glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &GG.maxubosize);
- glGetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, GG.line_width_range);
-
glGetIntegerv(GL_MAX_COLOR_TEXTURE_SAMPLES, &GG.samples_color_texture_max);
const char *vendor = (const char *)glGetString(GL_VENDOR);
diff --git a/source/blender/gpu/intern/gpu_framebuffer.cc b/source/blender/gpu/intern/gpu_framebuffer.cc
index da8ab80b347..88013640bfc 100644
--- a/source/blender/gpu/intern/gpu_framebuffer.cc
+++ b/source/blender/gpu/intern/gpu_framebuffer.cc
@@ -622,23 +622,37 @@ void GPU_framebuffer_clear(GPUFrameBuffer *fb,
{
CHECK_FRAMEBUFFER_IS_BOUND(fb);
- GPU_context_active_get()->state_manager->apply_state();
+ /* Save and restore the state. */
+ eGPUWriteMask write_mask = GPU_write_mask_get();
+ uint stencil_mask = GPU_stencil_mask_get();
+ eGPUStencilTest stencil_test = GPU_stencil_test_get();
if (buffers & GPU_COLOR_BIT) {
- glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ GPU_color_mask(true, true, true, true);
glClearColor(clear_col[0], clear_col[1], clear_col[2], clear_col[3]);
}
if (buffers & GPU_DEPTH_BIT) {
- glDepthMask(GL_TRUE);
+ GPU_depth_mask(true);
glClearDepth(clear_depth);
}
if (buffers & GPU_STENCIL_BIT) {
- glStencilMask(0xFF);
+ GPU_stencil_write_mask_set(0xFFu);
+ GPU_stencil_test(GPU_STENCIL_ALWAYS);
glClearStencil(clear_stencil);
}
+ GPU_context_active_get()->state_manager->apply_state();
+
GLbitfield mask = convert_buffer_bits_to_gl(buffers);
glClear(mask);
+
+ if (buffers & (GPU_COLOR_BIT | GPU_DEPTH_BIT)) {
+ GPU_write_mask(write_mask);
+ }
+ if (buffers & GPU_STENCIL_BIT) {
+ GPU_stencil_write_mask_set(stencil_mask);
+ GPU_stencil_test(stencil_test);
+ }
}
/* Clear all textures bound to this framebuffer with a different color. */
@@ -1103,18 +1117,22 @@ void GPU_offscreen_viewport_data_get(GPUOffScreen *ofs,
void GPU_clear_color(float red, float green, float blue, float alpha)
{
+ BLI_assert((GPU_write_mask_get() & GPU_WRITE_COLOR) != 0);
+
+ GPU_context_active_get()->state_manager->apply_state();
+
glClearColor(red, green, blue, alpha);
+ glClear(GL_COLOR_BUFFER_BIT);
}
void GPU_clear_depth(float depth)
{
- glClearDepth(depth);
-}
+ BLI_assert((GPU_write_mask_get() & GPU_WRITE_DEPTH) != 0);
-void GPU_clear(eGPUFrameBufferBits flags)
-{
GPU_context_active_get()->state_manager->apply_state();
- glClear(convert_buffer_bits_to_gl(flags));
+
+ glClearDepth(depth);
+ glClear(GL_DEPTH_BUFFER_BIT);
}
void GPU_frontbuffer_read_pixels(
diff --git a/source/blender/gpu/intern/gpu_immediate.cc b/source/blender/gpu/intern/gpu_immediate.cc
index dd05689d69a..431dbe848f7 100644
--- a/source/blender/gpu/intern/gpu_immediate.cc
+++ b/source/blender/gpu/intern/gpu_immediate.cc
@@ -73,7 +73,6 @@ typedef struct {
GLuint vao_id;
GPUShader *bound_program;
- const GPUShaderInterface *shader_interface;
GPUAttrBinding attr_binding;
uint16_t prev_enabled_attr_bits; /* <-- only affects this VAO, so we're ok */
} Immediate;
@@ -148,14 +147,13 @@ void immBindShader(GPUShader *shader)
BLI_assert(imm.bound_program == NULL);
imm.bound_program = shader;
- imm.shader_interface = shader->interface;
if (!imm.vertex_format.packed) {
VertexFormat_pack(&imm.vertex_format);
}
GPU_shader_bind(shader);
- get_attr_locations(&imm.vertex_format, &imm.attr_binding, imm.shader_interface);
+ get_attr_locations(&imm.vertex_format, &imm.attr_binding, shader);
GPU_matrix_bind(shader);
GPU_shader_set_srgb_uniform(shader);
}
@@ -749,123 +747,77 @@ void immVertex2iv(uint attr_id, const int data[2])
/* --- generic uniform functions --- */
-#if 0
-# if TRUST_NO_ONE
-# define GET_UNIFORM \
- const GPUShaderInput *uniform = GPU_shaderinterface_uniform(imm.shader_interface, name); \
- assert(uniform);
-# else
-# define GET_UNIFORM \
- const GPUShaderInput *uniform = GPU_shaderinterface_uniform(imm.shader_interface, name);
-# endif
-#else
-/* NOTE: It is possible to have uniform fully optimized out from the shader.
- * In this case we can't assert failure or allow NULL-pointer dereference.
- * TODO(sergey): How can we detect existing-but-optimized-out uniform but still
- * catch typos in uniform names passed to immUniform*() functions? */
-# define GET_UNIFORM \
- const GPUShaderInput *uniform = GPU_shaderinterface_uniform(imm.shader_interface, name); \
- if (uniform == NULL) \
- return;
-#endif
-
void immUniform1f(const char *name, float x)
{
- GET_UNIFORM
- glUniform1f(uniform->location, x);
+ GPU_shader_uniform_1f(imm.bound_program, name, x);
}
void immUniform2f(const char *name, float x, float y)
{
- GET_UNIFORM
- glUniform2f(uniform->location, x, y);
+ GPU_shader_uniform_2f(imm.bound_program, name, x, y);
}
void immUniform2fv(const char *name, const float data[2])
{
- GET_UNIFORM
- glUniform2fv(uniform->location, 1, data);
+ GPU_shader_uniform_2fv(imm.bound_program, name, data);
}
void immUniform3f(const char *name, float x, float y, float z)
{
- GET_UNIFORM
- glUniform3f(uniform->location, x, y, z);
+ GPU_shader_uniform_3f(imm.bound_program, name, x, y, z);
}
void immUniform3fv(const char *name, const float data[3])
{
- GET_UNIFORM
- glUniform3fv(uniform->location, 1, data);
-}
-
-/* can increase this limit or move to another file */
-#define MAX_UNIFORM_NAME_LEN 60
-
-/* Note array index is not supported for name (i.e: "array[0]"). */
-void immUniformArray3fv(const char *name, const float *data, int count)
-{
- GET_UNIFORM
- glUniform3fv(uniform->location, count, data);
+ GPU_shader_uniform_3fv(imm.bound_program, name, data);
}
void immUniform4f(const char *name, float x, float y, float z, float w)
{
- GET_UNIFORM
- glUniform4f(uniform->location, x, y, z, w);
+ GPU_shader_uniform_4f(imm.bound_program, name, x, y, z, w);
}
void immUniform4fv(const char *name, const float data[4])
{
- GET_UNIFORM
- glUniform4fv(uniform->location, 1, data);
+ GPU_shader_uniform_4fv(imm.bound_program, name, data);
}
/* Note array index is not supported for name (i.e: "array[0]"). */
void immUniformArray4fv(const char *name, const float *data, int count)
{
- GET_UNIFORM
- glUniform4fv(uniform->location, count, data);
+ GPU_shader_uniform_4fv_array(imm.bound_program, name, count, (float(*)[4])data);
}
void immUniformMatrix4fv(const char *name, const float data[4][4])
{
- GET_UNIFORM
- glUniformMatrix4fv(uniform->location, 1, GL_FALSE, (float *)data);
+ GPU_shader_uniform_mat4(imm.bound_program, name, data);
}
void immUniform1i(const char *name, int x)
{
- GET_UNIFORM
- glUniform1i(uniform->location, x);
-}
-
-void immUniform4iv(const char *name, const int data[4])
-{
- GET_UNIFORM
- glUniform4iv(uniform->location, 1, data);
+ GPU_shader_uniform_1i(imm.bound_program, name, x);
}
void immBindTexture(const char *name, GPUTexture *tex)
{
- GET_UNIFORM
- GPU_texture_bind(tex, uniform->binding);
+ int binding = GPU_shader_get_texture_binding(imm.bound_program, name);
+ GPU_texture_bind(tex, binding);
}
void immBindTextureSampler(const char *name, GPUTexture *tex, eGPUSamplerState state)
{
- GET_UNIFORM
- GPU_texture_bind_ex(tex, state, uniform->binding, true);
+ int binding = GPU_shader_get_texture_binding(imm.bound_program, name);
+ GPU_texture_bind_ex(tex, state, binding, true);
}
/* --- convenience functions for setting "uniform vec4 color" --- */
void immUniformColor4f(float r, float g, float b, float a)
{
- int32_t uniform_loc = GPU_shaderinterface_uniform_builtin(imm.shader_interface,
- GPU_UNIFORM_COLOR);
+ int32_t uniform_loc = GPU_shader_get_builtin_uniform(imm.bound_program, GPU_UNIFORM_COLOR);
BLI_assert(uniform_loc != -1);
- glUniform4f(uniform_loc, r, g, b, a);
+ float data[4] = {r, g, b, a};
+ GPU_shader_uniform_vector(imm.bound_program, uniform_loc, 4, 1, data);
}
void immUniformColor4fv(const float rgba[4])
diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c
index 8df1f94238a..1016e766140 100644
--- a/source/blender/gpu/intern/gpu_material.c
+++ b/source/blender/gpu/intern/gpu_material.c
@@ -47,7 +47,7 @@
#include "GPU_material.h"
#include "GPU_shader.h"
#include "GPU_texture.h"
-#include "GPU_uniformbuffer.h"
+#include "GPU_uniform_buffer.h"
#include "DRW_engine.h"
@@ -88,11 +88,11 @@ struct GPUMaterial {
eGPUMatFlag flag;
/* Used by 2.8 pipeline */
- GPUUniformBuffer *ubo; /* UBOs for shader uniforms. */
+ GPUUniformBuf *ubo; /* UBOs for shader uniforms. */
/* Eevee SSS */
- GPUUniformBuffer *sss_profile; /* UBO containing SSS profile. */
- GPUTexture *sss_tex_profile; /* Texture containing SSS profile. */
+ GPUUniformBuf *sss_profile; /* UBO containing SSS profile. */
+ GPUTexture *sss_tex_profile; /* Texture containing SSS profile. */
float sss_enabled;
float sss_radii[3];
int sss_samples;
@@ -174,13 +174,13 @@ static void gpu_material_free_single(GPUMaterial *material)
GPU_pass_release(material->pass);
}
if (material->ubo != NULL) {
- GPU_uniformbuffer_free(material->ubo);
+ GPU_uniformbuf_free(material->ubo);
}
if (material->sss_tex_profile != NULL) {
GPU_texture_free(material->sss_tex_profile);
}
if (material->sss_profile != NULL) {
- GPU_uniformbuffer_free(material->sss_profile);
+ GPU_uniformbuf_free(material->sss_profile);
}
if (material->coba_tex != NULL) {
GPU_texture_free(material->coba_tex);
@@ -220,7 +220,7 @@ Material *GPU_material_get_material(GPUMaterial *material)
return material->ma;
}
-GPUUniformBuffer *GPU_material_uniform_buffer_get(GPUMaterial *material)
+GPUUniformBuf *GPU_material_uniform_buffer_get(GPUMaterial *material)
{
return material->ubo;
}
@@ -232,7 +232,12 @@ GPUUniformBuffer *GPU_material_uniform_buffer_get(GPUMaterial *material)
*/
void GPU_material_uniform_buffer_create(GPUMaterial *material, ListBase *inputs)
{
- material->ubo = GPU_uniformbuffer_dynamic_create(inputs, NULL);
+#ifndef NDEBUG
+ const char *name = material->name;
+#else
+ const char *name = "Material";
+#endif
+ material->ubo = GPU_uniformbuf_create_from_list(inputs, name);
}
/* Eevee Subsurface scattering. */
@@ -507,13 +512,13 @@ void GPU_material_sss_profile_create(GPUMaterial *material,
/* Update / Create UBO */
if (material->sss_profile == NULL) {
- material->sss_profile = GPU_uniformbuffer_create(sizeof(GPUSssKernelData), NULL, NULL);
+ material->sss_profile = GPU_uniformbuf_create(sizeof(GPUSssKernelData));
}
}
-struct GPUUniformBuffer *GPU_material_sss_profile_get(GPUMaterial *material,
- int sample_len,
- GPUTexture **tex_profile)
+struct GPUUniformBuf *GPU_material_sss_profile_get(GPUMaterial *material,
+ int sample_len,
+ GPUTexture **tex_profile)
{
if (!material->sss_enabled) {
return NULL;
@@ -530,7 +535,7 @@ struct GPUUniformBuffer *GPU_material_sss_profile_get(GPUMaterial *material,
compute_sss_kernel(&kd, material->sss_radii, sample_len, material->sss_falloff, sharpness);
/* Update / Create UBO */
- GPU_uniformbuffer_update(material->sss_profile, &kd);
+ GPU_uniformbuf_update(material->sss_profile, &kd);
/* Update / Create Tex */
float *translucence_profile;
@@ -555,9 +560,9 @@ struct GPUUniformBuffer *GPU_material_sss_profile_get(GPUMaterial *material,
return material->sss_profile;
}
-struct GPUUniformBuffer *GPU_material_create_sss_profile_ubo(void)
+struct GPUUniformBuf *GPU_material_create_sss_profile_ubo(void)
{
- return GPU_uniformbuffer_create(sizeof(GPUSssKernelData), NULL, NULL);
+ return GPU_uniformbuf_create(sizeof(GPUSssKernelData));
}
#undef SSS_EXPONENT
@@ -735,7 +740,7 @@ GPUMaterial *GPU_material_from_nodetree(Scene *scene,
gpu_node_graph_free(&mat->graph);
}
- /* Only free after GPU_pass_shader_get where GPUUniformBuffer
+ /* Only free after GPU_pass_shader_get where GPUUniformBuf
* read data from the local tree. */
ntreeFreeLocalTree(localtree);
MEM_freeN(localtree);
diff --git a/source/blender/gpu/intern/gpu_matrix.cc b/source/blender/gpu/intern/gpu_matrix.cc
index 951652b9393..cdb6d303588 100644
--- a/source/blender/gpu/intern/gpu_matrix.cc
+++ b/source/blender/gpu/intern/gpu_matrix.cc
@@ -21,8 +21,6 @@
* \ingroup gpu
*/
-#include "GPU_shader_interface.h"
-
#include "gpu_context_private.hh"
#include "gpu_matrix_private.h"
@@ -649,14 +647,13 @@ void GPU_matrix_bind(GPUShader *shader)
* call this before a draw call if desired matrices are dirty
* call glUseProgram before this, as glUniform expects program to be bound
*/
- const GPUShaderInterface *shaderface = shader->interface;
- int32_t MV = GPU_shaderinterface_uniform_builtin(shaderface, GPU_UNIFORM_MODELVIEW);
- int32_t P = GPU_shaderinterface_uniform_builtin(shaderface, GPU_UNIFORM_PROJECTION);
- int32_t MVP = GPU_shaderinterface_uniform_builtin(shaderface, GPU_UNIFORM_MVP);
-
- int32_t N = GPU_shaderinterface_uniform_builtin(shaderface, GPU_UNIFORM_NORMAL);
- int32_t MV_inv = GPU_shaderinterface_uniform_builtin(shaderface, GPU_UNIFORM_MODELVIEW_INV);
- int32_t P_inv = GPU_shaderinterface_uniform_builtin(shaderface, GPU_UNIFORM_PROJECTION_INV);
+ int32_t MV = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_MODELVIEW);
+ int32_t P = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_PROJECTION);
+ int32_t MVP = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_MVP);
+
+ int32_t N = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_NORMAL);
+ int32_t MV_inv = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_MODELVIEW_INV);
+ int32_t P_inv = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_PROJECTION_INV);
if (MV != -1) {
GPU_shader_uniform_vector(shader, MV, 16, 1, (const float *)GPU_matrix_model_view_get(NULL));
diff --git a/source/blender/gpu/intern/gpu_node_graph.c b/source/blender/gpu/intern/gpu_node_graph.c
index 81cf2d69f4d..1b8a5e20240 100644
--- a/source/blender/gpu/intern/gpu_node_graph.c
+++ b/source/blender/gpu/intern/gpu_node_graph.c
@@ -592,10 +592,10 @@ bool GPU_stack_link(GPUMaterial *material,
return true;
}
-GPUNodeLink *GPU_uniformbuffer_link_out(GPUMaterial *mat,
- bNode *node,
- GPUNodeStack *stack,
- const int index)
+GPUNodeLink *GPU_uniformbuf_link_out(GPUMaterial *mat,
+ bNode *node,
+ GPUNodeStack *stack,
+ const int index)
{
return gpu_uniformbuffer_link(mat, node, stack, index, SOCK_OUT);
}
diff --git a/source/blender/gpu/intern/gpu_select_pick.c b/source/blender/gpu/intern/gpu_select_pick.c
index 29e2615345c..c3ccb68a998 100644
--- a/source/blender/gpu/intern/gpu_select_pick.c
+++ b/source/blender/gpu/intern/gpu_select_pick.c
@@ -27,6 +27,7 @@
#include <stdlib.h>
#include <string.h>
+#include "GPU_framebuffer.h"
#include "GPU_glew.h"
#include "GPU_immediate.h"
#include "GPU_select.h"
@@ -287,7 +288,7 @@ typedef struct GPUPickState {
int viewport[4];
int scissor[4];
eGPUWriteMask write_mask;
- bool depth_test;
+ eGPUDepthTest depth_test;
} GPUPickState;
static GPUPickState g_pick_state = {0};
@@ -311,18 +312,17 @@ void gpu_select_pick_begin(uint (*buffer)[4], uint bufsize, const rcti *input, c
/* Restrict OpenGL operations for when we don't have cache */
if (ps->is_cached == false) {
ps->write_mask = GPU_write_mask_get();
- ps->depth_test = GPU_depth_test_enabled();
+ ps->depth_test = GPU_depth_test_get();
GPU_scissor_get(ps->scissor);
/* disable writing to the framebuffer */
GPU_color_mask(false, false, false, false);
- glEnable(GL_DEPTH_TEST);
- glDepthMask(GL_TRUE);
+ GPU_depth_mask(true);
/* Always use #GL_LEQUAL even though GPU_SELECT_PICK_ALL always clears the buffer. This is
* because individual objects themselves might have sections that overlap and we need these
* to have the correct distance information. */
- glDepthFunc(GL_LEQUAL);
+ GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
float viewport[4];
GPU_viewport_size_get_f(viewport);
@@ -339,7 +339,7 @@ void gpu_select_pick_begin(uint (*buffer)[4], uint bufsize, const rcti *input, c
/* It's possible we don't want to clear depth buffer,
* so existing elements are masked by current z-buffer. */
- glClear(GL_DEPTH_BUFFER_BIT);
+ GPU_clear_depth(1.0f);
/* scratch buffer (read new values here) */
ps->gl.rect_depth_test = depth_buf_malloc(rect_len);
@@ -518,8 +518,13 @@ bool gpu_select_pick_load_id(uint id, bool end)
SWAP(DepthBufCache *, ps->gl.rect_depth, ps->gl.rect_depth_test);
if (g_pick_state.mode == GPU_SELECT_PICK_ALL) {
+ /* (fclem) This is to be on the safe side. I don't know if this is required. */
+ bool prev_depth_mask = GPU_depth_mask_get();
/* we want new depths every time */
- glClear(GL_DEPTH_BUFFER_BIT);
+ GPU_depth_mask(true);
+ GPU_clear_depth(1.0f);
+
+ GPU_depth_mask(prev_depth_mask);
}
}
}
diff --git a/source/blender/gpu/intern/gpu_select_sample_query.c b/source/blender/gpu/intern/gpu_select_sample_query.c
index 62414febb44..45d52b22664 100644
--- a/source/blender/gpu/intern/gpu_select_sample_query.c
+++ b/source/blender/gpu/intern/gpu_select_sample_query.c
@@ -26,6 +26,7 @@
#include <stdlib.h>
+#include "GPU_framebuffer.h"
#include "GPU_glew.h"
#include "GPU_select.h"
#include "GPU_state.h"
@@ -65,7 +66,7 @@ typedef struct GPUQueryState {
int viewport[4];
int scissor[4];
eGPUWriteMask write_mask;
- bool depth_test;
+ eGPUDepthTest depth_test;
} GPUQueryState;
static GPUQueryState g_query_state = {0};
@@ -91,41 +92,41 @@ void gpu_select_query_begin(
glGenQueries(g_query_state.num_of_queries, g_query_state.queries);
g_query_state.write_mask = GPU_write_mask_get();
- g_query_state.depth_test = GPU_depth_test_enabled();
+ g_query_state.depth_test = GPU_depth_test_get();
GPU_scissor_get(g_query_state.scissor);
+ GPU_viewport_size_get_i(g_query_state.viewport);
- /* disable writing to the framebuffer */
- GPU_color_mask(false, false, false, false);
+ /* Write to color buffer. Seems to fix issues with selecting alpha blended geom (see T7997). */
+ GPU_color_mask(true, true, true, true);
/* In order to save some fill rate we minimize the viewport using rect.
* We need to get the region of the viewport so that our geometry doesn't
* get rejected before the depth test. Should probably cull rect against
* the viewport but this is a rare case I think */
- GPU_viewport_size_get_i(g_query_state.viewport);
- GPU_viewport(g_query_state.viewport[0],
- g_query_state.viewport[1],
- BLI_rcti_size_x(input),
- BLI_rcti_size_y(input));
+
+ int viewport[4] = {
+ UNPACK2(g_query_state.viewport), BLI_rcti_size_x(input), BLI_rcti_size_y(input)};
+
+ GPU_viewport(UNPACK4(viewport));
+ GPU_scissor(UNPACK4(viewport));
+ GPU_scissor_test(false);
/* occlusion queries operates on fragments that pass tests and since we are interested on all
* objects in the view frustum independently of their order, we need to disable the depth test */
if (mode == GPU_SELECT_ALL) {
/* glQueries on Windows+Intel drivers only works with depth testing turned on.
* See T62947 for details */
- glEnable(GL_DEPTH_TEST);
- glDepthFunc(GL_ALWAYS);
- glDepthMask(GL_TRUE);
+ GPU_depth_test(GPU_DEPTH_ALWAYS);
+ GPU_depth_mask(true);
}
else if (mode == GPU_SELECT_NEAREST_FIRST_PASS) {
- glClear(GL_DEPTH_BUFFER_BIT);
- glEnable(GL_DEPTH_TEST);
- glDepthMask(GL_TRUE);
- glDepthFunc(GL_LEQUAL);
+ GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
+ GPU_depth_mask(true);
+ GPU_clear_depth(1.0f);
}
else if (mode == GPU_SELECT_NEAREST_SECOND_PASS) {
- glEnable(GL_DEPTH_TEST);
- glDepthMask(GL_FALSE);
- glDepthFunc(GL_EQUAL);
+ GPU_depth_test(GPU_DEPTH_EQUAL);
+ GPU_depth_mask(false);
}
}
diff --git a/source/blender/gpu/intern/gpu_shader.cc b/source/blender/gpu/intern/gpu_shader.cc
index 536396ad3c6..b1772bed6e8 100644
--- a/source/blender/gpu/intern/gpu_shader.cc
+++ b/source/blender/gpu/intern/gpu_shader.cc
@@ -41,7 +41,7 @@
#include "GPU_platform.h"
#include "GPU_shader.h"
#include "GPU_texture.h"
-#include "GPU_uniformbuffer.h"
+#include "GPU_uniform_buffer.h"
#include "gpu_backend.hh"
#include "gpu_context_private.hh"
@@ -52,6 +52,11 @@ extern "C" char datatoc_gpu_shader_colorspace_lib_glsl[];
using namespace blender;
using namespace blender::gpu;
+/** Opaque type hidding blender::gpu::Shader */
+struct GPUShader {
+ char _pad[1];
+};
+
/* -------------------------------------------------------------------- */
/** \name Debug functions
* \{ */
@@ -196,9 +201,7 @@ Shader::Shader(const char *sh_name)
Shader::~Shader()
{
- if (this->interface) {
- GPU_shaderinterface_discard(this->interface);
- }
+ delete interface;
}
static void standard_defines(Vector<const char *> &sources)
@@ -304,12 +307,12 @@ GPUShader *GPU_shader_create_ex(const char *vertcode,
return NULL;
};
- return static_cast<GPUShader *>(shader);
+ return reinterpret_cast<GPUShader *>(shader);
}
void GPU_shader_free(GPUShader *shader)
{
- delete static_cast<Shader *>(shader);
+ delete reinterpret_cast<Shader *>(shader);
}
/** \} */
@@ -345,7 +348,7 @@ GPUShader *GPU_shader_create_from_python(const char *vertcode,
}
GPUShader *sh = GPU_shader_create_ex(
- vertcode, fragcode, geomcode, libcode, defines, GPU_SHADER_TFB_NONE, NULL, 0, NULL);
+ vertcode, fragcode, geomcode, libcode, defines, GPU_SHADER_TFB_NONE, NULL, 0, "pyGPUShader");
MEM_SAFE_FREE(libcodecat);
return sh;
@@ -431,19 +434,19 @@ struct GPUShader *GPU_shader_create_from_arrays_impl(
void GPU_shader_bind(GPUShader *gpu_shader)
{
- Shader *shader = static_cast<Shader *>(gpu_shader);
+ Shader *shader = reinterpret_cast<Shader *>(gpu_shader);
GPUContext *ctx = GPU_context_active_get();
if (ctx->shader != shader) {
ctx->shader = shader;
shader->bind();
- GPU_matrix_bind(shader);
- GPU_shader_set_srgb_uniform(shader);
+ GPU_matrix_bind(gpu_shader);
+ GPU_shader_set_srgb_uniform(gpu_shader);
}
if (GPU_matrix_dirty_get()) {
- GPU_matrix_bind(shader);
+ GPU_matrix_bind(gpu_shader);
}
}
@@ -452,7 +455,7 @@ void GPU_shader_unbind(void)
#ifndef NDEBUG
GPUContext *ctx = GPU_context_active_get();
if (ctx->shader) {
- static_cast<Shader *>(ctx->shader)->unbind();
+ reinterpret_cast<Shader *>(ctx->shader)->unbind();
}
ctx->shader = NULL;
#endif
@@ -468,12 +471,12 @@ void GPU_shader_unbind(void)
bool GPU_shader_transform_feedback_enable(GPUShader *shader, GPUVertBuf *vertbuf)
{
- return static_cast<Shader *>(shader)->transform_feedback_enable(vertbuf);
+ return reinterpret_cast<Shader *>(shader)->transform_feedback_enable(vertbuf);
}
void GPU_shader_transform_feedback_disable(GPUShader *shader)
{
- static_cast<Shader *>(shader)->transform_feedback_disable();
+ reinterpret_cast<Shader *>(shader)->transform_feedback_disable();
}
/** \} */
@@ -484,43 +487,49 @@ void GPU_shader_transform_feedback_disable(GPUShader *shader)
int GPU_shader_get_uniform(GPUShader *shader, const char *name)
{
- const GPUShaderInput *uniform = GPU_shaderinterface_uniform(shader->interface, name);
+ ShaderInterface *interface = reinterpret_cast<Shader *>(shader)->interface;
+ const ShaderInput *uniform = interface->uniform_get(name);
return uniform ? uniform->location : -1;
}
int GPU_shader_get_builtin_uniform(GPUShader *shader, int builtin)
{
- return GPU_shaderinterface_uniform_builtin(shader->interface,
- static_cast<GPUUniformBuiltin>(builtin));
+ ShaderInterface *interface = reinterpret_cast<Shader *>(shader)->interface;
+ return interface->uniform_builtin((GPUUniformBuiltin)builtin);
}
int GPU_shader_get_builtin_block(GPUShader *shader, int builtin)
{
- return GPU_shaderinterface_block_builtin(shader->interface,
- static_cast<GPUUniformBlockBuiltin>(builtin));
+ ShaderInterface *interface = reinterpret_cast<Shader *>(shader)->interface;
+ return interface->ubo_builtin((GPUUniformBlockBuiltin)builtin);
}
+/* DEPRECATED. */
int GPU_shader_get_uniform_block(GPUShader *shader, const char *name)
{
- const GPUShaderInput *ubo = GPU_shaderinterface_ubo(shader->interface, name);
+ ShaderInterface *interface = reinterpret_cast<Shader *>(shader)->interface;
+ const ShaderInput *ubo = interface->ubo_get(name);
return ubo ? ubo->location : -1;
}
int GPU_shader_get_uniform_block_binding(GPUShader *shader, const char *name)
{
- const GPUShaderInput *ubo = GPU_shaderinterface_ubo(shader->interface, name);
+ ShaderInterface *interface = reinterpret_cast<Shader *>(shader)->interface;
+ const ShaderInput *ubo = interface->ubo_get(name);
return ubo ? ubo->binding : -1;
}
int GPU_shader_get_texture_binding(GPUShader *shader, const char *name)
{
- const GPUShaderInput *tex = GPU_shaderinterface_uniform(shader->interface, name);
+ ShaderInterface *interface = reinterpret_cast<Shader *>(shader)->interface;
+ const ShaderInput *tex = interface->uniform_get(name);
return tex ? tex->binding : -1;
}
int GPU_shader_get_attribute(GPUShader *shader, const char *name)
{
- const GPUShaderInput *attr = GPU_shaderinterface_attr(shader->interface, name);
+ ShaderInterface *interface = reinterpret_cast<Shader *>(shader)->interface;
+ const ShaderInput *attr = interface->attr_get(name);
return attr ? attr->location : -1;
}
@@ -546,13 +555,13 @@ int GPU_shader_get_program(GPUShader *UNUSED(shader))
void GPU_shader_uniform_vector(
GPUShader *shader, int loc, int len, int arraysize, const float *value)
{
- static_cast<Shader *>(shader)->uniform_float(loc, len, arraysize, value);
+ reinterpret_cast<Shader *>(shader)->uniform_float(loc, len, arraysize, value);
}
void GPU_shader_uniform_vector_int(
GPUShader *shader, int loc, int len, int arraysize, const int *value)
{
- static_cast<Shader *>(shader)->uniform_int(loc, len, arraysize, value);
+ reinterpret_cast<Shader *>(shader)->uniform_int(loc, len, arraysize, value);
}
void GPU_shader_uniform_int(GPUShader *shader, int location, int value)
@@ -565,14 +574,10 @@ void GPU_shader_uniform_float(GPUShader *shader, int location, float value)
GPU_shader_uniform_vector(shader, location, 1, 1, &value);
}
-#define GET_UNIFORM \
- const GPUShaderInput *uniform = GPU_shaderinterface_uniform(sh->interface, name); \
- BLI_assert(uniform);
-
void GPU_shader_uniform_1i(GPUShader *sh, const char *name, int value)
{
- GET_UNIFORM
- GPU_shader_uniform_int(sh, uniform->location, value);
+ const int loc = GPU_shader_get_uniform(sh, name);
+ GPU_shader_uniform_int(sh, loc, value);
}
void GPU_shader_uniform_1b(GPUShader *sh, const char *name, bool value)
@@ -600,44 +605,44 @@ void GPU_shader_uniform_4f(GPUShader *sh, const char *name, float x, float y, fl
void GPU_shader_uniform_1f(GPUShader *sh, const char *name, float x)
{
- GET_UNIFORM
- GPU_shader_uniform_float(sh, uniform->location, x);
+ const int loc = GPU_shader_get_uniform(sh, name);
+ GPU_shader_uniform_float(sh, loc, x);
}
void GPU_shader_uniform_2fv(GPUShader *sh, const char *name, const float data[2])
{
- GET_UNIFORM
- GPU_shader_uniform_vector(sh, uniform->location, 2, 1, data);
+ const int loc = GPU_shader_get_uniform(sh, name);
+ GPU_shader_uniform_vector(sh, loc, 2, 1, data);
}
void GPU_shader_uniform_3fv(GPUShader *sh, const char *name, const float data[3])
{
- GET_UNIFORM
- GPU_shader_uniform_vector(sh, uniform->location, 3, 1, data);
+ const int loc = GPU_shader_get_uniform(sh, name);
+ GPU_shader_uniform_vector(sh, loc, 3, 1, data);
}
void GPU_shader_uniform_4fv(GPUShader *sh, const char *name, const float data[4])
{
- GET_UNIFORM
- GPU_shader_uniform_vector(sh, uniform->location, 4, 1, data);
+ const int loc = GPU_shader_get_uniform(sh, name);
+ GPU_shader_uniform_vector(sh, loc, 4, 1, data);
}
void GPU_shader_uniform_mat4(GPUShader *sh, const char *name, const float data[4][4])
{
- GET_UNIFORM
- GPU_shader_uniform_vector(sh, uniform->location, 16, 1, (const float *)data);
+ const int loc = GPU_shader_get_uniform(sh, name);
+ GPU_shader_uniform_vector(sh, loc, 16, 1, (const float *)data);
}
void GPU_shader_uniform_2fv_array(GPUShader *sh, const char *name, int len, const float (*val)[2])
{
- GET_UNIFORM
- GPU_shader_uniform_vector(sh, uniform->location, 2, len, (const float *)val);
+ const int loc = GPU_shader_get_uniform(sh, name);
+ GPU_shader_uniform_vector(sh, loc, 2, len, (const float *)val);
}
void GPU_shader_uniform_4fv_array(GPUShader *sh, const char *name, int len, const float (*val)[4])
{
- GET_UNIFORM
- GPU_shader_uniform_vector(sh, uniform->location, 4, len, (const float *)val);
+ const int loc = GPU_shader_get_uniform(sh, name);
+ GPU_shader_uniform_vector(sh, loc, 4, len, (const float *)val);
}
/** \} */
@@ -657,7 +662,7 @@ static int g_shader_builtin_srgb_transform = 0;
void GPU_shader_set_srgb_uniform(GPUShader *shader)
{
- int32_t loc = GPU_shaderinterface_uniform_builtin(shader->interface, GPU_UNIFORM_SRGB_TRANSFORM);
+ int32_t loc = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_SRGB_TRANSFORM);
if (loc != -1) {
GPU_shader_uniform_vector_int(shader, loc, 1, 1, &g_shader_builtin_srgb_transform);
}
diff --git a/source/blender/gpu/intern/gpu_shader_builtin.c b/source/blender/gpu/intern/gpu_shader_builtin.c
index da5bcaeca17..ed95a236da5 100644
--- a/source/blender/gpu/intern/gpu_shader_builtin.c
+++ b/source/blender/gpu/intern/gpu_shader_builtin.c
@@ -40,7 +40,7 @@
#include "GPU_platform.h"
#include "GPU_shader.h"
#include "GPU_texture.h"
-#include "GPU_uniformbuffer.h"
+#include "GPU_uniform_buffer.h"
/* Adjust these constants as needed. */
#define MAX_DEFINE_LENGTH 256
diff --git a/source/blender/gpu/intern/gpu_shader_interface.cc b/source/blender/gpu/intern/gpu_shader_interface.cc
index ef90dde1877..dc59dca9f78 100644
--- a/source/blender/gpu/intern/gpu_shader_interface.cc
+++ b/source/blender/gpu/intern/gpu_shader_interface.cc
@@ -23,161 +23,41 @@
* GPU shader interface (C --> GLSL)
*/
-#include "BKE_global.h"
-
-#include "BLI_bitmap.h"
-#include "BLI_math_base.h"
-
#include "MEM_guardedalloc.h"
-#include "GPU_shader_interface.h"
-
-#include "gpu_batch_private.hh"
-#include "gpu_context_private.hh"
-
-#include "gl_batch.hh"
-
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-
-#define DEBUG_SHADER_INTERFACE 0
-
-#if DEBUG_SHADER_INTERFACE
-# include <stdio.h>
-#endif
-
-using namespace blender::gpu;
-
-static const char *BuiltinUniform_name(GPUUniformBuiltin u)
-{
- switch (u) {
- case GPU_UNIFORM_MODEL:
- return "ModelMatrix";
- case GPU_UNIFORM_VIEW:
- return "ViewMatrix";
- case GPU_UNIFORM_MODELVIEW:
- return "ModelViewMatrix";
- case GPU_UNIFORM_PROJECTION:
- return "ProjectionMatrix";
- case GPU_UNIFORM_VIEWPROJECTION:
- return "ViewProjectionMatrix";
- case GPU_UNIFORM_MVP:
- return "ModelViewProjectionMatrix";
-
- case GPU_UNIFORM_MODEL_INV:
- return "ModelMatrixInverse";
- case GPU_UNIFORM_VIEW_INV:
- return "ViewMatrixInverse";
- case GPU_UNIFORM_MODELVIEW_INV:
- return "ModelViewMatrixInverse";
- case GPU_UNIFORM_PROJECTION_INV:
- return "ProjectionMatrixInverse";
- case GPU_UNIFORM_VIEWPROJECTION_INV:
- return "ViewProjectionMatrixInverse";
-
- case GPU_UNIFORM_NORMAL:
- return "NormalMatrix";
- case GPU_UNIFORM_ORCO:
- return "OrcoTexCoFactors";
- case GPU_UNIFORM_CLIPPLANES:
- return "WorldClipPlanes";
-
- case GPU_UNIFORM_COLOR:
- return "color";
- case GPU_UNIFORM_BASE_INSTANCE:
- return "baseInstance";
- case GPU_UNIFORM_RESOURCE_CHUNK:
- return "resourceChunk";
- case GPU_UNIFORM_RESOURCE_ID:
- return "resourceId";
- case GPU_UNIFORM_SRGB_TRANSFORM:
- return "srgbTarget";
+#include "BLI_span.hh"
+#include "BLI_vector.hh"
- default:
- return NULL;
- }
-}
+#include "gpu_shader_interface.hh"
-static const char *BuiltinUniformBlock_name(GPUUniformBlockBuiltin u)
-{
- switch (u) {
- case GPU_UNIFORM_BLOCK_VIEW:
- return "viewBlock";
- case GPU_UNIFORM_BLOCK_MODEL:
- return "modelBlock";
- case GPU_UNIFORM_BLOCK_INFO:
- return "infoBlock";
- default:
- return NULL;
- }
-}
+namespace blender::gpu {
-GPU_INLINE bool match(const char *a, const char *b)
+ShaderInterface::ShaderInterface(void)
{
- return STREQ(a, b);
+ /* TODO(fclem) add unique ID for debugging. */
}
-GPU_INLINE uint hash_string(const char *str)
+ShaderInterface::~ShaderInterface(void)
{
- uint i = 0, c;
- while ((c = *str++)) {
- i = i * 37 + c;
- }
- return i;
+ /* Free memory used by name_buffer. */
+ MEM_freeN(name_buffer_);
+ MEM_freeN(inputs_);
}
-GPU_INLINE uint32_t set_input_name(GPUShaderInterface *shaderface,
- GPUShaderInput *input,
- char *name,
- uint32_t name_len)
+static void sort_input_list(MutableSpan<ShaderInput> dst)
{
- /* remove "[0]" from array name */
- if (name[name_len - 1] == ']') {
- name[name_len - 3] = '\0';
- name_len -= 3;
+ if (dst.size() == 0) {
+ return;
}
- input->name_offset = (uint32_t)(name - shaderface->name_buffer);
- input->name_hash = hash_string(name);
- return name_len + 1; /* include NULL terminator */
-}
-
-GPU_INLINE const GPUShaderInput *input_lookup(const GPUShaderInterface *shaderface,
- const GPUShaderInput *const inputs,
- const uint inputs_len,
- const char *name)
-{
- const uint name_hash = hash_string(name);
- /* Simple linear search for now. */
- for (int i = inputs_len - 1; i >= 0; i--) {
- if (inputs[i].name_hash == name_hash) {
- if ((i > 0) && UNLIKELY(inputs[i - 1].name_hash == name_hash)) {
- /* Hash colision resolve. */
- for (; i >= 0 && inputs[i].name_hash == name_hash; i--) {
- if (match(name, shaderface->name_buffer + inputs[i].name_offset)) {
- return inputs + i; /* not found */
- }
- }
- return NULL; /* not found */
- }
-
- /* This is a bit dangerous since we could have a hash collision.
- * where the asked uniform that does not exist has the same hash
- * as a real uniform. */
- BLI_assert(match(name, shaderface->name_buffer + inputs[i].name_offset));
- return inputs + i;
- }
- }
- return NULL; /* not found */
-}
+ Vector<ShaderInput> inputs_vec = Vector<ShaderInput>(dst.size());
+ MutableSpan<ShaderInput> src = inputs_vec.as_mutable_span();
+ src.copy_from(dst);
-/* Note that this modify the src array. */
-GPU_INLINE void sort_input_list(GPUShaderInput *dst, GPUShaderInput *src, const uint input_len)
-{
- for (uint i = 0; i < input_len; i++) {
- GPUShaderInput *input_src = &src[0];
- for (uint j = 1; j < input_len; j++) {
+ /* Simple sorting by going through the array and selecting the biggest element each time. */
+ for (uint i = 0; i < dst.size(); i++) {
+ ShaderInput *input_src = &src[0];
+ for (uint j = 1; j < src.size(); j++) {
if (src[j].name_hash > input_src->name_hash) {
input_src = &src[j];
}
@@ -187,360 +67,60 @@ GPU_INLINE void sort_input_list(GPUShaderInput *dst, GPUShaderInput *src, const
}
}
-static int block_binding(int32_t program, uint32_t block_index)
+/* Sorts all inputs inside their respective array.
+ * This is to allow fast hash collision detection.
+ * See ShaderInterface::input_lookup for more details. */
+void ShaderInterface::sort_inputs(void)
{
- /* For now just assign a consecutive index. In the future, we should set it in
- * the shader using layout(binding = i) and query its value. */
- glUniformBlockBinding(program, block_index, block_index);
- return block_index;
+ sort_input_list(MutableSpan<ShaderInput>(inputs_, attr_len_));
+ sort_input_list(MutableSpan<ShaderInput>(inputs_ + attr_len_, ubo_len_));
+ sort_input_list(MutableSpan<ShaderInput>(inputs_ + attr_len_ + ubo_len_, uniform_len_));
}
-static int sampler_binding(int32_t program,
- uint32_t uniform_index,
- int32_t uniform_location,
- int *sampler_len)
+void ShaderInterface::debug_print(void)
{
- /* Identify sampler uniforms and asign sampler units to them. */
- GLint type;
- glGetActiveUniformsiv(program, 1, &uniform_index, GL_UNIFORM_TYPE, &type);
-
- switch (type) {
- case GL_SAMPLER_1D:
- case GL_SAMPLER_2D:
- case GL_SAMPLER_3D:
- case GL_SAMPLER_CUBE:
- case GL_SAMPLER_CUBE_MAP_ARRAY_ARB: /* OpenGL 4.0 */
- case GL_SAMPLER_1D_SHADOW:
- case GL_SAMPLER_2D_SHADOW:
- case GL_SAMPLER_1D_ARRAY:
- case GL_SAMPLER_2D_ARRAY:
- case GL_SAMPLER_1D_ARRAY_SHADOW:
- case GL_SAMPLER_2D_ARRAY_SHADOW:
- case GL_SAMPLER_2D_MULTISAMPLE:
- case GL_SAMPLER_2D_MULTISAMPLE_ARRAY:
- case GL_SAMPLER_CUBE_SHADOW:
- case GL_SAMPLER_BUFFER:
- case GL_INT_SAMPLER_1D:
- case GL_INT_SAMPLER_2D:
- case GL_INT_SAMPLER_3D:
- case GL_INT_SAMPLER_CUBE:
- case GL_INT_SAMPLER_1D_ARRAY:
- case GL_INT_SAMPLER_2D_ARRAY:
- case GL_INT_SAMPLER_2D_MULTISAMPLE:
- case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
- case GL_INT_SAMPLER_BUFFER:
- case GL_UNSIGNED_INT_SAMPLER_1D:
- case GL_UNSIGNED_INT_SAMPLER_2D:
- case GL_UNSIGNED_INT_SAMPLER_3D:
- case GL_UNSIGNED_INT_SAMPLER_CUBE:
- case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY:
- case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
- case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE:
- case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
- case GL_UNSIGNED_INT_SAMPLER_BUFFER: {
- /* For now just assign a consecutive index. In the future, we should set it in
- * the shader using layout(binding = i) and query its value. */
- int binding = *sampler_len;
- glUniform1i(uniform_location, binding);
- (*sampler_len)++;
- return binding;
- }
- default:
- return -1;
- }
-}
+ Span<ShaderInput> attrs = Span<ShaderInput>(inputs_, attr_len_);
+ Span<ShaderInput> ubos = Span<ShaderInput>(inputs_ + attr_len_, ubo_len_);
+ Span<ShaderInput> uniforms = Span<ShaderInput>(inputs_ + attr_len_ + ubo_len_, uniform_len_);
+ char *name_buf = name_buffer_;
+ const char format[] = " | %.8x : %4d : %s\n";
-GPUShaderInterface *GPU_shaderinterface_create(int32_t program)
-{
-#ifndef NDEBUG
- GLint curr_program;
- glGetIntegerv(GL_CURRENT_PROGRAM, &curr_program);
- BLI_assert(curr_program == program);
-#endif
-
- GLint max_attr_name_len = 0, attr_len = 0;
- glGetProgramiv(program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &max_attr_name_len);
- glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &attr_len);
-
- GLint max_ubo_name_len = 0, ubo_len = 0;
- glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, &max_ubo_name_len);
- glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCKS, &ubo_len);
-
- GLint max_uniform_name_len = 0, active_uniform_len = 0, uniform_len = 0;
- glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &max_uniform_name_len);
- glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &active_uniform_len);
- uniform_len = active_uniform_len;
-
- /* Work around driver bug with Intel HD 4600 on Windows 7/8, where
- * GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH does not work. */
- if (attr_len > 0 && max_attr_name_len == 0) {
- max_attr_name_len = 256;
- }
- if (ubo_len > 0 && max_ubo_name_len == 0) {
- max_ubo_name_len = 256;
+ printf(" \033[1mGPUShaderInterface : \033[0m\n");
+ if (attrs.size() > 0) {
+ printf("\n Attributes :\n");
}
- if (uniform_len > 0 && max_uniform_name_len == 0) {
- max_uniform_name_len = 256;
+ for (const ShaderInput &attr : attrs) {
+ printf(format, attr.name_hash, attr.location, name_buf + attr.name_offset);
}
- /* GL_ACTIVE_UNIFORMS lied to us! Remove the UBO uniforms from the total before
- * allocating the uniform array. */
- GLint max_ubo_uni_len = 0;
- for (int i = 0; i < ubo_len; i++) {
- GLint ubo_uni_len;
- glGetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &ubo_uni_len);
- max_ubo_uni_len = max_ii(max_ubo_uni_len, ubo_uni_len);
- uniform_len -= ubo_uni_len;
+ if (uniforms.size() > 0) {
+ printf("\n Uniforms :\n");
}
- /* Bit set to true if uniform comes from a uniform block. */
- BLI_bitmap *uniforms_from_blocks = BLI_BITMAP_NEW(active_uniform_len, __func__);
- /* Set uniforms from block for exclusion. */
- GLint *ubo_uni_ids = (GLint *)MEM_mallocN(sizeof(GLint) * max_ubo_uni_len, __func__);
- for (int i = 0; i < ubo_len; i++) {
- GLint ubo_uni_len;
- glGetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &ubo_uni_len);
- glGetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, ubo_uni_ids);
- for (int u = 0; u < ubo_uni_len; u++) {
- BLI_BITMAP_ENABLE(uniforms_from_blocks, ubo_uni_ids[u]);
+ for (const ShaderInput &uni : uniforms) {
+ /* Bypass samplers. */
+ if (uni.binding == -1) {
+ printf(format, uni.name_hash, uni.location, name_buf + uni.name_offset);
}
}
- MEM_freeN(ubo_uni_ids);
- uint32_t name_buffer_offset = 0;
- const uint32_t name_buffer_len = attr_len * max_attr_name_len + ubo_len * max_ubo_name_len +
- uniform_len * max_uniform_name_len;
-
- int input_tot_len = attr_len + ubo_len + uniform_len;
- size_t interface_size = sizeof(GPUShaderInterface) + sizeof(GPUShaderInput) * input_tot_len;
-
- GPUShaderInterface *shaderface = (GPUShaderInterface *)MEM_callocN(interface_size,
- "GPUShaderInterface");
- shaderface->attribute_len = attr_len;
- shaderface->ubo_len = ubo_len;
- shaderface->uniform_len = uniform_len;
- shaderface->name_buffer = (char *)MEM_mallocN(name_buffer_len, "name_buffer");
- GPUShaderInput *inputs = shaderface->inputs;
-
- /* Temp buffer. */
- int input_tmp_len = max_iii(attr_len, ubo_len, uniform_len);
- GPUShaderInput *inputs_tmp = (GPUShaderInput *)MEM_mallocN(
- sizeof(GPUShaderInput) * input_tmp_len, "name_buffer");
-
- /* Attributes */
- shaderface->enabled_attr_mask = 0;
- for (int i = 0, idx = 0; i < attr_len; i++) {
- char *name = shaderface->name_buffer + name_buffer_offset;
- GLsizei remaining_buffer = name_buffer_len - name_buffer_offset;
- GLsizei name_len = 0;
- GLenum type;
- GLint size;
-
- glGetActiveAttrib(program, i, remaining_buffer, &name_len, &size, &type, name);
- GLint location = glGetAttribLocation(program, name);
- /* Ignore OpenGL names like `gl_BaseInstanceARB`, `gl_InstanceID` and `gl_VertexID`. */
- if (location == -1) {
- shaderface->attribute_len--;
- continue;
- }
-
- GPUShaderInput *input = &inputs_tmp[idx++];
- input->location = input->binding = location;
-
- name_buffer_offset += set_input_name(shaderface, input, name, name_len);
- shaderface->enabled_attr_mask |= (1 << input->location);
+ if (ubos.size() > 0) {
+ printf("\n Uniform Buffer Objects :\n");
}
- sort_input_list(inputs, inputs_tmp, shaderface->attribute_len);
- inputs += shaderface->attribute_len;
-
- /* Uniform Blocks */
- for (int i = 0, idx = 0; i < ubo_len; i++) {
- char *name = shaderface->name_buffer + name_buffer_offset;
- GLsizei remaining_buffer = name_buffer_len - name_buffer_offset;
- GLsizei name_len = 0;
-
- glGetActiveUniformBlockName(program, i, remaining_buffer, &name_len, name);
-
- GPUShaderInput *input = &inputs_tmp[idx++];
- input->binding = input->location = block_binding(program, i);
-
- name_buffer_offset += set_input_name(shaderface, input, name, name_len);
- shaderface->enabled_ubo_mask |= (1 << input->binding);
+ for (const ShaderInput &ubo : ubos) {
+ printf(format, ubo.name_hash, ubo.binding, name_buf + ubo.name_offset);
}
- sort_input_list(inputs, inputs_tmp, shaderface->ubo_len);
- inputs += shaderface->ubo_len;
- /* Uniforms */
- for (int i = 0, idx = 0, sampler = 0; i < active_uniform_len; i++) {
- if (BLI_BITMAP_TEST(uniforms_from_blocks, i)) {
- continue;
- }
- char *name = shaderface->name_buffer + name_buffer_offset;
- GLsizei remaining_buffer = name_buffer_len - name_buffer_offset;
- GLsizei name_len = 0;
-
- glGetActiveUniformName(program, i, remaining_buffer, &name_len, name);
-
- GPUShaderInput *input = &inputs_tmp[idx++];
- input->location = glGetUniformLocation(program, name);
- input->binding = sampler_binding(program, i, input->location, &sampler);
-
- name_buffer_offset += set_input_name(shaderface, input, name, name_len);
- shaderface->enabled_tex_mask |= (input->binding != -1) ? (1lu << input->binding) : 0lu;
- }
- sort_input_list(inputs, inputs_tmp, shaderface->uniform_len);
-
- /* Builtin Uniforms */
- for (int32_t u_int = 0; u_int < GPU_NUM_UNIFORMS; u_int++) {
- GPUUniformBuiltin u = static_cast<GPUUniformBuiltin>(u_int);
- shaderface->builtins[u] = glGetUniformLocation(program, BuiltinUniform_name(u));
- }
-
- /* Builtin Uniforms Blocks */
- for (int32_t u_int = 0; u_int < GPU_NUM_UNIFORM_BLOCKS; u_int++) {
- GPUUniformBlockBuiltin u = static_cast<GPUUniformBlockBuiltin>(u_int);
- const GPUShaderInput *block = GPU_shaderinterface_ubo(shaderface, BuiltinUniformBlock_name(u));
- shaderface->builtin_blocks[u] = (block != NULL) ? block->binding : -1;
- }
-
- /* Batches ref buffer */
- shaderface->batches_len = GPU_SHADERINTERFACE_REF_ALLOC_COUNT;
- shaderface->batches = (void **)MEM_callocN(shaderface->batches_len * sizeof(GPUBatch *),
- "GPUShaderInterface batches");
-
- MEM_freeN(uniforms_from_blocks);
- MEM_freeN(inputs_tmp);
-
- /* Resize name buffer to save some memory. */
- if (name_buffer_offset < name_buffer_len) {
- shaderface->name_buffer = (char *)MEM_reallocN(shaderface->name_buffer, name_buffer_offset);
- }
-
-#if DEBUG_SHADER_INTERFACE
- char *name_buf = shaderface->name_buffer;
- printf("--- GPUShaderInterface %p, program %d ---\n", shaderface, program);
- if (shaderface->attribute_len > 0) {
- printf("Attributes {\n");
- for (int i = 0; i < shaderface->attribute_len; i++) {
- GPUShaderInput *input = shaderface->inputs + i;
- printf("\t(location = %d) %s;\n", input->location, name_buf + input->name_offset);
- }
- printf("};\n");
- }
- if (shaderface->ubo_len > 0) {
- printf("Uniform Buffer Objects {\n");
- for (int i = 0; i < shaderface->ubo_len; i++) {
- GPUShaderInput *input = shaderface->inputs + shaderface->attribute_len + i;
- printf("\t(binding = %d) %s;\n", input->binding, name_buf + input->name_offset);
- }
- printf("};\n");
+ if (enabled_tex_mask_ > 0) {
+ printf("\n Samplers :\n");
}
- if (shaderface->enabled_tex_mask > 0) {
- printf("Samplers {\n");
- for (int i = 0; i < shaderface->uniform_len; i++) {
- GPUShaderInput *input = shaderface->inputs + shaderface->attribute_len +
- shaderface->ubo_len + i;
- if (input->binding != -1) {
- printf("\t(location = %d, binding = %d) %s;\n",
- input->location,
- input->binding,
- name_buf + input->name_offset);
- }
- }
- printf("};\n");
- }
- if (shaderface->uniform_len > 0) {
- printf("Uniforms {\n");
- for (int i = 0; i < shaderface->uniform_len; i++) {
- GPUShaderInput *input = shaderface->inputs + shaderface->attribute_len +
- shaderface->ubo_len + i;
- if (input->binding == -1) {
- printf("\t(location = %d) %s;\n", input->location, name_buf + input->name_offset);
- }
+ for (const ShaderInput &samp : uniforms) {
+ /* Bypass uniforms. */
+ if (samp.binding != -1) {
+ printf(format, samp.name_hash, samp.binding, name_buf + samp.name_offset);
}
- printf("};\n");
}
- printf("--- GPUShaderInterface end ---\n\n");
-#endif
- return shaderface;
-}
-
-void GPU_shaderinterface_discard(GPUShaderInterface *shaderface)
-{
- /* Free memory used by name_buffer. */
- MEM_freeN(shaderface->name_buffer);
- /* Remove this interface from all linked Batches vao cache. */
- for (int i = 0; i < shaderface->batches_len; i++) {
- if (shaderface->batches[i] != NULL) {
- /* XXX GL specific. to be removed during refactor. */
- reinterpret_cast<GLVaoCache *>(shaderface->batches[i])->remove(shaderface);
- }
- }
- MEM_freeN(shaderface->batches);
- /* Free memory used by shader interface by its self. */
- MEM_freeN(shaderface);
+ printf("\n");
}
-const GPUShaderInput *GPU_shaderinterface_attr(const GPUShaderInterface *shaderface,
- const char *name)
-{
- uint ofs = 0;
- return input_lookup(shaderface, shaderface->inputs + ofs, shaderface->attribute_len, name);
-}
-
-const GPUShaderInput *GPU_shaderinterface_ubo(const GPUShaderInterface *shaderface,
- const char *name)
-{
- uint ofs = shaderface->attribute_len;
- return input_lookup(shaderface, shaderface->inputs + ofs, shaderface->ubo_len, name);
-}
-
-const GPUShaderInput *GPU_shaderinterface_uniform(const GPUShaderInterface *shaderface,
- const char *name)
-{
- uint ofs = shaderface->attribute_len + shaderface->ubo_len;
- return input_lookup(shaderface, shaderface->inputs + ofs, shaderface->uniform_len, name);
-}
-
-int32_t GPU_shaderinterface_uniform_builtin(const GPUShaderInterface *shaderface,
- GPUUniformBuiltin builtin)
-{
- BLI_assert(builtin >= 0 && builtin < GPU_NUM_UNIFORMS);
- return shaderface->builtins[builtin];
-}
-
-int32_t GPU_shaderinterface_block_builtin(const GPUShaderInterface *shaderface,
- GPUUniformBlockBuiltin builtin)
-{
- BLI_assert(builtin >= 0 && builtin < GPU_NUM_UNIFORM_BLOCKS);
- return shaderface->builtin_blocks[builtin];
-}
-
-void GPU_shaderinterface_add_batch_ref(GPUShaderInterface *shaderface, void *batch)
-{
- int i; /* find first unused slot */
- for (i = 0; i < shaderface->batches_len; i++) {
- if (shaderface->batches[i] == NULL) {
- break;
- }
- }
- if (i == shaderface->batches_len) {
- /* Not enough place, realloc the array. */
- i = shaderface->batches_len;
- shaderface->batches_len += GPU_SHADERINTERFACE_REF_ALLOC_COUNT;
- shaderface->batches = (void **)MEM_recallocN(shaderface->batches,
- sizeof(void *) * shaderface->batches_len);
- }
- /** XXX todo cleanup. */
- shaderface->batches[i] = reinterpret_cast<void *>(batch);
-}
-
-void GPU_shaderinterface_remove_batch_ref(GPUShaderInterface *shaderface, void *batch)
-{
- for (int i = 0; i < shaderface->batches_len; i++) {
- if (shaderface->batches[i] == batch) {
- shaderface->batches[i] = NULL;
- break; /* cannot have duplicates */
- }
- }
-}
+} // namespace blender::gpu
diff --git a/source/blender/gpu/intern/gpu_shader_interface.hh b/source/blender/gpu/intern/gpu_shader_interface.hh
new file mode 100644
index 00000000000..6e1cb342c68
--- /dev/null
+++ b/source/blender/gpu/intern/gpu_shader_interface.hh
@@ -0,0 +1,229 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2016 by Mike Erwin.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup gpu
+ *
+ * GPU shader interface (C --> GLSL)
+ *
+ * Structure detailing needed vertex inputs and resources for a specific shader.
+ * A shader interface can be shared between two similar shaders.
+ */
+
+#pragma once
+
+#include <cstring> /* required for STREQ later on. */
+
+#include "BLI_hash.h"
+#include "BLI_utildefines.h"
+
+#include "GPU_shader.h"
+
+namespace blender::gpu {
+
+typedef struct ShaderInput {
+ uint32_t name_offset;
+ uint32_t name_hash;
+ int32_t location;
+ /** Defined at interface creation or in shader. Only for Samplers, UBOs and Vertex Attribs. */
+ int32_t binding;
+} ShaderInput;
+
+/**
+ * Implementation of Shader interface.
+ * Base class which is then specialized for each implementation (GL, VK, ...).
+ **/
+class ShaderInterface {
+ /* TODO(fclem) should be protected. */
+ public:
+ /** Flat array. In this order: Attributes, Ubos, Uniforms. */
+ ShaderInput *inputs_ = NULL;
+ /** Buffer containing all inputs names separated by '\0'. */
+ char *name_buffer_ = NULL;
+ /** Input counts inside input array. */
+ uint attr_len_ = 0;
+ uint ubo_len_ = 0;
+ uint uniform_len_ = 0;
+ /** Enabled bindpoints that needs to be fed with data. */
+ uint16_t enabled_attr_mask_ = 0;
+ uint16_t enabled_ubo_mask_ = 0;
+ uint64_t enabled_tex_mask_ = 0;
+ /** Location of builtin uniforms. Fast access, no lookup needed. */
+ int32_t builtins_[GPU_NUM_UNIFORMS];
+ int32_t builtin_blocks_[GPU_NUM_UNIFORM_BLOCKS];
+
+ public:
+ ShaderInterface();
+ virtual ~ShaderInterface();
+
+ void debug_print(void);
+
+ inline const ShaderInput *attr_get(const char *name) const
+ {
+ return input_lookup(inputs_, attr_len_, name);
+ }
+
+ inline const ShaderInput *ubo_get(const char *name) const
+ {
+ return input_lookup(inputs_ + attr_len_, ubo_len_, name);
+ }
+
+ inline const ShaderInput *uniform_get(const char *name) const
+ {
+ return input_lookup(inputs_ + attr_len_ + ubo_len_, uniform_len_, name);
+ }
+
+ /* Returns uniform location. */
+ inline int32_t uniform_builtin(const GPUUniformBuiltin builtin) const
+ {
+ BLI_assert(builtin >= 0 && builtin < GPU_NUM_UNIFORMS);
+ return builtins_[builtin];
+ }
+
+ /* Returns binding position. */
+ inline int32_t ubo_builtin(const GPUUniformBlockBuiltin builtin) const
+ {
+ BLI_assert(builtin >= 0 && builtin < GPU_NUM_UNIFORM_BLOCKS);
+ return builtin_blocks_[builtin];
+ }
+
+ protected:
+ static inline const char *builtin_uniform_name(GPUUniformBuiltin u);
+ static inline const char *builtin_uniform_block_name(GPUUniformBlockBuiltin u);
+
+ inline uint32_t set_input_name(ShaderInput *input, char *name, uint32_t name_len) const;
+
+ /* Finalize interface construction by sorting the ShaderInputs for faster lookups. */
+ void sort_inputs(void);
+
+ private:
+ inline const ShaderInput *input_lookup(const ShaderInput *const inputs,
+ const uint inputs_len,
+ const char *name) const;
+};
+
+inline const char *ShaderInterface::builtin_uniform_name(GPUUniformBuiltin u)
+{
+ switch (u) {
+ case GPU_UNIFORM_MODEL:
+ return "ModelMatrix";
+ case GPU_UNIFORM_VIEW:
+ return "ViewMatrix";
+ case GPU_UNIFORM_MODELVIEW:
+ return "ModelViewMatrix";
+ case GPU_UNIFORM_PROJECTION:
+ return "ProjectionMatrix";
+ case GPU_UNIFORM_VIEWPROJECTION:
+ return "ViewProjectionMatrix";
+ case GPU_UNIFORM_MVP:
+ return "ModelViewProjectionMatrix";
+
+ case GPU_UNIFORM_MODEL_INV:
+ return "ModelMatrixInverse";
+ case GPU_UNIFORM_VIEW_INV:
+ return "ViewMatrixInverse";
+ case GPU_UNIFORM_MODELVIEW_INV:
+ return "ModelViewMatrixInverse";
+ case GPU_UNIFORM_PROJECTION_INV:
+ return "ProjectionMatrixInverse";
+ case GPU_UNIFORM_VIEWPROJECTION_INV:
+ return "ViewProjectionMatrixInverse";
+
+ case GPU_UNIFORM_NORMAL:
+ return "NormalMatrix";
+ case GPU_UNIFORM_ORCO:
+ return "OrcoTexCoFactors";
+ case GPU_UNIFORM_CLIPPLANES:
+ return "WorldClipPlanes";
+
+ case GPU_UNIFORM_COLOR:
+ return "color";
+ case GPU_UNIFORM_BASE_INSTANCE:
+ return "baseInstance";
+ case GPU_UNIFORM_RESOURCE_CHUNK:
+ return "resourceChunk";
+ case GPU_UNIFORM_RESOURCE_ID:
+ return "resourceId";
+ case GPU_UNIFORM_SRGB_TRANSFORM:
+ return "srgbTarget";
+
+ default:
+ return NULL;
+ }
+}
+
+inline const char *ShaderInterface::builtin_uniform_block_name(GPUUniformBlockBuiltin u)
+{
+ switch (u) {
+ case GPU_UNIFORM_BLOCK_VIEW:
+ return "viewBlock";
+ case GPU_UNIFORM_BLOCK_MODEL:
+ return "modelBlock";
+ case GPU_UNIFORM_BLOCK_INFO:
+ return "infoBlock";
+ default:
+ return NULL;
+ }
+}
+
+/* Returns string length including '\0' terminator. */
+inline uint32_t ShaderInterface::set_input_name(ShaderInput *input,
+ char *name,
+ uint32_t name_len) const
+{
+ /* remove "[0]" from array name */
+ if (name[name_len - 1] == ']') {
+ name[name_len - 3] = '\0';
+ name_len -= 3;
+ }
+
+ input->name_offset = (uint32_t)(name - name_buffer_);
+ input->name_hash = BLI_hash_string(name);
+ return name_len + 1; /* include NULL terminator */
+}
+
+inline const ShaderInput *ShaderInterface::input_lookup(const ShaderInput *const inputs,
+ const uint inputs_len,
+ const char *name) const
+{
+ const uint name_hash = BLI_hash_string(name);
+ /* Simple linear search for now. */
+ for (int i = inputs_len - 1; i >= 0; i--) {
+ if (inputs[i].name_hash == name_hash) {
+ if ((i > 0) && UNLIKELY(inputs[i - 1].name_hash == name_hash)) {
+ /* Hash colision resolve. */
+ for (; i >= 0 && inputs[i].name_hash == name_hash; i--) {
+ if (STREQ(name, name_buffer_ + inputs[i].name_offset)) {
+ return inputs + i; /* not found */
+ }
+ }
+ return NULL; /* not found */
+ }
+
+ /* This is a bit dangerous since we could have a hash collision.
+ * where the asked uniform that does not exist has the same hash
+ * as a real uniform. */
+ BLI_assert(STREQ(name, name_buffer_ + inputs[i].name_offset));
+ return inputs + i;
+ }
+ }
+ return NULL; /* not found */
+}
+
+} // namespace blender::gpu
diff --git a/source/blender/gpu/intern/gpu_shader_private.hh b/source/blender/gpu/intern/gpu_shader_private.hh
index 1f667fb4cf9..d56a7b2500b 100644
--- a/source/blender/gpu/intern/gpu_shader_private.hh
+++ b/source/blender/gpu/intern/gpu_shader_private.hh
@@ -23,13 +23,25 @@
#include "BLI_span.hh"
#include "GPU_shader.h"
-#include "GPU_shader_interface.h"
#include "GPU_vertex_buffer.h"
+#include "gpu_shader_interface.hh"
namespace blender {
namespace gpu {
-class Shader : public GPUShader {
+/**
+ * Implementation of shader compilation and uniforms handling.
+ * Base class which is then specialized for each implementation (GL, VK, ...).
+ **/
+class Shader {
+ public:
+ /** Uniform & attribute locations for shader. */
+ ShaderInterface *interface = nullptr;
+
+ protected:
+ /** For debugging purpose. */
+ char name[64];
+
public:
Shader(const char *name);
virtual ~Shader();
diff --git a/source/blender/gpu/intern/gpu_state.cc b/source/blender/gpu/intern/gpu_state.cc
index f02ec9c5cd4..fcbaa500e2d 100644
--- a/source/blender/gpu/intern/gpu_state.cc
+++ b/source/blender/gpu/intern/gpu_state.cc
@@ -44,7 +44,7 @@ using namespace blender::gpu;
do { \
GPUStateManager *stack = GPU_context_active_get()->state_manager; \
auto &state_object = stack->_prefix##state; \
- state_object._state = _value; \
+ state_object._state = (_value); \
} while (0)
#define SET_IMMUTABLE_STATE(_state, _value) SET_STATE(, _state, _value)
@@ -74,10 +74,14 @@ void GPU_provoking_vertex(eGPUProvokingVertex vert)
SET_IMMUTABLE_STATE(provoking_vert, vert);
}
-/* TODO explicit depth test. */
-void GPU_depth_test(bool enable)
+void GPU_depth_test(eGPUDepthTest test)
{
- SET_IMMUTABLE_STATE(depth_test, (enable) ? GPU_DEPTH_LESS_EQUAL : GPU_DEPTH_NONE);
+ SET_IMMUTABLE_STATE(depth_test, test);
+}
+
+void GPU_stencil_test(eGPUStencilTest test)
+{
+ SET_IMMUTABLE_STATE(stencil_test, test);
}
void GPU_line_smooth(bool enable)
@@ -104,11 +108,11 @@ void GPU_color_mask(bool r, bool g, bool b, bool a)
{
GPUStateManager *stack = GPU_context_active_get()->state_manager;
auto &state = stack->state;
- eGPUWriteMask write_mask = state.write_mask;
- SET_FLAG_FROM_TEST(write_mask, r, GPU_WRITE_RED);
- SET_FLAG_FROM_TEST(write_mask, g, GPU_WRITE_GREEN);
- SET_FLAG_FROM_TEST(write_mask, b, GPU_WRITE_BLUE);
- SET_FLAG_FROM_TEST(write_mask, a, GPU_WRITE_ALPHA);
+ uint32_t write_mask = state.write_mask;
+ SET_FLAG_FROM_TEST(write_mask, r, (uint32_t)GPU_WRITE_RED);
+ SET_FLAG_FROM_TEST(write_mask, g, (uint32_t)GPU_WRITE_GREEN);
+ SET_FLAG_FROM_TEST(write_mask, b, (uint32_t)GPU_WRITE_BLUE);
+ SET_FLAG_FROM_TEST(write_mask, a, (uint32_t)GPU_WRITE_ALPHA);
state.write_mask = write_mask;
}
@@ -116,8 +120,8 @@ void GPU_depth_mask(bool depth)
{
GPUStateManager *stack = GPU_context_active_get()->state_manager;
auto &state = stack->state;
- eGPUWriteMask write_mask = state.write_mask;
- SET_FLAG_FROM_TEST(write_mask, depth, GPU_WRITE_DEPTH);
+ uint32_t write_mask = state.write_mask;
+ SET_FLAG_FROM_TEST(write_mask, depth, (uint32_t)GPU_WRITE_DEPTH);
state.write_mask = write_mask;
}
@@ -141,13 +145,13 @@ void GPU_state_set(eGPUWriteMask write_mask,
{
GPUStateManager *stack = GPU_context_active_get()->state_manager;
auto &state = stack->state;
- state.write_mask = write_mask;
- state.blend = blend;
- state.culling_test = culling_test;
- state.depth_test = depth_test;
- state.stencil_test = stencil_test;
- state.stencil_op = stencil_op;
- state.provoking_vert = provoking_vert;
+ state.write_mask = (uint32_t)write_mask;
+ state.blend = (uint32_t)blend;
+ state.culling_test = (uint32_t)culling_test;
+ state.depth_test = (uint32_t)depth_test;
+ state.stencil_test = (uint32_t)stencil_test;
+ state.stencil_op = (uint32_t)stencil_op;
+ state.provoking_vert = (uint32_t)provoking_vert;
}
/** \} */
@@ -197,7 +201,8 @@ void GPU_scissor(int x, int y, int width, int height)
{
GPUStateManager *stack = GPU_context_active_get()->state_manager;
auto &state = stack->mutable_state;
- int scissor_rect[4] = {x, y, width, height};
+ bool enabled = state.scissor_rect[2] > 0;
+ int scissor_rect[4] = {x, y, enabled ? width : -width, height};
copy_v4_v4_int(state.scissor_rect, scissor_rect);
}
@@ -213,10 +218,12 @@ void GPU_stencil_reference_set(uint reference)
{
SET_MUTABLE_STATE(stencil_reference, (uint8_t)reference);
}
+
void GPU_stencil_write_mask_set(uint write_mask)
{
SET_MUTABLE_STATE(stencil_write_mask, (uint8_t)write_mask);
}
+
void GPU_stencil_compare_mask_set(uint compare_mask)
{
SET_MUTABLE_STATE(stencil_compare_mask, (uint8_t)compare_mask);
@@ -231,19 +238,31 @@ void GPU_stencil_compare_mask_set(uint compare_mask)
eGPUBlend GPU_blend_get()
{
GPUState &state = GPU_context_active_get()->state_manager->state;
- return state.blend;
+ return (eGPUBlend)state.blend;
}
eGPUWriteMask GPU_write_mask_get()
{
GPUState &state = GPU_context_active_get()->state_manager->state;
- return state.write_mask;
+ return (eGPUWriteMask)state.write_mask;
+}
+
+uint GPU_stencil_mask_get()
+{
+ GPUStateMutable &state = GPU_context_active_get()->state_manager->mutable_state;
+ return state.stencil_write_mask;
+}
+
+eGPUDepthTest GPU_depth_test_get()
+{
+ GPUState &state = GPU_context_active_get()->state_manager->state;
+ return (eGPUDepthTest)state.depth_test;
}
-bool GPU_depth_test_enabled()
+eGPUStencilTest GPU_stencil_test_get()
{
GPUState &state = GPU_context_active_get()->state_manager->state;
- return state.depth_test != GPU_DEPTH_NONE;
+ return (eGPUStencilTest)state.stencil_test;
}
void GPU_scissor_get(int coords[4])
diff --git a/source/blender/gpu/intern/gpu_state_private.hh b/source/blender/gpu/intern/gpu_state_private.hh
index 1ba79c7c048..a1bfefbaff5 100644
--- a/source/blender/gpu/intern/gpu_state_private.hh
+++ b/source/blender/gpu/intern/gpu_state_private.hh
@@ -35,13 +35,20 @@ namespace gpu {
* Try to keep small to reduce validation time. */
union GPUState {
struct {
- eGPUWriteMask write_mask : 13;
- eGPUBlend blend : 4;
- eGPUFaceCullTest culling_test : 2;
- eGPUDepthTest depth_test : 3;
- eGPUStencilTest stencil_test : 3;
- eGPUStencilOp stencil_op : 3;
- eGPUProvokingVertex provoking_vert : 1;
+ /** eGPUWriteMask */
+ uint32_t write_mask : 13;
+ /** eGPUBlend */
+ uint32_t blend : 4;
+ /** eGPUFaceCullTest */
+ uint32_t culling_test : 2;
+ /** eGPUDepthTest */
+ uint32_t depth_test : 3;
+ /** eGPUStencilTest */
+ uint32_t stencil_test : 3;
+ /** eGPUStencilOp */
+ uint32_t stencil_op : 3;
+ /** eGPUProvokingVertex */
+ uint32_t provoking_vert : 1;
/** Enable bits. */
uint32_t logic_op_xor : 1;
uint32_t invert_facing : 1;
@@ -144,6 +151,10 @@ inline GPUStateMutable operator~(const GPUStateMutable &a)
return r;
}
+/**
+ * State manager keeping track of the draw state and applying it before drawing.
+ * Base class which is then specialized for each implementation (GL, VK, ...).
+ **/
class GPUStateManager {
public:
GPUState state;
diff --git a/source/blender/gpu/intern/gpu_texture.cc b/source/blender/gpu/intern/gpu_texture.cc
index 91990dac83f..e70eeece104 100644
--- a/source/blender/gpu/intern/gpu_texture.cc
+++ b/source/blender/gpu/intern/gpu_texture.cc
@@ -1158,9 +1158,9 @@ static GLenum convert_target_to_gl(int dimension, bool is_array)
{
switch (dimension) {
case 1:
- return is_array ? GL_TEXTURE_1D : GL_TEXTURE_1D_ARRAY;
+ return is_array ? GL_TEXTURE_1D_ARRAY : GL_TEXTURE_1D;
case 2:
- return is_array ? GL_TEXTURE_2D : GL_TEXTURE_2D_ARRAY;
+ return is_array ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D;
case 3:
return GL_TEXTURE_3D;
default:
@@ -2179,6 +2179,11 @@ void GPU_texture_get_mipmap_size(GPUTexture *tex, int lvl, int *size)
void GPU_samplers_init(void)
{
+ float max_anisotropy = 1.0f;
+ if (GLEW_EXT_texture_filter_anisotropic) {
+ glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_anisotropy);
+ }
+
glGenSamplers(GPU_SAMPLER_MAX, GG.samplers);
for (int i = 0; i < GPU_SAMPLER_MAX; i++) {
eGPUSamplerState state = static_cast<eGPUSamplerState>(i);
@@ -2193,7 +2198,7 @@ void GPU_samplers_init(void)
GLenum compare_mode = (state & GPU_SAMPLER_COMPARE) ? GL_COMPARE_REF_TO_TEXTURE : GL_NONE;
/* TODO(fclem) Anisotropic level should be a render engine parameter. */
float aniso_filter = ((state & GPU_SAMPLER_MIPMAP) && (state & GPU_SAMPLER_ANISO)) ?
- U.anisotropic_filter :
+ max_ff(max_anisotropy, U.anisotropic_filter) :
1.0f;
glSamplerParameteri(GG.samplers[i], GL_TEXTURE_WRAP_S, wrap_s);
diff --git a/source/blender/gpu/intern/gpu_uniformbuffer.cc b/source/blender/gpu/intern/gpu_uniform_buffer.cc
index e203ffd848f..94aa6bd76ab 100644
--- a/source/blender/gpu/intern/gpu_uniformbuffer.cc
+++ b/source/blender/gpu/intern/gpu_uniform_buffer.cc
@@ -13,7 +13,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
- * The Original Code is Copyright (C) 2005 Blender Foundation.
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
* All rights reserved.
*/
@@ -27,58 +27,46 @@
#include "BLI_blenlib.h"
#include "BLI_math_base.h"
-#include "gpu_context_private.hh"
+#include "gpu_backend.hh"
#include "gpu_node_graph.h"
-#include "GPU_extensions.h"
-#include "GPU_glew.h"
#include "GPU_material.h"
-#include "GPU_uniformbuffer.h"
-
-typedef struct GPUUniformBuffer {
- /** Data size in bytes. */
- int size;
- /** GL handle for UBO. */
- GLuint bindcode;
- /** Current binding point. */
- int bindpoint;
- /** Continuous memory block to copy to GPU. Is own by the GPUUniformBuffer. */
- void *data;
-} GPUUniformBuffer;
-
-GPUUniformBuffer *GPU_uniformbuffer_create(int size, const void *data, char err_out[256])
+
+#include "GPU_extensions.h"
+
+#include "GPU_uniform_buffer.h"
+#include "gpu_uniform_buffer_private.hh"
+
+/* -------------------------------------------------------------------- */
+/** \name Creation & Deletion
+ * \{ */
+
+namespace blender::gpu {
+
+UniformBuf::UniformBuf(size_t size, const char *name)
{
/* Make sure that UBO is padded to size of vec4 */
BLI_assert((size % 16) == 0);
+ BLI_assert(size <= GPU_max_ubo_size());
- if (size > GPU_max_ubo_size()) {
- if (err_out) {
- BLI_strncpy(err_out, "GPUUniformBuffer: UBO too big", 256);
- }
- return NULL;
- }
-
- GPUUniformBuffer *ubo = (GPUUniformBuffer *)MEM_mallocN(sizeof(GPUUniformBuffer), __func__);
- ubo->size = size;
- ubo->data = NULL;
- ubo->bindcode = 0;
- ubo->bindpoint = -1;
-
- /* Direct init. */
- if (data != NULL) {
- GPU_uniformbuffer_update(ubo, data);
- }
+ size_in_bytes_ = size;
- return ubo;
+ BLI_strncpy(name_, name, sizeof(name_));
}
-void GPU_uniformbuffer_free(GPUUniformBuffer *ubo)
+UniformBuf::~UniformBuf()
{
- MEM_SAFE_FREE(ubo->data);
- GPU_buf_free(ubo->bindcode);
- MEM_freeN(ubo);
+ MEM_SAFE_FREE(data_);
}
+} // namespace blender::gpu
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Uniform buffer from GPUInput list
+ * \{ */
+
/**
* We need to pad some data types (vec3) on the C side
* To match the GPU expected memory block alignment.
@@ -111,10 +99,10 @@ static int inputs_cmp(const void *a, const void *b)
* Make sure we respect the expected alignment of UBOs.
* mat4, vec4, pad vec3 as vec4, then vec2, then floats.
*/
-static void gpu_uniformbuffer_inputs_sort(ListBase *inputs)
+static void buffer_from_list_inputs_sort(ListBase *inputs)
{
-/* Only support up to this type, if you want to extend it, make sure the
- * padding logic is correct for the new types. */
+/* Only support up to this type, if you want to extend it, make sure static void
+ * inputs_sobuffer_size_compute *inputs) padding logic is correct for the new types. */
#define MAX_UBO_GPU_TYPE GPU_MAT4
/* Order them as mat4, vec4, vec3, vec2, float. */
@@ -173,23 +161,9 @@ static void gpu_uniformbuffer_inputs_sort(ListBase *inputs)
#undef MAX_UBO_GPU_TYPE
}
-/**
- * Create dynamic UBO from parameters
- * Return NULL if failed to create or if \param inputs: is empty.
- *
- * \param inputs: ListBase of #BLI_genericNodeN(#GPUInput).
- */
-GPUUniformBuffer *GPU_uniformbuffer_dynamic_create(ListBase *inputs, char err_out[256])
+static inline size_t buffer_size_from_list(ListBase *inputs)
{
- /* There is no point on creating an UBO if there is no arguments. */
- if (BLI_listbase_is_empty(inputs)) {
- return NULL;
- }
- /* Make sure we comply to the ubo alignment requirements. */
- gpu_uniformbuffer_inputs_sort(inputs);
-
size_t buffer_size = 0;
-
LISTBASE_FOREACH (LinkData *, link, inputs) {
const eGPUType gputype = get_padded_gpu_type(link);
buffer_size += gputype * sizeof(float);
@@ -197,8 +171,12 @@ GPUUniformBuffer *GPU_uniformbuffer_dynamic_create(ListBase *inputs, char err_ou
/* Round up to size of vec4. (Opengl Requirement) */
size_t alignment = sizeof(float[4]);
buffer_size = divide_ceil_u(buffer_size, alignment) * alignment;
- void *data = MEM_mallocN(buffer_size, __func__);
+ return buffer_size;
+}
+
+static inline void buffer_fill_from_list(void *data, ListBase *inputs)
+{
/* Now that we know the total ubo size we can start populating it. */
float *offset = (float *)data;
LISTBASE_FOREACH (LinkData *, link, inputs) {
@@ -206,71 +184,73 @@ GPUUniformBuffer *GPU_uniformbuffer_dynamic_create(ListBase *inputs, char err_ou
memcpy(offset, input->vec, input->type * sizeof(float));
offset += get_padded_gpu_type(link);
}
-
- /* Pass data as NULL for late init. */
- GPUUniformBuffer *ubo = GPU_uniformbuffer_create(buffer_size, NULL, err_out);
- /* Data will be update just before binding. */
- ubo->data = data;
- return ubo;
}
-static void gpu_uniformbuffer_init(GPUUniformBuffer *ubo)
-{
- BLI_assert(ubo->bindcode == 0);
- ubo->bindcode = GPU_buf_alloc();
+/** \} */
- if (ubo->bindcode == 0) {
- fprintf(stderr, "GPUUniformBuffer: UBO create failed");
- BLI_assert(0);
- return;
- }
+/* -------------------------------------------------------------------- */
+/** \name C-API
+ * \{ */
- glBindBuffer(GL_UNIFORM_BUFFER, ubo->bindcode);
- glBufferData(GL_UNIFORM_BUFFER, ubo->size, NULL, GL_DYNAMIC_DRAW);
-}
+using namespace blender::gpu;
-void GPU_uniformbuffer_update(GPUUniformBuffer *ubo, const void *data)
+GPUUniformBuf *GPU_uniformbuf_create_ex(size_t size, const void *data, const char *name)
{
- if (ubo->bindcode == 0) {
- gpu_uniformbuffer_init(ubo);
+ UniformBuf *ubo = GPUBackend::get()->uniformbuf_alloc(size, name);
+ /* Direct init. */
+ if (data != NULL) {
+ ubo->update(data);
}
-
- glBindBuffer(GL_UNIFORM_BUFFER, ubo->bindcode);
- glBufferSubData(GL_UNIFORM_BUFFER, 0, ubo->size, data);
- glBindBuffer(GL_UNIFORM_BUFFER, 0);
+ return reinterpret_cast<GPUUniformBuf *>(ubo);
}
-void GPU_uniformbuffer_bind(GPUUniformBuffer *ubo, int number)
+/**
+ * Create UBO from inputs list.
+ * Return NULL if failed to create or if \param inputs: is empty.
+ *
+ * \param inputs: ListBase of #BLI_genericNodeN(#GPUInput).
+ */
+GPUUniformBuf *GPU_uniformbuf_create_from_list(ListBase *inputs, const char *name)
{
- if (number >= GPU_max_ubo_binds()) {
- fprintf(stderr, "Not enough UBO slots.\n");
- return;
+ /* There is no point on creating an UBO if there is no arguments. */
+ if (BLI_listbase_is_empty(inputs)) {
+ return NULL;
}
- if (ubo->bindcode == 0) {
- gpu_uniformbuffer_init(ubo);
- }
+ buffer_from_list_inputs_sort(inputs);
+ size_t buffer_size = buffer_size_from_list(inputs);
+ void *data = MEM_mallocN(buffer_size, __func__);
+ buffer_fill_from_list(data, inputs);
- if (ubo->data != NULL) {
- GPU_uniformbuffer_update(ubo, ubo->data);
- MEM_SAFE_FREE(ubo->data);
- }
+ UniformBuf *ubo = GPUBackend::get()->uniformbuf_alloc(buffer_size, name);
+ /* Defer data upload. */
+ ubo->attach_data(data);
+ return reinterpret_cast<GPUUniformBuf *>(ubo);
+}
- glBindBufferBase(GL_UNIFORM_BUFFER, number, ubo->bindcode);
- ubo->bindpoint = number;
+void GPU_uniformbuf_free(GPUUniformBuf *ubo)
+{
+ delete reinterpret_cast<UniformBuf *>(ubo);
}
-void GPU_uniformbuffer_unbind(GPUUniformBuffer *ubo)
+void GPU_uniformbuf_update(GPUUniformBuf *ubo, const void *data)
{
-#ifndef NDEBUG
- glBindBufferBase(GL_UNIFORM_BUFFER, ubo->bindpoint, 0);
-#endif
- ubo->bindpoint = 0;
+ reinterpret_cast<UniformBuf *>(ubo)->update(data);
}
-void GPU_uniformbuffer_unbind_all(void)
+void GPU_uniformbuf_bind(GPUUniformBuf *ubo, int slot)
{
- for (int i = 0; i < GPU_max_ubo_binds(); i++) {
- glBindBufferBase(GL_UNIFORM_BUFFER, i, 0);
- }
+ reinterpret_cast<UniformBuf *>(ubo)->bind(slot);
}
+
+void GPU_uniformbuf_unbind(GPUUniformBuf *ubo)
+{
+ reinterpret_cast<UniformBuf *>(ubo)->unbind();
+}
+
+void GPU_uniformbuf_unbind_all(void)
+{
+ /* FIXME */
+}
+
+/** \} */
diff --git a/source/blender/gpu/intern/gpu_uniform_buffer_private.hh b/source/blender/gpu/intern/gpu_uniform_buffer_private.hh
new file mode 100644
index 00000000000..288cbae526e
--- /dev/null
+++ b/source/blender/gpu/intern/gpu_uniform_buffer_private.hh
@@ -0,0 +1,69 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2020, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#pragma once
+
+#include "BLI_sys_types.h"
+
+namespace blender {
+namespace gpu {
+
+#ifdef DEBUG
+# define DEBUG_NAME_LEN 8
+#else
+# define DEBUG_NAME_LEN 64
+#endif
+
+/**
+ * Implementation of Uniform Buffers.
+ * Base class which is then specialized for each implementation (GL, VK, ...).
+ **/
+class UniformBuf {
+ protected:
+ /** Data size in bytes. */
+ size_t size_in_bytes_;
+ /** Continuous memory block to copy to GPU. This data is owned by the UniformBuf. */
+ void *data_ = NULL;
+ /** Debugging name */
+ char name_[DEBUG_NAME_LEN];
+
+ public:
+ UniformBuf(size_t size, const char *name);
+ virtual ~UniformBuf();
+
+ virtual void update(const void *data) = 0;
+ virtual void bind(int slot) = 0;
+ virtual void unbind(void) = 0;
+
+ /** Used to defer data upload at drawing time.
+ * This is useful if the thread has no context bound.
+ * This transfers ownership to this UniformBuf. */
+ void attach_data(void *data)
+ {
+ data_ = data;
+ }
+};
+
+#undef DEBUG_NAME_LEN
+
+} // namespace gpu
+} // namespace blender
diff --git a/source/blender/gpu/intern/gpu_vertex_format.cc b/source/blender/gpu/intern/gpu_vertex_format.cc
index 2b16f4482b4..ed317b3a0df 100644
--- a/source/blender/gpu/intern/gpu_vertex_format.cc
+++ b/source/blender/gpu/intern/gpu_vertex_format.cc
@@ -407,6 +407,6 @@ void VertexFormat_pack(GPUVertFormat *format)
void GPU_vertformat_from_shader(GPUVertFormat *format, const struct GPUShader *gpushader)
{
- const Shader *shader = static_cast<const Shader *>(gpushader);
+ const Shader *shader = reinterpret_cast<const Shader *>(gpushader);
shader->vertformat_from_shader(format);
}
diff --git a/source/blender/gpu/intern/gpu_viewport.c b/source/blender/gpu/intern/gpu_viewport.c
index ba938349761..fd1265dc2a6 100644
--- a/source/blender/gpu/intern/gpu_viewport.c
+++ b/source/blender/gpu/intern/gpu_viewport.c
@@ -41,7 +41,7 @@
#include "GPU_immediate.h"
#include "GPU_matrix.h"
#include "GPU_texture.h"
-#include "GPU_uniformbuffer.h"
+#include "GPU_uniform_buffer.h"
#include "GPU_viewport.h"
#include "DRW_engine.h"
@@ -1020,8 +1020,8 @@ void GPU_viewport_free(GPUViewport *viewport)
}
for (int i = 0; i < viewport->vmempool.ubo_len; i++) {
- GPU_uniformbuffer_free(viewport->vmempool.matrices_ubo[i]);
- GPU_uniformbuffer_free(viewport->vmempool.obinfos_ubo[i]);
+ GPU_uniformbuf_free(viewport->vmempool.matrices_ubo[i]);
+ GPU_uniformbuf_free(viewport->vmempool.obinfos_ubo[i]);
}
MEM_SAFE_FREE(viewport->vmempool.matrices_ubo);
MEM_SAFE_FREE(viewport->vmempool.obinfos_ubo);
diff --git a/source/blender/gpu/opengl/gl_backend.hh b/source/blender/gpu/opengl/gl_backend.hh
index 63a14af6612..0234c1a3f63 100644
--- a/source/blender/gpu/opengl/gl_backend.hh
+++ b/source/blender/gpu/opengl/gl_backend.hh
@@ -31,6 +31,7 @@
#include "gl_context.hh"
#include "gl_drawlist.hh"
#include "gl_shader.hh"
+#include "gl_uniform_buffer.hh"
namespace blender {
namespace gpu {
@@ -40,6 +41,11 @@ class GLBackend : public GPUBackend {
GLSharedOrphanLists shared_orphan_list_;
public:
+ static GLBackend *get(void)
+ {
+ return static_cast<GLBackend *>(GPUBackend::get());
+ }
+
GPUContext *context_alloc(void *ghost_window)
{
return new GLContext(ghost_window, shared_orphan_list_);
@@ -60,6 +66,11 @@ class GLBackend : public GPUBackend {
return new GLShader(name);
};
+ UniformBuf *uniformbuf_alloc(int size, const char *name)
+ {
+ return new GLUniformBuf(size, name);
+ };
+
/* TODO remove */
void buf_free(GLuint buf_id);
void tex_free(GLuint tex_id);
diff --git a/source/blender/gpu/opengl/gl_batch.cc b/source/blender/gpu/opengl/gl_batch.cc
index fade8763065..7d6270bca93 100644
--- a/source/blender/gpu/opengl/gl_batch.cc
+++ b/source/blender/gpu/opengl/gl_batch.cc
@@ -33,6 +33,7 @@
#include "gpu_batch_private.hh"
#include "gpu_primitive_private.h"
+#include "gpu_shader_private.hh"
#include "gl_batch.hh"
#include "gl_context.hh"
@@ -68,10 +69,11 @@ void GLVaoCache::init(void)
}
vao_base_instance_ = 0;
base_instance_ = 0;
+ vao_id_ = 0;
}
/* Create a new VAO object and store it in the cache. */
-void GLVaoCache::insert(const GPUShaderInterface *interface, GLuint vao)
+void GLVaoCache::insert(const GLShaderInterface *interface, GLuint vao)
{
/* Now insert the cache. */
if (!is_dynamic_vao_count) {
@@ -90,8 +92,7 @@ void GLVaoCache::insert(const GPUShaderInterface *interface, GLuint vao)
/* Erase previous entries, they will be added back if drawn again. */
for (int i = 0; i < GPU_VAO_STATIC_LEN; i++) {
if (static_vaos.interfaces[i] != NULL) {
- GPU_shaderinterface_remove_batch_ref(
- const_cast<GPUShaderInterface *>(static_vaos.interfaces[i]), this);
+ const_cast<GLShaderInterface *>(static_vaos.interfaces[i])->ref_remove(this);
context_->vao_free(static_vaos.vao_ids[i]);
}
}
@@ -99,8 +100,8 @@ void GLVaoCache::insert(const GPUShaderInterface *interface, GLuint vao)
is_dynamic_vao_count = true;
/* Init dynamic arrays and let the branch below set the values. */
dynamic_vaos.count = GPU_BATCH_VAO_DYN_ALLOC_COUNT;
- dynamic_vaos.interfaces = (const GPUShaderInterface **)MEM_callocN(
- dynamic_vaos.count * sizeof(GPUShaderInterface *), "dyn vaos interfaces");
+ dynamic_vaos.interfaces = (const GLShaderInterface **)MEM_callocN(
+ dynamic_vaos.count * sizeof(GLShaderInterface *), "dyn vaos interfaces");
dynamic_vaos.vao_ids = (GLuint *)MEM_callocN(dynamic_vaos.count * sizeof(GLuint),
"dyn vaos ids");
}
@@ -118,8 +119,8 @@ void GLVaoCache::insert(const GPUShaderInterface *interface, GLuint vao)
/* Not enough place, realloc the array. */
i = dynamic_vaos.count;
dynamic_vaos.count += GPU_BATCH_VAO_DYN_ALLOC_COUNT;
- dynamic_vaos.interfaces = (const GPUShaderInterface **)MEM_recallocN(
- (void *)dynamic_vaos.interfaces, sizeof(GPUShaderInterface *) * dynamic_vaos.count);
+ dynamic_vaos.interfaces = (const GLShaderInterface **)MEM_recallocN(
+ (void *)dynamic_vaos.interfaces, sizeof(GLShaderInterface *) * dynamic_vaos.count);
dynamic_vaos.vao_ids = (GLuint *)MEM_recallocN(dynamic_vaos.vao_ids,
sizeof(GLuint) * dynamic_vaos.count);
}
@@ -127,15 +128,15 @@ void GLVaoCache::insert(const GPUShaderInterface *interface, GLuint vao)
dynamic_vaos.vao_ids[i] = vao;
}
- GPU_shaderinterface_add_batch_ref(const_cast<GPUShaderInterface *>(interface), this);
+ const_cast<GLShaderInterface *>(interface)->ref_add(this);
}
-void GLVaoCache::remove(const GPUShaderInterface *interface)
+void GLVaoCache::remove(const GLShaderInterface *interface)
{
const int count = (is_dynamic_vao_count) ? dynamic_vaos.count : GPU_VAO_STATIC_LEN;
GLuint *vaos = (is_dynamic_vao_count) ? dynamic_vaos.vao_ids : static_vaos.vao_ids;
- const GPUShaderInterface **interfaces = (is_dynamic_vao_count) ? dynamic_vaos.interfaces :
- static_vaos.interfaces;
+ const GLShaderInterface **interfaces = (is_dynamic_vao_count) ? dynamic_vaos.interfaces :
+ static_vaos.interfaces;
for (int i = 0; i < count; i++) {
if (interfaces[i] == interface) {
context_->vao_free(vaos[i]);
@@ -151,8 +152,8 @@ void GLVaoCache::clear(void)
GLContext *ctx = static_cast<GLContext *>(GPU_context_active_get());
const int count = (is_dynamic_vao_count) ? dynamic_vaos.count : GPU_VAO_STATIC_LEN;
GLuint *vaos = (is_dynamic_vao_count) ? dynamic_vaos.vao_ids : static_vaos.vao_ids;
- const GPUShaderInterface **interfaces = (is_dynamic_vao_count) ? dynamic_vaos.interfaces :
- static_vaos.interfaces;
+ const GLShaderInterface **interfaces = (is_dynamic_vao_count) ? dynamic_vaos.interfaces :
+ static_vaos.interfaces;
/* Early out, nothing to free. */
if (context_ == NULL) {
return;
@@ -171,10 +172,9 @@ void GLVaoCache::clear(void)
}
for (int i = 0; i < count; i++) {
- if (interfaces[i] == NULL) {
- continue;
+ if (interfaces[i] != NULL) {
+ const_cast<GLShaderInterface *>(interfaces[i])->ref_remove(this);
}
- GPU_shaderinterface_remove_batch_ref(const_cast<GPUShaderInterface *>(interfaces[i]), this);
}
if (is_dynamic_vao_count) {
@@ -190,11 +190,11 @@ void GLVaoCache::clear(void)
}
/* Return 0 on cache miss (invalid VAO) */
-GLuint GLVaoCache::lookup(const GPUShaderInterface *interface)
+GLuint GLVaoCache::lookup(const GLShaderInterface *interface)
{
const int count = (is_dynamic_vao_count) ? dynamic_vaos.count : GPU_VAO_STATIC_LEN;
- const GPUShaderInterface **interfaces = (is_dynamic_vao_count) ? dynamic_vaos.interfaces :
- static_vaos.interfaces;
+ const GLShaderInterface **interfaces = (is_dynamic_vao_count) ? dynamic_vaos.interfaces :
+ static_vaos.interfaces;
for (int i = 0; i < count; i++) {
if (interfaces[i] == interface) {
return (is_dynamic_vao_count) ? dynamic_vaos.vao_ids[i] : static_vaos.vao_ids[i];
@@ -226,7 +226,9 @@ GLuint GLVaoCache::base_instance_vao_get(GPUBatch *batch, int i_first)
{
this->context_check();
/* Make sure the interface is up to date. */
- if (interface_ != GPU_context_active_get()->shader->interface) {
+ Shader *shader = GPU_context_active_get()->shader;
+ GLShaderInterface *interface = static_cast<GLShaderInterface *>(shader->interface);
+ if (interface_ != interface) {
vao_get(batch);
/* Trigger update. */
base_instance_ = 0;
@@ -238,6 +240,7 @@ GLuint GLVaoCache::base_instance_vao_get(GPUBatch *batch, int i_first)
#ifdef __APPLE__
glDeleteVertexArrays(1, &vao_base_instance_);
vao_base_instance_ = 0;
+ base_instance_ = 0;
#endif
if (vao_base_instance_ == 0) {
@@ -248,16 +251,17 @@ GLuint GLVaoCache::base_instance_vao_get(GPUBatch *batch, int i_first)
base_instance_ = i_first;
GLVertArray::update_bindings(vao_base_instance_, batch, interface_, i_first);
}
- return base_instance_;
+ return vao_base_instance_;
}
GLuint GLVaoCache::vao_get(GPUBatch *batch)
{
this->context_check();
- GPUContext *ctx = GPU_context_active_get();
- if (interface_ != ctx->shader->interface) {
- interface_ = ctx->shader->interface;
+ Shader *shader = GPU_context_active_get()->shader;
+ GLShaderInterface *interface = static_cast<GLShaderInterface *>(shader->interface);
+ if (interface_ != interface) {
+ interface_ = interface;
vao_id_ = this->lookup(interface_);
if (vao_id_ == 0) {
@@ -303,6 +307,7 @@ void GLBatch::bind(int i_first)
GPU_context_active_get()->state_manager->apply_state();
if (flag & GPU_BATCH_DIRTY) {
+ flag &= ~GPU_BATCH_DIRTY;
vao_cache_.clear();
}
@@ -324,6 +329,8 @@ void GLBatch::bind(int i_first)
void GLBatch::draw(int v_first, int v_count, int i_first, int i_count)
{
+ GL_CHECK_ERROR("Batch Pre drawing");
+
this->bind(i_first);
BLI_assert(v_count > 0 && i_count > 0);
@@ -350,6 +357,7 @@ void GLBatch::draw(int v_first, int v_count, int i_first, int i_count)
glDrawElementsInstancedBaseVertex(
gl_type, v_count, index_type, v_first_ofs, i_count, base_index);
}
+ GL_CHECK_ERROR("Batch Post-drawing Indexed");
}
else {
#ifdef __APPLE__
@@ -364,6 +372,7 @@ void GLBatch::draw(int v_first, int v_count, int i_first, int i_count)
#ifdef __APPLE__
glEnable(GL_PRIMITIVE_RESTART);
#endif
+ GL_CHECK_ERROR("Batch Post-drawing Non-indexed");
}
}
diff --git a/source/blender/gpu/opengl/gl_batch.hh b/source/blender/gpu/opengl/gl_batch.hh
index d70f43aed2a..9399148c68d 100644
--- a/source/blender/gpu/opengl/gl_batch.hh
+++ b/source/blender/gpu/opengl/gl_batch.hh
@@ -32,11 +32,12 @@
#include "glew-mx.h"
-#include "GPU_shader_interface.h"
-
namespace blender {
namespace gpu {
+class GLContext;
+class GLShaderInterface;
+
#define GPU_VAO_STATIC_LEN 3
/* Vao management: remembers all geometry state (vertex attribute bindings & element buffer)
@@ -45,9 +46,9 @@ namespace gpu {
class GLVaoCache {
private:
/** Context for which the vao_cache_ was generated. */
- struct GLContext *context_ = NULL;
+ GLContext *context_ = NULL;
/** Last interface this batch was drawn with. */
- GPUShaderInterface *interface_ = NULL;
+ GLShaderInterface *interface_ = NULL;
/** Cached vao for the last interface. */
GLuint vao_id_ = 0;
/** Used whend arb_base_instance is not supported. */
@@ -58,13 +59,13 @@ class GLVaoCache {
union {
/** Static handle count */
struct {
- const GPUShaderInterface *interfaces[GPU_VAO_STATIC_LEN];
+ const GLShaderInterface *interfaces[GPU_VAO_STATIC_LEN];
GLuint vao_ids[GPU_VAO_STATIC_LEN];
} static_vaos;
/** Dynamic handle count */
struct {
uint count;
- const GPUShaderInterface **interfaces;
+ const GLShaderInterface **interfaces;
GLuint *vao_ids;
} dynamic_vaos;
};
@@ -76,9 +77,9 @@ class GLVaoCache {
GLuint vao_get(GPUBatch *batch);
GLuint base_instance_vao_get(GPUBatch *batch, int i_first);
- GLuint lookup(const GPUShaderInterface *interface);
- void insert(const GPUShaderInterface *interface, GLuint vao_id);
- void remove(const GPUShaderInterface *interface);
+ GLuint lookup(const GLShaderInterface *interface);
+ void insert(const GLShaderInterface *interface, GLuint vao_id);
+ void remove(const GLShaderInterface *interface);
void clear(void);
private:
diff --git a/source/blender/gpu/opengl/gl_context.cc b/source/blender/gpu/opengl/gl_context.cc
index 11f313f639b..2ac361d28e1 100644
--- a/source/blender/gpu/opengl/gl_context.cc
+++ b/source/blender/gpu/opengl/gl_context.cc
@@ -22,6 +22,7 @@
*/
#include "BLI_assert.h"
+#include "BLI_system.h"
#include "BLI_utildefines.h"
#include "GPU_framebuffer.h"
@@ -238,3 +239,37 @@ void GLContext::framebuffer_unregister(struct GPUFrameBuffer *fb)
}
/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Error Checking
+ *
+ * This is only useful for implementation that does not support the KHR_debug extension.
+ * \{ */
+
+void GLContext::check_error(const char *info)
+{
+ GLenum error = glGetError();
+
+#define ERROR_CASE(err) \
+ case err: \
+ fprintf(stderr, "GL error: %s : %s\n", #err, info); \
+ BLI_system_backtrace(stderr); \
+ break;
+
+ switch (error) {
+ ERROR_CASE(GL_INVALID_ENUM)
+ ERROR_CASE(GL_INVALID_VALUE)
+ ERROR_CASE(GL_INVALID_OPERATION)
+ ERROR_CASE(GL_INVALID_FRAMEBUFFER_OPERATION)
+ ERROR_CASE(GL_OUT_OF_MEMORY)
+ ERROR_CASE(GL_STACK_UNDERFLOW)
+ ERROR_CASE(GL_STACK_OVERFLOW)
+ case GL_NO_ERROR:
+ break;
+ default:
+ fprintf(stderr, "Unknown GL error: %x : %s", error, info);
+ break;
+ }
+}
+
+/** \} */
diff --git a/source/blender/gpu/opengl/gl_context.hh b/source/blender/gpu/opengl/gl_context.hh
index 0b762c939f1..35121a960ba 100644
--- a/source/blender/gpu/opengl/gl_context.hh
+++ b/source/blender/gpu/opengl/gl_context.hh
@@ -32,13 +32,20 @@
#include "glew-mx.h"
-#include "gl_batch.hh"
-
#include <mutex>
+/* Enabled on MacOS by default since there is no support for debug callbacks. */
+#if defined(DEBUG) && defined(__APPLE__)
+# define GL_CHECK_ERROR(info) GLContext::check_error(info)
+#else
+# define GL_CHECK_ERROR(info)
+#endif
+
namespace blender {
namespace gpu {
+class GLVaoCache;
+
class GLSharedOrphanLists {
public:
/** Mutex for the bellow structures. */
@@ -51,7 +58,7 @@ class GLSharedOrphanLists {
void orphans_clear(void);
};
-struct GLContext : public GPUContext {
+class GLContext : public GPUContext {
/* TODO(fclem) these needs to become private. */
public:
/** Default VAO for procedural draw calls. */
@@ -78,6 +85,8 @@ struct GLContext : public GPUContext {
GLContext(void *ghost_window, GLSharedOrphanLists &shared_orphan_list);
~GLContext();
+ static void check_error(const char *info);
+
void activate(void) override;
void deactivate(void) override;
diff --git a/source/blender/gpu/opengl/gl_drawlist.hh b/source/blender/gpu/opengl/gl_drawlist.hh
index 4f085149388..b690b8f8a98 100644
--- a/source/blender/gpu/opengl/gl_drawlist.hh
+++ b/source/blender/gpu/opengl/gl_drawlist.hh
@@ -19,6 +19,9 @@
/** \file
* \ingroup gpu
+ *
+ * Implementation of Multi Draw Indirect using OpenGL.
+ * Fallback if the needed extensions are not supported.
*/
#pragma once
@@ -37,6 +40,9 @@
namespace blender {
namespace gpu {
+/**
+ * Implementation of Multi Draw Indirect using OpenGL.
+ **/
class GLDrawList : public DrawList {
public:
GLDrawList(int length);
diff --git a/source/blender/gpu/opengl/gl_shader.cc b/source/blender/gpu/opengl/gl_shader.cc
index ea33ff00d69..3ec818b53a6 100644
--- a/source/blender/gpu/opengl/gl_shader.cc
+++ b/source/blender/gpu/opengl/gl_shader.cc
@@ -29,6 +29,7 @@
#include "GPU_platform.h"
#include "gl_shader.hh"
+#include "gl_shader_interface.hh"
using namespace blender;
using namespace blender::gpu;
@@ -48,7 +49,7 @@ GLShader::GLShader(const char *name) : Shader(name)
#ifndef __APPLE__
if ((G.debug & G_DEBUG_GPU) && (GLEW_VERSION_4_3 || GLEW_KHR_debug)) {
char sh_name[64];
- BLI_snprintf(sh_name, sizeof(sh_name), "ShaderProgram-%s", name);
+ SNPRINTF(sh_name, "ShaderProgram-%s", name);
glObjectLabel(GL_PROGRAM, shader_program_, -1, sh_name);
}
#endif
@@ -150,6 +151,7 @@ GLuint GLShader::create_shader_stage(GLenum gl_stage, MutableSpan<const char *>
}
if (!status) {
glDeleteShader(shader);
+ compilation_failed_ = true;
return 0;
}
@@ -192,6 +194,10 @@ void GLShader::fragment_shader_from_glsl(MutableSpan<const char *> sources)
bool GLShader::finalize(void)
{
+ if (compilation_failed_) {
+ return false;
+ }
+
glLinkProgram(shader_program_);
GLint status;
@@ -203,10 +209,7 @@ bool GLShader::finalize(void)
return false;
}
- /* TODO(fclem) We need this to modify the image binding points using glUniform.
- * This could be avoided using glProgramUniform in GL 4.1. */
- glUseProgram(shader_program_);
- interface = GPU_shaderinterface_create(shader_program_);
+ interface = new GLShaderInterface(shader_program_);
return true;
}
diff --git a/source/blender/gpu/opengl/gl_shader.hh b/source/blender/gpu/opengl/gl_shader.hh
index b432a04abaa..a686014f4c5 100644
--- a/source/blender/gpu/opengl/gl_shader.hh
+++ b/source/blender/gpu/opengl/gl_shader.hh
@@ -19,9 +19,6 @@
/** \file
* \ingroup gpu
- *
- * GPUDrawList is an API to do lots of similar draw-calls very fast using
- * multi-draw-indirect. There is a fallback if the feature is not supported.
*/
#pragma once
@@ -35,14 +32,19 @@
namespace blender {
namespace gpu {
+/**
+ * Implementation of shader compilation and uniforms handling using OpenGL.
+ **/
class GLShader : public Shader {
private:
/** Handle for full program (links shader stages below). */
GLuint shader_program_ = 0;
-
+ /** Individual shader stages. */
GLuint vert_shader_ = 0;
GLuint geom_shader_ = 0;
GLuint frag_shader_ = 0;
+ /** True if any shader failed to compile. */
+ bool compilation_failed_ = false;
eGPUShaderTFBType transform_feedback_type_ = GPU_SHADER_TFB_NONE;
diff --git a/source/blender/gpu/opengl/gl_shader_interface.cc b/source/blender/gpu/opengl/gl_shader_interface.cc
new file mode 100644
index 00000000000..423db5c8c97
--- /dev/null
+++ b/source/blender/gpu/opengl/gl_shader_interface.cc
@@ -0,0 +1,297 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2016 by Mike Erwin.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup gpu
+ *
+ * GPU shader interface (C --> GLSL)
+ */
+
+#include "BLI_bitmap.h"
+
+#include "gl_batch.hh"
+
+#include "gl_shader_interface.hh"
+
+namespace blender::gpu {
+
+/* -------------------------------------------------------------------- */
+/** \name Binding assignment
+ *
+ * To mimic vulkan, we assign binding at shader creation to avoid shader recompilation.
+ * In the future, we should set it in the shader using layout(binding = i) and query its value.
+ * \{ */
+
+static inline int block_binding(int32_t program, uint32_t block_index)
+{
+ /* For now just assign a consecutive index. In the future, we should set it in
+ * the shader using layout(binding = i) and query its value. */
+ glUniformBlockBinding(program, block_index, block_index);
+ return block_index;
+}
+
+static inline int sampler_binding(int32_t program,
+ uint32_t uniform_index,
+ int32_t uniform_location,
+ int *sampler_len)
+{
+ /* Identify sampler uniforms and asign sampler units to them. */
+ GLint type;
+ glGetActiveUniformsiv(program, 1, &uniform_index, GL_UNIFORM_TYPE, &type);
+
+ switch (type) {
+ case GL_SAMPLER_1D:
+ case GL_SAMPLER_2D:
+ case GL_SAMPLER_3D:
+ case GL_SAMPLER_CUBE:
+ case GL_SAMPLER_CUBE_MAP_ARRAY_ARB: /* OpenGL 4.0 */
+ case GL_SAMPLER_1D_SHADOW:
+ case GL_SAMPLER_2D_SHADOW:
+ case GL_SAMPLER_1D_ARRAY:
+ case GL_SAMPLER_2D_ARRAY:
+ case GL_SAMPLER_1D_ARRAY_SHADOW:
+ case GL_SAMPLER_2D_ARRAY_SHADOW:
+ case GL_SAMPLER_2D_MULTISAMPLE:
+ case GL_SAMPLER_2D_MULTISAMPLE_ARRAY:
+ case GL_SAMPLER_CUBE_SHADOW:
+ case GL_SAMPLER_BUFFER:
+ case GL_INT_SAMPLER_1D:
+ case GL_INT_SAMPLER_2D:
+ case GL_INT_SAMPLER_3D:
+ case GL_INT_SAMPLER_CUBE:
+ case GL_INT_SAMPLER_1D_ARRAY:
+ case GL_INT_SAMPLER_2D_ARRAY:
+ case GL_INT_SAMPLER_2D_MULTISAMPLE:
+ case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
+ case GL_INT_SAMPLER_BUFFER:
+ case GL_UNSIGNED_INT_SAMPLER_1D:
+ case GL_UNSIGNED_INT_SAMPLER_2D:
+ case GL_UNSIGNED_INT_SAMPLER_3D:
+ case GL_UNSIGNED_INT_SAMPLER_CUBE:
+ case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY:
+ case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
+ case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE:
+ case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
+ case GL_UNSIGNED_INT_SAMPLER_BUFFER: {
+ /* For now just assign a consecutive index. In the future, we should set it in
+ * the shader using layout(binding = i) and query its value. */
+ int binding = *sampler_len;
+ glUniform1i(uniform_location, binding);
+ (*sampler_len)++;
+ return binding;
+ }
+ default:
+ return -1;
+ }
+}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Creation / Destruction
+ * \{ */
+
+GLShaderInterface::GLShaderInterface(GLuint program)
+{
+ /* Necessary to make glUniform works. */
+ glUseProgram(program);
+
+ GLint max_attr_name_len = 0, attr_len = 0;
+ glGetProgramiv(program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &max_attr_name_len);
+ glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &attr_len);
+
+ GLint max_ubo_name_len = 0, ubo_len = 0;
+ glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, &max_ubo_name_len);
+ glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCKS, &ubo_len);
+
+ GLint max_uniform_name_len = 0, active_uniform_len = 0, uniform_len = 0;
+ glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &max_uniform_name_len);
+ glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &active_uniform_len);
+ uniform_len = active_uniform_len;
+
+ /* Work around driver bug with Intel HD 4600 on Windows 7/8, where
+ * GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH does not work. */
+ if (attr_len > 0 && max_attr_name_len == 0) {
+ max_attr_name_len = 256;
+ }
+ if (ubo_len > 0 && max_ubo_name_len == 0) {
+ max_ubo_name_len = 256;
+ }
+ if (uniform_len > 0 && max_uniform_name_len == 0) {
+ max_uniform_name_len = 256;
+ }
+
+ /* GL_ACTIVE_UNIFORMS lied to us! Remove the UBO uniforms from the total before
+ * allocating the uniform array. */
+ GLint max_ubo_uni_len = 0;
+ for (int i = 0; i < ubo_len; i++) {
+ GLint ubo_uni_len;
+ glGetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &ubo_uni_len);
+ max_ubo_uni_len = max_ii(max_ubo_uni_len, ubo_uni_len);
+ uniform_len -= ubo_uni_len;
+ }
+ /* Bit set to true if uniform comes from a uniform block. */
+ BLI_bitmap *uniforms_from_blocks = BLI_BITMAP_NEW(active_uniform_len, __func__);
+ /* Set uniforms from block for exclusion. */
+ GLint *ubo_uni_ids = (GLint *)MEM_mallocN(sizeof(GLint) * max_ubo_uni_len, __func__);
+ for (int i = 0; i < ubo_len; i++) {
+ GLint ubo_uni_len;
+ glGetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &ubo_uni_len);
+ glGetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, ubo_uni_ids);
+ for (int u = 0; u < ubo_uni_len; u++) {
+ BLI_BITMAP_ENABLE(uniforms_from_blocks, ubo_uni_ids[u]);
+ }
+ }
+ MEM_freeN(ubo_uni_ids);
+
+ int input_tot_len = attr_len + ubo_len + uniform_len;
+ inputs_ = (ShaderInput *)MEM_callocN(sizeof(ShaderInput) * input_tot_len, __func__);
+
+ const uint32_t name_buffer_len = attr_len * max_attr_name_len + ubo_len * max_ubo_name_len +
+ uniform_len * max_uniform_name_len;
+ name_buffer_ = (char *)MEM_mallocN(name_buffer_len, "name_buffer");
+ uint32_t name_buffer_offset = 0;
+
+ /* Attributes */
+ enabled_attr_mask_ = 0;
+ for (int i = 0; i < attr_len; i++) {
+ char *name = name_buffer_ + name_buffer_offset;
+ GLsizei remaining_buffer = name_buffer_len - name_buffer_offset;
+ GLsizei name_len = 0;
+ GLenum type;
+ GLint size;
+
+ glGetActiveAttrib(program, i, remaining_buffer, &name_len, &size, &type, name);
+ GLint location = glGetAttribLocation(program, name);
+ /* Ignore OpenGL names like `gl_BaseInstanceARB`, `gl_InstanceID` and `gl_VertexID`. */
+ if (location == -1) {
+ continue;
+ }
+
+ ShaderInput *input = &inputs_[attr_len_++];
+ input->location = input->binding = location;
+
+ name_buffer_offset += set_input_name(input, name, name_len);
+ enabled_attr_mask_ |= (1 << input->location);
+ }
+
+ /* Uniform Blocks */
+ for (int i = 0; i < ubo_len; i++) {
+ char *name = name_buffer_ + name_buffer_offset;
+ GLsizei remaining_buffer = name_buffer_len - name_buffer_offset;
+ GLsizei name_len = 0;
+
+ glGetActiveUniformBlockName(program, i, remaining_buffer, &name_len, name);
+
+ ShaderInput *input = &inputs_[attr_len_ + ubo_len_++];
+ input->binding = input->location = block_binding(program, i);
+
+ name_buffer_offset += this->set_input_name(input, name, name_len);
+ enabled_ubo_mask_ |= (1 << input->binding);
+ }
+
+ /* Uniforms */
+ for (int i = 0, sampler = 0; i < active_uniform_len; i++) {
+ if (BLI_BITMAP_TEST(uniforms_from_blocks, i)) {
+ continue;
+ }
+ char *name = name_buffer_ + name_buffer_offset;
+ GLsizei remaining_buffer = name_buffer_len - name_buffer_offset;
+ GLsizei name_len = 0;
+
+ glGetActiveUniformName(program, i, remaining_buffer, &name_len, name);
+
+ ShaderInput *input = &inputs_[attr_len_ + ubo_len_ + uniform_len_++];
+ input->location = glGetUniformLocation(program, name);
+ input->binding = sampler_binding(program, i, input->location, &sampler);
+
+ name_buffer_offset += this->set_input_name(input, name, name_len);
+ enabled_tex_mask_ |= (input->binding != -1) ? (1lu << input->binding) : 0lu;
+ }
+
+ /* Builtin Uniforms */
+ for (int32_t u_int = 0; u_int < GPU_NUM_UNIFORMS; u_int++) {
+ GPUUniformBuiltin u = static_cast<GPUUniformBuiltin>(u_int);
+ builtins_[u] = glGetUniformLocation(program, builtin_uniform_name(u));
+ }
+
+ /* Builtin Uniforms Blocks */
+ for (int32_t u_int = 0; u_int < GPU_NUM_UNIFORM_BLOCKS; u_int++) {
+ GPUUniformBlockBuiltin u = static_cast<GPUUniformBlockBuiltin>(u_int);
+ const ShaderInput *block = this->ubo_get(builtin_uniform_block_name(u));
+ builtin_blocks_[u] = (block != NULL) ? block->binding : -1;
+ }
+
+ MEM_freeN(uniforms_from_blocks);
+
+ /* Resize name buffer to save some memory. */
+ if (name_buffer_offset < name_buffer_len) {
+ name_buffer_ = (char *)MEM_reallocN(name_buffer_, name_buffer_offset);
+ }
+
+ // this->debug_print();
+
+ this->sort_inputs();
+}
+
+GLShaderInterface::~GLShaderInterface()
+{
+ for (auto *ref : refs_) {
+ if (ref != NULL) {
+ ref->remove(this);
+ }
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Batch Reference
+ * \{ */
+
+void GLShaderInterface::ref_add(GLVaoCache *ref)
+{
+ for (int i = 0; i < refs_.size(); i++) {
+ if (refs_[i] == NULL) {
+ refs_[i] = ref;
+ return;
+ }
+ }
+ refs_.append(ref);
+}
+
+void GLShaderInterface::ref_remove(GLVaoCache *ref)
+{
+ for (int i = 0; i < refs_.size(); i++) {
+ if (refs_[i] == ref) {
+ refs_[i] = NULL;
+ break; /* cannot have duplicates */
+ }
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Validation
+ * TODO
+ * \{ */
+
+/** \} */
+
+} // namespace blender::gpu \ No newline at end of file
diff --git a/source/blender/gpu/opengl/gl_shader_interface.hh b/source/blender/gpu/opengl/gl_shader_interface.hh
new file mode 100644
index 00000000000..0b9585aa389
--- /dev/null
+++ b/source/blender/gpu/opengl/gl_shader_interface.hh
@@ -0,0 +1,63 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup gpu
+ *
+ * GPU shader interface (C --> GLSL)
+ *
+ * Structure detailing needed vertex inputs and resources for a specific shader.
+ * A shader interface can be shared between two similar shaders.
+ */
+
+#pragma once
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_vector.hh"
+
+#include "glew-mx.h"
+
+#include "gpu_shader_interface.hh"
+
+namespace blender::gpu {
+
+class GLVaoCache;
+
+/**
+ * Implementation of Shader interface using OpenGL.
+ **/
+class GLShaderInterface : public ShaderInterface {
+ private:
+ /** Reference to VaoCaches using this interface */
+ Vector<GLVaoCache *> refs_;
+
+ public:
+ GLShaderInterface(GLuint program);
+ ~GLShaderInterface();
+
+ void ref_add(GLVaoCache *ref);
+ void ref_remove(GLVaoCache *ref);
+
+ // bool resource_binding_validate();
+
+ MEM_CXX_CLASS_ALLOC_FUNCS("GLShaderInterface");
+};
+
+} // namespace blender::gpu
diff --git a/source/blender/gpu/opengl/gl_state.cc b/source/blender/gpu/opengl/gl_state.cc
index 3e3695e0b48..7dc3e44c516 100644
--- a/source/blender/gpu/opengl/gl_state.cc
+++ b/source/blender/gpu/opengl/gl_state.cc
@@ -26,6 +26,7 @@
#include "glew-mx.h"
+#include "gl_context.hh"
#include "gl_state.hh"
using namespace blender::gpu;
@@ -53,6 +54,9 @@ GLStateManager::GLStateManager(void) : GPUStateManager()
glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
}
+ /* Limits. */
+ glGetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, line_width_range_);
+
/* Force update using default state. */
current_ = ~state;
current_mutable_ = ~mutable_state;
@@ -65,23 +69,23 @@ void GLStateManager::set_state(const GPUState &state)
GPUState changed = state ^ current_;
if (changed.blend != 0) {
- set_blend(state.blend);
+ set_blend((eGPUBlend)state.blend);
}
if (changed.write_mask != 0) {
- set_write_mask(state.write_mask);
+ set_write_mask((eGPUWriteMask)state.write_mask);
}
if (changed.depth_test != 0) {
- set_depth_test(state.depth_test);
+ set_depth_test((eGPUDepthTest)state.depth_test);
}
if (changed.stencil_test != 0 || changed.stencil_op != 0) {
- set_stencil_test(state.stencil_test, state.stencil_op);
- set_stencil_mask(state.stencil_test, mutable_state);
+ set_stencil_test((eGPUStencilTest)state.stencil_test, (eGPUStencilOp)state.stencil_op);
+ set_stencil_mask((eGPUStencilTest)state.stencil_test, mutable_state);
}
if (changed.clip_distances != 0) {
set_clip_distances(state.clip_distances, current_.clip_distances);
}
if (changed.culling_test != 0) {
- set_backface_culling(state.culling_test);
+ set_backface_culling((eGPUFaceCullTest)state.culling_test);
}
if (changed.logic_op_xor != 0) {
set_logic_op(state.logic_op_xor);
@@ -90,7 +94,7 @@ void GLStateManager::set_state(const GPUState &state)
set_facing(state.invert_facing);
}
if (changed.provoking_vert != 0) {
- set_provoking_vert(state.provoking_vert);
+ set_provoking_vert((eGPUProvokingVertex)state.provoking_vert);
}
if (changed.shadow_bias != 0) {
set_shadow_bias(state.shadow_bias);
@@ -150,7 +154,7 @@ void GLStateManager::set_mutable_state(const GPUStateMutable &state)
if (changed.line_width != 0) {
/* TODO remove, should use wide line shader. */
- glLineWidth(clamp_f(state.line_width, 1.0f, GPU_max_line_width()));
+ glLineWidth(clamp_f(state.line_width, line_width_range_[0], line_width_range_[1]));
}
if (changed.depth_range[0] != 0 || changed.depth_range[1] != 0) {
@@ -160,7 +164,7 @@ void GLStateManager::set_mutable_state(const GPUStateMutable &state)
if (changed.stencil_compare_mask != 0 || changed.stencil_reference != 0 ||
changed.stencil_write_mask != 0) {
- set_stencil_mask(current_.stencil_test, state);
+ set_stencil_mask((eGPUStencilTest)current_.stencil_test, state);
}
current_mutable_ = state;
diff --git a/source/blender/gpu/opengl/gl_state.hh b/source/blender/gpu/opengl/gl_state.hh
index b05fcc5d044..8e806cb3e7a 100644
--- a/source/blender/gpu/opengl/gl_state.hh
+++ b/source/blender/gpu/opengl/gl_state.hh
@@ -31,11 +31,17 @@
namespace blender {
namespace gpu {
+/**
+ * State manager keeping track of the draw state and applying it before drawing.
+ * Opengl Implementation.
+ **/
class GLStateManager : public GPUStateManager {
private:
/** Current state of the GL implementation. Avoids resetting the whole state for every change. */
GPUState current_;
GPUStateMutable current_mutable_;
+ /** Limits. */
+ float line_width_range_[2];
public:
GLStateManager();
diff --git a/source/blender/gpu/opengl/gl_uniform_buffer.cc b/source/blender/gpu/opengl/gl_uniform_buffer.cc
new file mode 100644
index 00000000000..5115034639c
--- /dev/null
+++ b/source/blender/gpu/opengl/gl_uniform_buffer.cc
@@ -0,0 +1,126 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#include "BKE_global.h"
+
+#include "BLI_string.h"
+
+#include "GPU_extensions.h"
+
+#include "gpu_backend.hh"
+#include "gpu_context_private.hh"
+
+#include "gl_backend.hh"
+#include "gl_uniform_buffer.hh"
+
+namespace blender::gpu {
+
+/* -------------------------------------------------------------------- */
+/** \name Creation & Deletion
+ * \{ */
+
+GLUniformBuf::GLUniformBuf(size_t size, const char *name) : UniformBuf(size, name)
+{
+ /* Do not create ubo GL buffer here to allow allocation from any thread. */
+}
+
+GLUniformBuf::~GLUniformBuf()
+{
+ GLBackend::get()->buf_free(ubo_id_);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Data upload / update
+ * \{ */
+
+void GLUniformBuf::init(void)
+{
+ BLI_assert(GPU_context_active_get());
+
+ glGenBuffers(1, &ubo_id_);
+ glBindBuffer(GL_UNIFORM_BUFFER, ubo_id_);
+ glBufferData(GL_UNIFORM_BUFFER, size_in_bytes_, NULL, GL_DYNAMIC_DRAW);
+
+#ifndef __APPLE__
+ if ((G.debug & G_DEBUG_GPU) && (GLEW_VERSION_4_3 || GLEW_KHR_debug)) {
+ char sh_name[64];
+ SNPRINTF(sh_name, "UBO-%s", name_);
+ glObjectLabel(GL_BUFFER, ubo_id_, -1, sh_name);
+ }
+#endif
+}
+
+void GLUniformBuf::update(const void *data)
+{
+ if (ubo_id_ == 0) {
+ this->init();
+ }
+ glBindBuffer(GL_UNIFORM_BUFFER, ubo_id_);
+ glBufferSubData(GL_UNIFORM_BUFFER, 0, size_in_bytes_, data);
+ glBindBuffer(GL_UNIFORM_BUFFER, 0);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Usage
+ * \{ */
+
+void GLUniformBuf::bind(int slot)
+{
+ if (slot >= GPU_max_ubo_binds()) {
+ fprintf(stderr,
+ "Error: Trying to bind \"%s\" ubo to slot %d which is above the reported limit of %d.",
+ name_,
+ slot,
+ GPU_max_ubo_binds());
+ return;
+ }
+
+ if (ubo_id_ == 0) {
+ this->init();
+ }
+
+ if (data_ != NULL) {
+ this->update(data_);
+ MEM_SAFE_FREE(data_);
+ }
+
+ slot_ = slot;
+ glBindBufferBase(GL_UNIFORM_BUFFER, slot_, ubo_id_);
+}
+
+void GLUniformBuf::unbind(void)
+{
+#ifdef DEBUG
+ /* NOTE: This only unbinds the last bound slot. */
+ glBindBufferBase(GL_UNIFORM_BUFFER, slot_, 0);
+#endif
+ slot_ = 0;
+}
+
+/** \} */
+
+} // namespace blender::gpu \ No newline at end of file
diff --git a/source/blender/gpu/opengl/gl_uniform_buffer.hh b/source/blender/gpu/opengl/gl_uniform_buffer.hh
new file mode 100644
index 00000000000..b71356c4121
--- /dev/null
+++ b/source/blender/gpu/opengl/gl_uniform_buffer.hh
@@ -0,0 +1,58 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#pragma once
+
+#include "MEM_guardedalloc.h"
+
+#include "gpu_uniform_buffer_private.hh"
+
+#include "glew-mx.h"
+
+namespace blender {
+namespace gpu {
+
+/**
+ * Implementation of Uniform Buffers using OpenGL.
+ **/
+class GLUniformBuf : public UniformBuf {
+ private:
+ int slot_ = -1;
+ GLuint ubo_id_ = 0;
+
+ public:
+ GLUniformBuf(size_t size, const char *name);
+ ~GLUniformBuf();
+
+ void update(const void *data) override;
+ void bind(int slot) override;
+ void unbind(void) override;
+
+ private:
+ void init(void);
+
+ MEM_CXX_CLASS_ALLOC_FUNCS("GLUniformBuf");
+};
+
+} // namespace gpu
+} // namespace blender
diff --git a/source/blender/gpu/opengl/gl_vertex_array.cc b/source/blender/gpu/opengl/gl_vertex_array.cc
index 907dc37e46f..b2d2445f113 100644
--- a/source/blender/gpu/opengl/gl_vertex_array.cc
+++ b/source/blender/gpu/opengl/gl_vertex_array.cc
@@ -23,9 +23,9 @@
#include "GPU_glew.h"
-#include "GPU_shader_interface.h"
#include "GPU_vertex_buffer.h"
+#include "gpu_shader_interface.hh"
#include "gpu_vertex_format_private.h"
#include "gl_batch.hh"
@@ -33,14 +33,14 @@
#include "gl_vertex_array.hh"
-using namespace blender::gpu;
+namespace blender::gpu {
/* -------------------------------------------------------------------- */
/** \name Vertex Array Bindings
* \{ */
/* Returns enabled vertex pointers as a bitflag (one bit per attrib). */
-static uint16_t vbo_bind(const GPUShaderInterface *interface,
+static uint16_t vbo_bind(const ShaderInterface *interface,
const GPUVertFormat *format,
uint v_first,
uint v_len,
@@ -68,7 +68,7 @@ static uint16_t vbo_bind(const GPUShaderInterface *interface,
for (uint n_idx = 0; n_idx < a->name_len; n_idx++) {
const char *name = GPU_vertformat_attr_name_get(format, a, n_idx);
- const GPUShaderInput *input = GPU_shaderinterface_attr(interface, name);
+ const ShaderInput *input = interface->attr_get(name);
if (input == NULL) {
continue;
@@ -111,10 +111,10 @@ static uint16_t vbo_bind(const GPUShaderInterface *interface,
/* Update the Attrib Binding of the currently bound VAO. */
void GLVertArray::update_bindings(const GLuint vao,
const GPUBatch *batch,
- const GPUShaderInterface *interface,
+ const ShaderInterface *interface,
const int base_instance)
{
- uint16_t attr_mask = interface->enabled_attr_mask;
+ uint16_t attr_mask = interface->enabled_attr_mask_;
glBindVertexArray(vao);
@@ -156,3 +156,5 @@ void GLVertArray::update_bindings(const GLuint vao,
}
/** \} */
+
+} // namespace blender::gpu \ No newline at end of file
diff --git a/source/blender/gpu/opengl/gl_vertex_array.hh b/source/blender/gpu/opengl/gl_vertex_array.hh
index 6da414d7e62..59cd50ad7b8 100644
--- a/source/blender/gpu/opengl/gl_vertex_array.hh
+++ b/source/blender/gpu/opengl/gl_vertex_array.hh
@@ -26,7 +26,7 @@
#include "glew-mx.h"
#include "GPU_batch.h"
-#include "GPU_shader_interface.h"
+#include "gl_shader_interface.hh"
namespace blender {
namespace gpu {
@@ -35,7 +35,7 @@ namespace GLVertArray {
void update_bindings(const GLuint vao,
const GPUBatch *batch,
- const GPUShaderInterface *interface,
+ const ShaderInterface *interface,
const int base_instance);
} // namespace GLVertArray
diff --git a/source/blender/imbuf/intern/colormanagement.c b/source/blender/imbuf/intern/colormanagement.c
index 2c42d59a2d9..6dd4d14cbc7 100644
--- a/source/blender/imbuf/intern/colormanagement.c
+++ b/source/blender/imbuf/intern/colormanagement.c
@@ -1267,12 +1267,12 @@ void IMB_colormanagement_check_file_config(Main *bmain)
/* check sequencer strip input color space settings */
Sequence *seq;
- SEQ_BEGIN (scene->ed, seq) {
+ SEQ_ALL_BEGIN (scene->ed, seq) {
if (seq->strip) {
colormanage_check_colorspace_settings(&seq->strip->colorspace_settings, "sequencer strip");
}
}
- SEQ_END;
+ SEQ_ALL_END;
}
/* ** check input color space settings ** */
diff --git a/source/blender/io/alembic/tests/abc_export_test.cc b/source/blender/io/alembic/tests/abc_export_test.cc
index 5c2b505958e..c602868b07e 100644
--- a/source/blender/io/alembic/tests/abc_export_test.cc
+++ b/source/blender/io/alembic/tests/abc_export_test.cc
@@ -36,6 +36,8 @@ class AlembicExportTest : public testing::Test {
bmain = BKE_main_new();
+ DEG_register_node_types();
+
/* TODO(sergey): Pass scene layer somehow? */
ViewLayer *view_layer = (ViewLayer *)scene.view_layers.first;
depsgraph = DEG_graph_new(bmain, &scene, view_layer, DAG_EVAL_RENDER);
@@ -45,6 +47,7 @@ class AlembicExportTest : public testing::Test {
{
BKE_main_free(bmain);
DEG_graph_free(depsgraph);
+ DEG_free_node_types();
deleteArchive();
}
diff --git a/source/blender/io/collada/BlenderContext.cpp b/source/blender/io/collada/BlenderContext.cpp
index a9783a9b9c4..1d3bffacb79 100644
--- a/source/blender/io/collada/BlenderContext.cpp
+++ b/source/blender/io/collada/BlenderContext.cpp
@@ -123,7 +123,7 @@ bContext *BlenderContext::get_context()
Depsgraph *BlenderContext::get_depsgraph()
{
if (!depsgraph) {
- depsgraph = BKE_scene_get_depsgraph(main, scene, view_layer, true);
+ depsgraph = BKE_scene_ensure_depsgraph(main, scene, view_layer);
}
return depsgraph;
}
diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h
index feda4ba43eb..e16a22f5459 100644
--- a/source/blender/makesdna/DNA_ID.h
+++ b/source/blender/makesdna/DNA_ID.h
@@ -686,10 +686,6 @@ typedef enum IDRecalcFlag {
ID_RECALC_PARAMETERS = (1 << 21),
- /* Makes it so everything what depends on time.
- * Basically, the same what changing frame in a timeline will do. */
- ID_RECALC_TIME = (1 << 22),
-
/* Input has changed and datablock is to be reload from disk.
* Applies to movie clips to inform that copy-on-written version is to be refreshed for the new
* input file or for color space changes. */
diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h
index 3873763e3e0..033a69d230e 100644
--- a/source/blender/makesdna/DNA_brush_types.h
+++ b/source/blender/makesdna/DNA_brush_types.h
@@ -343,6 +343,7 @@ typedef enum eBrushClothDeformType {
BRUSH_CLOTH_DEFORM_PINCH_PERPENDICULAR = 4,
BRUSH_CLOTH_DEFORM_INFLATE = 5,
BRUSH_CLOTH_DEFORM_EXPAND = 6,
+ BRUSH_CLOTH_DEFORM_SNAKE_HOOK = 7,
} eBrushClothDeformType;
typedef enum eBrushSmoothDeformType {
diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h
index 37cda5f0e12..ac9766bc6a5 100644
--- a/source/blender/makesdna/DNA_mesh_types.h
+++ b/source/blender/makesdna/DNA_mesh_types.h
@@ -310,6 +310,7 @@ enum {
REMESH_VOXEL = 0,
REMESH_QUAD = 1,
REMESH_TET = 2,
+ REMESH_TETLATTICE = 3,
};
/* Subsurf Type */
diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h
index b92c9f42a73..2839d826df9 100644
--- a/source/blender/makesdna/DNA_modifier_types.h
+++ b/source/blender/makesdna/DNA_modifier_types.h
@@ -862,7 +862,8 @@ typedef struct BooleanModifierData {
struct Object *object;
char operation;
- char _pad[2];
+ char solver;
+ char _pad[1];
char bm_flag;
float double_threshold;
} BooleanModifierData;
@@ -873,7 +874,12 @@ typedef enum {
eBooleanModifierOp_Difference = 2,
} BooleanModifierOp;
-/* bm_flag (only used when G_DEBUG) */
+typedef enum {
+ eBooleanModifierSolver_Fast = 0,
+ eBooleanModifierSolver_Exact = 1,
+} BooleanModifierSolver;
+
+/* bm_flag only used when G_DEBUG. */
enum {
eBooleanModifierBMeshFlag_BMesh_Separate = (1 << 0),
eBooleanModifierBMeshFlag_BMesh_NoDissolve = (1 << 1),
diff --git a/source/blender/makesdna/DNA_object_force_types.h b/source/blender/makesdna/DNA_object_force_types.h
index 33b16350354..da9ea45d5d4 100644
--- a/source/blender/makesdna/DNA_object_force_types.h
+++ b/source/blender/makesdna/DNA_object_force_types.h
@@ -193,8 +193,6 @@ typedef struct SBVertex {
float vec[4];
} SBVertex;
-typedef struct ADMMPDInterfaceData ADMMPDInterfaceData;
-
/* Container for data that is shared among CoW copies.
*
* This is placed in a separate struct so that values can be changed
@@ -202,6 +200,7 @@ typedef struct ADMMPDInterfaceData ADMMPDInterfaceData;
typedef struct SoftBody_Shared {
struct PointCache *pointcache;
struct ListBase ptcaches;
+ struct ListBase *admmpd_list;
} SoftBody_Shared;
typedef struct SoftBody {
@@ -212,31 +211,28 @@ typedef struct SoftBody {
/** Not saved in file. */
struct BodySpring *bspring;
- struct ADMMPDInterfaceData *admmpd;
-
/* ADMM-PD settings */
- int solver_mode; // 0=legacy, 1=admmpd
- int admmpd_mesh_mode; // 0=embedded, 1=tetgen, 2=cloth
- int admmpd_substeps; // break time step into smaller bits
- int admmpd_max_admm_iters; // max solver iterations
- int admmpd_self_collision; // 0 or 1
- int admmpd_material; // see enum
- int admmpd_embed_res; // embedded resolution depth
- float admmpd_converge_eps; // convergence epsilon
- float admmpd_youngs_exp; // Youngs mod exponent
- float admmpd_poisson; // Poisson ratio
- float admmpd_density_kgm3; // unit-density of object
- float admmpd_ck_exp; // collision stiffness exponent (10^n)
- float admmpd_pk_exp; // goal stiffness exponent (10^n)
- float admmpd_floor_z; // floor position
- float admmpd_gravity; // in m/s^2
- float admmpd_strainlimit_min; // [0,1]
- float admmpd_strainlimit_max; // [1,100]
- int admmpd_maxthreads; // -1 = auto
- int admmpd_loglevel; // 0=none, 1=low, 2=high
- int admmpd_linsolver; // global step
+ int solver_mode; /* 0=legacy, 0=admmpd */
+ int admmpd_mesh_mode; /* 0=embedded, 1=tetgen, 2=cloth */
+ int admmpd_substeps; /* break time step into smaller bits */
+ int admmpd_max_admm_iters; /* max solver iterations */
+ int admmpd_self_collision; /* 0 or 1 */
+ int admmpd_material; /* see enum */
+ int admmpd_embed_res; /* embedded resolution depth */
+ float admmpd_converge_eps; /* convergence epsilon */
+ float admmpd_youngs_exp; /* Youngs mod exponent */
+ float admmpd_poisson; /* Poisson ratio */
+ float admmpd_density_kgm3; /* unit-density of object */
+ float admmpd_ck_exp; /* collision stiffness exponent (10^n) */
+ float admmpd_pk_exp; /* goal stiffness exponent (10^n) */
+ float admmpd_floor_z; /* floor position */
+ float admmpd_gravity; /* in m/s^2 */
+ float admmpd_strainlimit_min; /* [0,1] */
+ float admmpd_strainlimit_max; /* [1,100] */
+ int admmpd_maxthreads; /* -1 = auto */
+ int admmpd_loglevel; /* 0=none, 1=low, 2=high */
+ int admmpd_linsolver; /* global step */
char admmpd_namedVG_selfcollision[64];
- // int admmpd_pad; // padding
char _pad;
char msg_lock;
@@ -435,8 +431,8 @@ typedef struct SoftBody {
#define SBC_MODE_AVGMINMAX 4
/* sb->solver_mode */
-#define SOLVER_MODE_ADMMPD 0
-#define SOLVER_MODE_LEGACY 1
+#define SOLVER_MODE_LEGACY 0
+#define SOLVER_MODE_ADMMPD 1
#ifdef __cplusplus
}
diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h
index 62c072831b4..586c704e0f1 100644
--- a/source/blender/makesdna/DNA_object_types.h
+++ b/source/blender/makesdna/DNA_object_types.h
@@ -108,15 +108,6 @@ enum {
BOUNDBOX_DIRTY = (1 << 1),
};
-typedef struct LodLevel {
- struct LodLevel *next, *prev;
- struct Object *source;
- int flags;
- float distance;
- char _pad0[4];
- int obhysteresis;
-} LodLevel;
-
struct CustomData_MeshMasks;
/* Not saved in file! */
@@ -393,10 +384,6 @@ typedef struct Object {
char empty_image_flag;
char _pad8[5];
- /** Contains data for levels of detail. */
- ListBase lodlevels;
- LodLevel *currentlod;
-
struct PreviewImage *preview;
/** Runtime evaluation data (keep last). */
diff --git a/source/blender/makesdna/DNA_outliner_types.h b/source/blender/makesdna/DNA_outliner_types.h
index 46c8b1570e3..52d466fdbdd 100644
--- a/source/blender/makesdna/DNA_outliner_types.h
+++ b/source/blender/makesdna/DNA_outliner_types.h
@@ -109,6 +109,8 @@ enum {
#define TSE_SCENE_COLLECTION_BASE 39
#define TSE_VIEW_COLLECTION_BASE 40
#define TSE_SCENE_OBJECTS_BASE 41
+#define TSE_GPENCIL_EFFECT_BASE 42
+#define TSE_GPENCIL_EFFECT 43
/* Check whether given TreeStoreElem should have a real ID in its ->id member. */
#define TSE_IS_REAL_ID(_tse) \
diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h
index e8c4d5cde20..8e4063b36eb 100644
--- a/source/blender/makesdna/DNA_screen_types.h
+++ b/source/blender/makesdna/DNA_screen_types.h
@@ -534,9 +534,8 @@ typedef enum eScreen_Redraws_Flag {
/** #Panel.flag */
enum {
PNL_SELECT = (1 << 0),
- PNL_CLOSEDX = (1 << 1),
- PNL_CLOSEDY = (1 << 2),
- PNL_CLOSED = (PNL_CLOSEDX | PNL_CLOSEDY),
+ PNL_UNUSED_1 = (1 << 1), /* Cleared */
+ PNL_CLOSED = (1 << 2),
/* PNL_TABBED = (1 << 3), */ /*UNUSED*/
/* PNL_OVERLAP = (1 << 4), */ /*UNUSED*/
PNL_PIN = (1 << 5),
diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h
index 136fe3744ef..ec46d2680ca 100644
--- a/source/blender/makesdna/DNA_userdef_types.h
+++ b/source/blender/makesdna/DNA_userdef_types.h
@@ -327,7 +327,7 @@ typedef struct ThemeSpace {
unsigned char syntaxd[4], syntaxr[4]; // in nodespace used for distort
unsigned char line_numbers[4];
- char _pad6[7];
+ char _pad6[3];
unsigned char nodeclass_output[4], nodeclass_filter[4];
unsigned char nodeclass_vector[4], nodeclass_texture[4];
@@ -372,8 +372,6 @@ typedef struct ThemeSpace {
/** Two uses, for uvs with modifier applied on mesh and uvs during painting. */
unsigned char uv_shadow[4];
- /** Uvs of other objects. */
- unsigned char uv_others[4];
/** Outliner - filter match. */
unsigned char match[4];
diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c
index 7aaedbff1ce..29e29961028 100644
--- a/source/blender/makesdna/intern/makesdna.c
+++ b/source/blender/makesdna/intern/makesdna.c
@@ -1373,6 +1373,7 @@ static int make_structDNA(const char *base_directory,
/* write a simple enum with all structs offsets,
* should only be accessed via SDNA_TYPE_FROM_STRUCT macro */
{
+ fprintf(file_offsets, "#pragma once\n");
fprintf(file_offsets, "#define SDNA_TYPE_FROM_STRUCT(id) _SDNA_TYPE_##id\n");
fprintf(file_offsets, "enum {\n");
for (i = 0; i < structs_len; i++) {
diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt
index 1896813bdb3..303005a0f9e 100644
--- a/source/blender/makesrna/intern/CMakeLists.txt
+++ b/source/blender/makesrna/intern/CMakeLists.txt
@@ -352,6 +352,10 @@ if(WITH_XR_OPENXR)
add_definitions(-DWITH_XR_OPENXR)
endif()
+if(WITH_GMP)
+ add_definitions(-DWITH_GMP)
+endif()
+
# Build makesrna executable
blender_include_dirs(
.
diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c
index f48a7e6715d..0b923eb5635 100644
--- a/source/blender/makesrna/intern/rna_brush.c
+++ b/source/blender/makesrna/intern/rna_brush.c
@@ -2016,6 +2016,7 @@ static void rna_def_brush(BlenderRNA *brna)
{BRUSH_CLOTH_DEFORM_INFLATE, "INFLATE", 0, "Inflate", ""},
{BRUSH_CLOTH_DEFORM_GRAB, "GRAB", 0, "Grab", ""},
{BRUSH_CLOTH_DEFORM_EXPAND, "EXPAND", 0, "Expand", ""},
+ {BRUSH_CLOTH_DEFORM_SNAKE_HOOK, "SNAKE_HOOK", 0, "Snake Hook", ""},
{0, NULL, 0, NULL, NULL},
};
diff --git a/source/blender/makesrna/intern/rna_color.c b/source/blender/makesrna/intern/rna_color.c
index cfc20db12f4..ab4d936ae34 100644
--- a/source/blender/makesrna/intern/rna_color.c
+++ b/source/blender/makesrna/intern/rna_color.c
@@ -623,13 +623,13 @@ static void rna_ColorManagedColorspaceSettings_reload_update(Main *bmain,
bool seq_found = false;
if (&scene->sequencer_colorspace_settings != colorspace_settings) {
- SEQ_BEGIN (scene->ed, seq) {
+ SEQ_ALL_BEGIN (scene->ed, seq) {
if (seq->strip && &seq->strip->colorspace_settings == colorspace_settings) {
seq_found = true;
break;
}
}
- SEQ_END;
+ SEQ_ALL_END;
}
if (seq_found) {
@@ -643,10 +643,10 @@ static void rna_ColorManagedColorspaceSettings_reload_update(Main *bmain,
BKE_sequence_invalidate_cache_preprocessed(scene, seq);
}
else {
- SEQ_BEGIN (scene->ed, seq) {
+ SEQ_ALL_BEGIN (scene->ed, seq) {
BKE_sequence_free_anim(seq);
}
- SEQ_END;
+ SEQ_ALL_END;
}
WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, NULL);
diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h
index 6254e40a410..ad43202d850 100644
--- a/source/blender/makesrna/intern/rna_internal.h
+++ b/source/blender/makesrna/intern/rna_internal.h
@@ -455,10 +455,10 @@ void RNA_def_main_cachefiles(BlenderRNA *brna, PropertyRNA *cprop);
void RNA_def_main_paintcurves(BlenderRNA *brna, PropertyRNA *cprop);
void RNA_def_main_workspaces(BlenderRNA *brna, PropertyRNA *cprop);
void RNA_def_main_lightprobes(BlenderRNA *brna, PropertyRNA *cprop);
-#ifdef WITH_PARTICLE_NODES
+#ifdef WITH_HAIR_NODES
void RNA_def_main_hairs(BlenderRNA *brna, PropertyRNA *cprop);
#endif
-#ifdef WITH_HAIR_NODES
+#ifdef WITH_PARTICLE_NODES
void RNA_def_main_pointclouds(BlenderRNA *brna, PropertyRNA *cprop);
#endif
void RNA_def_main_volumes(BlenderRNA *brna, PropertyRNA *cprop);
diff --git a/source/blender/makesrna/intern/rna_layer.c b/source/blender/makesrna/intern/rna_layer.c
index e7a898b97ae..fc5e957bba6 100644
--- a/source/blender/makesrna/intern/rna_layer.c
+++ b/source/blender/makesrna/intern/rna_layer.c
@@ -184,10 +184,7 @@ static PointerRNA rna_ViewLayer_depsgraph_get(PointerRNA *ptr)
if (GS(id->name) == ID_SCE) {
Scene *scene = (Scene *)id;
ViewLayer *view_layer = (ViewLayer *)ptr->data;
- // NOTE: We don't allocate new depsgraph here, so the bmain is ignored. So it's easier to pass
- // NULL.
- // Still weak though.
- Depsgraph *depsgraph = BKE_scene_get_depsgraph(NULL, scene, view_layer, false);
+ Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer);
return rna_pointer_inherit_refine(ptr, &RNA_Depsgraph, depsgraph);
}
return PointerRNA_NULL;
@@ -206,7 +203,7 @@ static void rna_ViewLayer_update_tagged(ID *id_ptr,
ReportList *reports)
{
Scene *scene = (Scene *)id_ptr;
- Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, true);
+ Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer);
if (DEG_is_evaluating(depsgraph)) {
BKE_report(reports, RPT_ERROR, "Dependency graph update requested during evaluation");
diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c
index 53de811eaa8..c5cceecb859 100644
--- a/source/blender/makesrna/intern/rna_mesh.c
+++ b/source/blender/makesrna/intern/rna_mesh.c
@@ -58,7 +58,8 @@ const EnumPropertyItem rna_enum_mesh_delimit_mode_items[] = {
static const EnumPropertyItem rna_enum_mesh_remesh_mode_items[] = {
{REMESH_VOXEL, "VOXEL", 0, "Voxel", "Use the voxel remesher"},
{REMESH_QUAD, "QUAD", 0, "Quad", "Use the quad remesher"},
- {REMESH_TET, "TET", 0, "Tet", "Use the tetrahedralize remesher"},
+ {REMESH_TET, "TET", 0, "TetGen", "Use the tetrahedralize remesher"},
+ {REMESH_TETLATTICE, "TETLATTICE", 0, "Tet lattice", "Use the tet-lattice remesher"},
{0, NULL, 0, NULL, NULL},
};
diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c
index 0338a094d14..d9e151e5f73 100644
--- a/source/blender/makesrna/intern/rna_modifier.c
+++ b/source/blender/makesrna/intern/rna_modifier.c
@@ -2819,6 +2819,16 @@ static void rna_def_modifier_boolean(BlenderRNA *brna)
{0, NULL, 0, NULL, NULL},
};
+ static const EnumPropertyItem prop_solver_items[] = {
+ {eBooleanModifierSolver_Fast,
+ "FAST",
+ 0,
+ "Fast",
+ "Simple solver for the best performance, without support for overlapping geometry"},
+ {eBooleanModifierSolver_Exact, "EXACT", 0, "Exact", "Advanced solver for the best result"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
srna = RNA_def_struct(brna, "BooleanModifier", "Modifier");
RNA_def_struct_ui_text(srna, "Boolean Modifier", "Boolean operations modifier");
RNA_def_struct_sdna(srna, "BooleanModifierData");
@@ -2847,6 +2857,12 @@ static void rna_def_modifier_boolean(BlenderRNA *brna)
prop, "Overlap Threshold", "Threshold for checking overlapping geometry");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
+ prop = RNA_def_property(srna, "solver", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, prop_solver_items);
+ RNA_def_property_enum_default(prop, eBooleanModifierSolver_Exact);
+ RNA_def_property_ui_text(prop, "Solver", "Method for calculating booleans");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
/* BMesh debugging options, only used when G_DEBUG is set */
/* BMesh intersection options */
diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index af07185ab4a..3237f33835e 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -5434,7 +5434,8 @@ static void def_sh_bevel(StructRNA *srna)
prop = RNA_def_property(srna, "samples", PROP_INT, PROP_UNSIGNED);
RNA_def_property_int_sdna(prop, NULL, "custom1");
- RNA_def_property_range(prop, 2, 16);
+ RNA_def_property_range(prop, 2, 128);
+ RNA_def_property_ui_range(prop, 2, 16, 1, 1);
RNA_def_property_ui_text(prop, "Samples", "Number of rays to trace per shader evaluation");
RNA_def_property_update(prop, 0, "rna_Node_update");
}
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index 3ec7963a81e..e470a8ddb85 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -929,7 +929,7 @@ static const char *rna_Scene_statistics_string_get(Scene *scene,
ReportList *reports,
ViewLayer *view_layer)
{
- if (BKE_scene_find_from_view_layer(bmain, view_layer) != scene) {
+ if (!BKE_scene_has_view_layer(scene, view_layer)) {
BKE_reportf(reports,
RPT_ERROR,
"View Layer '%s' not found in scene '%s'",
@@ -1883,11 +1883,10 @@ static void object_simplify_update(Object *ob)
}
if (ob->instance_collection) {
- CollectionObject *cob;
-
- for (cob = ob->instance_collection->gobject.first; cob; cob = cob->next) {
- object_simplify_update(cob->ob);
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (ob->instance_collection, ob_collection) {
+ object_simplify_update(ob_collection);
}
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
}
}
@@ -5936,7 +5935,7 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
RNA_def_property_int_funcs(prop, "rna_RenderSettings_threads_get", NULL, NULL);
RNA_def_property_ui_text(prop,
"Threads",
- "Number of CPU threads to use simultaneously while rendering "
+ "Maximum number of CPU cores to use simultaneously while rendering "
"(for multi-core/CPU systems)");
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
diff --git a/source/blender/makesrna/intern/rna_scene_api.c b/source/blender/makesrna/intern/rna_scene_api.c
index d258677c606..ff887e53965 100644
--- a/source/blender/makesrna/intern/rna_scene_api.c
+++ b/source/blender/makesrna/intern/rna_scene_api.c
@@ -72,7 +72,7 @@ static void rna_Scene_frame_set(Scene *scene, Main *bmain, int frame, float subf
for (ViewLayer *view_layer = scene->view_layers.first; view_layer != NULL;
view_layer = view_layer->next) {
- Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, true);
+ Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer);
BKE_scene_graph_update_for_newframe(depsgraph);
}
diff --git a/source/blender/makesrna/intern/rna_space_api.c b/source/blender/makesrna/intern/rna_space_api.c
index 28fdc5fb60f..e4c0ade1533 100644
--- a/source/blender/makesrna/intern/rna_space_api.c
+++ b/source/blender/makesrna/intern/rna_space_api.c
@@ -49,7 +49,7 @@ static void rna_RegionView3D_update(ID *id, RegionView3D *rv3d, bContext *C)
if (WM_window_get_active_screen(win) == screen) {
Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
- Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, true);
+ Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer);
ED_view3d_update_viewmat(depsgraph, scene, v3d, region, NULL, NULL, NULL, false);
break;
diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c
index 90873fa73f4..699e08302e7 100644
--- a/source/blender/makesrna/intern/rna_userdef.c
+++ b/source/blender/makesrna/intern/rna_userdef.c
@@ -2478,6 +2478,11 @@ static void rna_def_userdef_theme_space_file(BlenderRNA *brna)
RNA_def_property_array(prop, 3);
RNA_def_property_ui_text(prop, "Selected File", "");
RNA_def_property_update(prop, 0, "rna_userdef_theme_update");
+
+ prop = RNA_def_property(srna, "row_alternate", PROP_FLOAT, PROP_COLOR_GAMMA);
+ RNA_def_property_array(prop, 4);
+ RNA_def_property_ui_text(prop, "Alternate Rows", "Overlay color on every other row");
+ RNA_def_property_update(prop, 0, "rna_userdef_theme_update");
}
static void rna_def_userdef_theme_space_outliner(BlenderRNA *brna)
@@ -3039,12 +3044,6 @@ static void rna_def_userdef_theme_space_image(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Texture paint/Modifier UVs", "");
RNA_def_property_update(prop, 0, "rna_userdef_theme_update");
- prop = RNA_def_property(srna, "uv_others", PROP_FLOAT, PROP_COLOR_GAMMA);
- RNA_def_property_float_sdna(prop, NULL, "uv_others");
- RNA_def_property_array(prop, 4);
- RNA_def_property_ui_text(prop, "Other Object UVs", "");
- RNA_def_property_update(prop, 0, "rna_userdef_theme_update");
-
prop = RNA_def_property(srna, "frame_current", PROP_FLOAT, PROP_COLOR_GAMMA);
RNA_def_property_float_sdna(prop, NULL, "cframe");
RNA_def_property_array(prop, 3);
diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt
index cf87e3598b6..d2ac9dc85de 100644
--- a/source/blender/modifiers/CMakeLists.txt
+++ b/source/blender/modifiers/CMakeLists.txt
@@ -171,6 +171,10 @@ if(WITH_CYCLES)
add_definitions(-DWITH_CYCLES)
endif()
+if(WITH_GMP)
+ add_definitions(-DWITH_GMP)
+endif()
+
# So we can have special tricks in modifier system.
add_definitions(${GL_DEFINITIONS})
diff --git a/source/blender/modifiers/intern/MOD_boolean.c b/source/blender/modifiers/intern/MOD_boolean.c
index 08fd7fb229d..0c8b8f6af3e 100644
--- a/source/blender/modifiers/intern/MOD_boolean.c
+++ b/source/blender/modifiers/intern/MOD_boolean.c
@@ -62,6 +62,7 @@
#include "bmesh.h"
#include "bmesh_tools.h"
+#include "tools/bmesh_boolean.h"
#include "tools/bmesh_intersect.h"
#ifdef DEBUG_TIME
@@ -75,6 +76,11 @@ static void initData(ModifierData *md)
bmd->double_threshold = 1e-6f;
bmd->operation = eBooleanModifierOp_Difference;
+#ifdef WITH_GMP
+ bmd->solver = eBooleanModifierSolver_Exact;
+#else
+ bmd->solver = eBooleanModifierSolver_Fast;
+#endif
}
static bool isDisabled(const struct Scene *UNUSED(scene),
@@ -315,19 +321,30 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
0;
}
- BM_mesh_intersect(bm,
- looptris,
- tottri,
- bm_face_isect_pair,
- NULL,
- false,
- use_separate,
- use_dissolve,
- use_island_connect,
- false,
- false,
- bmd->operation,
- bmd->double_threshold);
+#ifdef WITH_GMP
+ bool use_exact = bmd->solver == eBooleanModifierSolver_Exact;
+#else
+ bool use_exact = false;
+#endif
+
+ if (use_exact) {
+ BM_mesh_boolean(bm, looptris, tottri, bm_face_isect_pair, NULL, false, bmd->operation);
+ }
+ else {
+ BM_mesh_intersect(bm,
+ looptris,
+ tottri,
+ bm_face_isect_pair,
+ NULL,
+ false,
+ use_separate,
+ use_dissolve,
+ use_island_connect,
+ false,
+ false,
+ bmd->operation,
+ bmd->double_threshold);
+ }
MEM_freeN(looptris);
}
@@ -374,7 +391,18 @@ static void panel_draw(const bContext *C, Panel *panel)
uiLayoutSetPropSep(layout, true);
uiItemR(layout, &ptr, "object", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "double_threshold", 0, NULL, ICON_NONE);
+
+#ifdef WITH_GMP
+ bool use_exact = RNA_enum_get(&ptr, "solver") == eBooleanModifierSolver_Exact;
+#else
+ bool use_exact = false;
+#endif
+ if (!use_exact) {
+ uiItemR(layout, &ptr, "double_threshold", 0, NULL, ICON_NONE);
+ }
+#ifdef WITH_GMP
+ uiItemR(layout, &ptr, "solver", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+#endif
if (G.debug) {
uiLayout *col = uiLayoutColumn(layout, true);
@@ -394,7 +422,7 @@ ModifierTypeInfo modifierType_Boolean = {
/* structName */ "BooleanModifierData",
/* structSize */ sizeof(BooleanModifierData),
/* type */ eModifierTypeType_Nonconstructive,
- /* flags */ eModifierTypeFlag_AcceptsMesh,
+ /* flags */ eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_SupportsEditmode,
/* copyData */ BKE_modifier_copydata_generic,
diff --git a/source/blender/modifiers/intern/MOD_meshdeform.c b/source/blender/modifiers/intern/MOD_meshdeform.c
index ae031bffb04..4dee70608f8 100644
--- a/source/blender/modifiers/intern/MOD_meshdeform.c
+++ b/source/blender/modifiers/intern/MOD_meshdeform.c
@@ -37,7 +37,6 @@
#include "BKE_context.h"
#include "BKE_deform.h"
#include "BKE_editmesh.h"
-#include "BKE_global.h"
#include "BKE_lib_id.h"
#include "BKE_lib_query.h"
#include "BKE_mesh.h"
@@ -332,12 +331,7 @@ static void meshdeform_vert_task(void *__restrict userdata,
if (totweight > 0.0f) {
mul_v3_fl(co, fac / totweight);
mul_m3_v3(data->icagemat, co);
- if (G.debug_value != 527) {
- add_v3_v3(vertexCos[iter], co);
- }
- else {
- copy_v3_v3(vertexCos[iter], co);
- }
+ add_v3_v3(vertexCos[iter], co);
}
}
@@ -353,9 +347,8 @@ static void meshdeformModifier_do(ModifierData *md,
Mesh *cagemesh;
MDeformVert *dvert = NULL;
float imat[4][4], cagemat[4][4], iobmat[4][4], icagemat[3][3], cmat[4][4];
- float co[3], (*dco)[3] = NULL, (*bindcagecos)[3];
+ float(*dco)[3] = NULL, (*bindcagecos)[3];
int a, totvert, totcagevert, defgrp_index;
- float(*cagecos)[3] = NULL;
MeshdeformUserdata data;
static int recursive_bind_sentinel = 0;
@@ -406,7 +399,7 @@ static void meshdeformModifier_do(ModifierData *md,
/* verify we have compatible weights */
totvert = numVerts;
- totcagevert = cagemesh->totvert;
+ totcagevert = BKE_mesh_wrapper_vert_len(cagemesh);
if (mmd->totvert != totvert) {
BKE_modifier_set_error(md, "Vertices changed from %d to %d", mmd->totvert, totvert);
@@ -422,27 +415,22 @@ static void meshdeformModifier_do(ModifierData *md,
goto finally;
}
- /* setup deformation data */
- cagecos = BKE_mesh_vert_coords_alloc(cagemesh, NULL);
- bindcagecos = (float(*)[3])mmd->bindcagecos;
-
/* We allocate 1 element extra to make it possible to
* load the values to SSE registers, which are float4.
*/
dco = MEM_calloc_arrayN((totcagevert + 1), sizeof(*dco), "MDefDco");
zero_v3(dco[totcagevert]);
+
+ /* setup deformation data */
+ BKE_mesh_wrapper_vert_coords_copy(cagemesh, dco, totcagevert);
+ bindcagecos = (float(*)[3])mmd->bindcagecos;
+
for (a = 0; a < totcagevert; a++) {
/* get cage vertex in world space with binding transform */
- copy_v3_v3(co, cagecos[a]);
-
- if (G.debug_value != 527) {
- mul_m4_v3(mmd->bindmat, co);
- /* compute difference with world space bind coord */
- sub_v3_v3v3(dco[a], co, bindcagecos[a]);
- }
- else {
- copy_v3_v3(dco[a], co);
- }
+ float co[3];
+ mul_v3_m4v3(co, mmd->bindmat, dco[a]);
+ /* compute difference with world space bind coord */
+ sub_v3_v3v3(dco[a], co, bindcagecos[a]);
}
MOD_get_vgroup(ob, mesh, mmd->defgrp_name, &dvert, &defgrp_index);
@@ -464,7 +452,6 @@ static void meshdeformModifier_do(ModifierData *md,
finally:
MEM_SAFE_FREE(dco);
- MEM_SAFE_FREE(cagecos);
}
static void deformVerts(ModifierData *md,
diff --git a/source/blender/nodes/shader/node_shader_util.h b/source/blender/nodes/shader/node_shader_util.h
index de192f51a5f..c1687e1a349 100644
--- a/source/blender/nodes/shader/node_shader_util.h
+++ b/source/blender/nodes/shader/node_shader_util.h
@@ -67,7 +67,7 @@
#include "GPU_material.h"
#include "GPU_texture.h"
-#include "GPU_uniformbuffer.h"
+#include "GPU_uniform_buffer.h"
#ifdef __cplusplus
# include "FN_multi_function_builder.hh"
diff --git a/source/blender/nodes/shader/nodes/node_shader_rgb.c b/source/blender/nodes/shader/nodes/node_shader_rgb.c
index 47d9496c889..0bdef9a2a17 100644
--- a/source/blender/nodes/shader/nodes/node_shader_rgb.c
+++ b/source/blender/nodes/shader/nodes/node_shader_rgb.c
@@ -35,7 +35,7 @@ static int gpu_shader_rgb(GPUMaterial *mat,
GPUNodeStack *in,
GPUNodeStack *out)
{
- GPUNodeLink *link = GPU_uniformbuffer_link_out(mat, node, out, 0);
+ GPUNodeLink *link = GPU_uniformbuf_link_out(mat, node, out, 0);
return GPU_stack_link(mat, node, "set_rgba", in, out, link);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_value.cc b/source/blender/nodes/shader/nodes/node_shader_value.cc
index 1d7c3f47233..1a1382fa8af 100644
--- a/source/blender/nodes/shader/nodes/node_shader_value.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_value.cc
@@ -35,7 +35,7 @@ static int gpu_shader_value(GPUMaterial *mat,
GPUNodeStack *in,
GPUNodeStack *out)
{
- GPUNodeLink *link = GPU_uniformbuffer_link_out(mat, node, out, 0);
+ GPUNodeLink *link = GPU_uniformbuf_link_out(mat, node, out, 0);
return GPU_stack_link(mat, node, "set_value", in, out, link);
}
diff --git a/source/blender/python/bmesh/CMakeLists.txt b/source/blender/python/bmesh/CMakeLists.txt
index 818498fe7db..89df127075d 100644
--- a/source/blender/python/bmesh/CMakeLists.txt
+++ b/source/blender/python/bmesh/CMakeLists.txt
@@ -64,4 +64,8 @@ if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
+if(WITH_GMP)
+ add_definitions(-DWITH_GMP)
+endif()
+
blender_add_lib(bf_python_bmesh "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/python/bmesh/bmesh_py_ops_call.c b/source/blender/python/bmesh/bmesh_py_ops_call.c
index a387ba31c84..d0676ec1947 100644
--- a/source/blender/python/bmesh/bmesh_py_ops_call.c
+++ b/source/blender/python/bmesh/bmesh_py_ops_call.c
@@ -228,7 +228,7 @@ static int bpy_slot_from_py(BMesh *bm,
break;
}
case BMO_OP_SLOT_FLT: {
- float param = PyFloat_AsDouble(value);
+ const float param = PyFloat_AsDouble(value);
if (param == -1 && PyErr_Occurred()) {
PyErr_Format(PyExc_TypeError,
"%.200s: keyword \"%.200s\" expected a float, not %.200s",
@@ -840,7 +840,7 @@ PyObject *BPy_BMO_call(BPy_BMeshOpFunc *self, PyObject *args, PyObject *kw)
{
char slot_name_strip[MAX_SLOTNAME];
const char *ch = strchr(slot->slot_name, '.'); /* can't fail! */
- int tot = ch - slot->slot_name;
+ const int tot = ch - slot->slot_name;
BLI_assert(ch != NULL);
memcpy(slot_name_strip, slot->slot_name, tot);
slot_name_strip[tot] = '\0';
diff --git a/source/blender/python/bmesh/bmesh_py_types.c b/source/blender/python/bmesh/bmesh_py_types.c
index 04bceb17c20..2b174de7136 100644
--- a/source/blender/python/bmesh/bmesh_py_types.c
+++ b/source/blender/python/bmesh/bmesh_py_types.c
@@ -1093,7 +1093,7 @@ static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args, PyObject
bool use_deform = true;
bool use_cage = false;
bool use_fnorm = true;
- CustomData_MeshMasks data_masks = CD_MASK_BMESH;
+ const CustomData_MeshMasks data_masks = CD_MASK_BMESH;
BPY_BM_CHECK_OBJ(self);
@@ -1346,7 +1346,7 @@ static PyObject *bpy_bmesh_transform(BPy_BMElem *self, PyObject *args, PyObject
}
}
else {
- char filter_flags_ch = (char)filter_flags;
+ const char filter_flags_ch = (char)filter_flags;
BM_ITER_MESH (eve, &iter, self->bm, BM_VERTS_OF_MESH) {
if (BM_elem_flag_test(eve, filter_flags_ch)) {
mul_m4_v3((float(*)[4])mat_ptr, eve->co);
@@ -3222,7 +3222,7 @@ static PyObject *bpy_bmelemseq_subscript(BPy_BMElemSeq *self, PyObject *key)
{
/* don't need error check here */
if (PyIndex_Check(key)) {
- Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError);
+ const Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError);
if (i == -1 && PyErr_Occurred()) {
return NULL;
}
@@ -3255,7 +3255,7 @@ static PyObject *bpy_bmelemseq_subscript(BPy_BMElemSeq *self, PyObject *key)
if (start < 0 || stop < 0) {
/* only get the length for negative values */
- Py_ssize_t len = bpy_bmelemseq_length(self);
+ const Py_ssize_t len = bpy_bmelemseq_length(self);
if (start < 0) {
start += len;
}
diff --git a/source/blender/python/bmesh/bmesh_py_types_customdata.c b/source/blender/python/bmesh/bmesh_py_types_customdata.c
index 51616030d30..a9a9a3ad5d9 100644
--- a/source/blender/python/bmesh/bmesh_py_types_customdata.c
+++ b/source/blender/python/bmesh/bmesh_py_types_customdata.c
@@ -714,7 +714,7 @@ static PyObject *bpy_bmlayercollection_subscript_slice(BPy_BMLayerCollection *se
Py_ssize_t start,
Py_ssize_t stop)
{
- Py_ssize_t len = bpy_bmlayercollection_length(self);
+ const Py_ssize_t len = bpy_bmlayercollection_length(self);
int count = 0;
PyObject *tuple;
@@ -746,7 +746,7 @@ static PyObject *bpy_bmlayercollection_subscript(BPy_BMLayerCollection *self, Py
return bpy_bmlayercollection_subscript_str(self, _PyUnicode_AsString(key));
}
if (PyIndex_Check(key)) {
- Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError);
+ const Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError);
if (i == -1 && PyErr_Occurred()) {
return NULL;
}
@@ -779,7 +779,7 @@ static PyObject *bpy_bmlayercollection_subscript(BPy_BMLayerCollection *self, Py
if (start < 0 || stop < 0) {
/* only get the length for negative values */
- Py_ssize_t len = bpy_bmlayercollection_length(self);
+ const Py_ssize_t len = bpy_bmlayercollection_length(self);
if (start < 0) {
start += len;
}
@@ -1127,7 +1127,7 @@ int BPy_BMLayerItem_SetItem(BPy_BMElem *py_ele, BPy_BMLayerItem *py_layer, PyObj
}
case CD_PROP_FLOAT:
case CD_PAINT_MASK: {
- float tmp_val = PyFloat_AsDouble(py_value);
+ const float tmp_val = PyFloat_AsDouble(py_value);
if (UNLIKELY(tmp_val == -1 && PyErr_Occurred())) {
PyErr_Format(
PyExc_TypeError, "expected a float, not a %.200s", Py_TYPE(py_value)->tp_name);
@@ -1140,7 +1140,7 @@ int BPy_BMLayerItem_SetItem(BPy_BMElem *py_ele, BPy_BMLayerItem *py_layer, PyObj
}
case CD_PROP_INT32:
case CD_FACEMAP: {
- int tmp_val = PyC_Long_AsI32(py_value);
+ const int tmp_val = PyC_Long_AsI32(py_value);
if (UNLIKELY(tmp_val == -1 && PyErr_Occurred())) {
/* error is set */
ret = -1;
@@ -1187,7 +1187,7 @@ int BPy_BMLayerItem_SetItem(BPy_BMElem *py_ele, BPy_BMLayerItem *py_layer, PyObj
break;
}
case CD_BWEIGHT: {
- float tmp_val = PyFloat_AsDouble(py_value);
+ const float tmp_val = PyFloat_AsDouble(py_value);
if (UNLIKELY(tmp_val == -1 && PyErr_Occurred())) {
PyErr_Format(
PyExc_TypeError, "expected a float, not a %.200s", Py_TYPE(py_value)->tp_name);
@@ -1199,7 +1199,7 @@ int BPy_BMLayerItem_SetItem(BPy_BMElem *py_ele, BPy_BMLayerItem *py_layer, PyObj
break;
}
case CD_CREASE: {
- float tmp_val = PyFloat_AsDouble(py_value);
+ const float tmp_val = PyFloat_AsDouble(py_value);
if (UNLIKELY(tmp_val == -1 && PyErr_Occurred())) {
PyErr_Format(
PyExc_TypeError, "expected a float, not a %.200s", Py_TYPE(py_value)->tp_name);
diff --git a/source/blender/python/bmesh/bmesh_py_types_select.c b/source/blender/python/bmesh/bmesh_py_types_select.c
index d69668341ff..9bb9815f731 100644
--- a/source/blender/python/bmesh/bmesh_py_types_select.c
+++ b/source/blender/python/bmesh/bmesh_py_types_select.c
@@ -246,7 +246,7 @@ static PyObject *bpy_bmeditselseq_subscript(BPy_BMEditSelSeq *self, PyObject *ke
{
/* don't need error check here */
if (PyIndex_Check(key)) {
- Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError);
+ const Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError);
if (i == -1 && PyErr_Occurred()) {
return NULL;
}
@@ -279,7 +279,7 @@ static PyObject *bpy_bmeditselseq_subscript(BPy_BMEditSelSeq *self, PyObject *ke
if (start < 0 || stop < 0) {
/* only get the length for negative values */
- Py_ssize_t len = bpy_bmeditselseq_length(self);
+ const Py_ssize_t len = bpy_bmeditselseq_length(self);
if (start < 0) {
start += len;
}
diff --git a/source/blender/python/generic/bgl.c b/source/blender/python/generic/bgl.c
index 405541554c9..89fe9f8c6aa 100644
--- a/source/blender/python/generic/bgl.c
+++ b/source/blender/python/generic/bgl.c
@@ -461,7 +461,7 @@ int BGL_typeSize(int type)
static int gl_buffer_type_from_py_buffer(Py_buffer *pybuffer)
{
const char format = PyC_StructFmt_type_from_str(pybuffer->format);
- Py_ssize_t itemsize = pybuffer->itemsize;
+ const Py_ssize_t itemsize = pybuffer->itemsize;
if (PyC_StructFmt_type_is_float_any(format)) {
if (itemsize == 4) {
@@ -705,7 +705,7 @@ static int BGL_BufferOrOffsetConverter(PyObject *object, BufferOrOffset *buffer)
return 1;
}
if (PyNumber_Check(object)) {
- Py_ssize_t offset = PyNumber_AsSsize_t(object, PyExc_IndexError);
+ const Py_ssize_t offset = PyNumber_AsSsize_t(object, PyExc_IndexError);
if (offset == -1 && PyErr_Occurred()) {
return 0;
}
@@ -907,7 +907,7 @@ static int Buffer_ass_item(Buffer *self, int i, PyObject *v)
Buffer *row = (Buffer *)Buffer_item(self, i);
if (row) {
- int ret = Buffer_ass_slice(row, 0, self->dimensions[1], v);
+ const int ret = Buffer_ass_slice(row, 0, self->dimensions[1], v);
Py_DECREF(row);
return ret;
}
diff --git a/source/blender/python/generic/idprop_py_api.c b/source/blender/python/generic/idprop_py_api.c
index 615ce514a3e..314a34e3dec 100644
--- a/source/blender/python/generic/idprop_py_api.c
+++ b/source/blender/python/generic/idprop_py_api.c
@@ -435,7 +435,7 @@ static IDProperty *idp_from_PyBytes(const char *name, PyObject *ob)
static int idp_array_type_from_formatstr_and_size(const char *typestr, Py_ssize_t itemsize)
{
- char format = PyC_StructFmt_type_from_str(typestr);
+ const char format = PyC_StructFmt_type_from_str(typestr);
if (PyC_StructFmt_type_is_float_any(format)) {
if (itemsize == 4) {
@@ -473,7 +473,7 @@ static IDProperty *idp_from_PySequence_Buffer(const char *name, Py_buffer *buffe
IDProperty *prop;
IDPropertyTemplate val = {0};
- int id_type = idp_array_type_from_formatstr_and_size(buffer->format, buffer->itemsize);
+ const int id_type = idp_array_type_from_formatstr_and_size(buffer->format, buffer->itemsize);
if (id_type == -1) {
/* should never happen as the type has been checked before */
return NULL;
@@ -560,7 +560,7 @@ static IDProperty *idp_from_PySequence(const char *name, PyObject *ob)
if (PyObject_CheckBuffer(ob)) {
PyObject_GetBuffer(ob, &buffer, PyBUF_SIMPLE | PyBUF_FORMAT);
- char format = PyC_StructFmt_type_from_str(buffer.format);
+ const char format = PyC_StructFmt_type_from_str(buffer.format);
if (PyC_StructFmt_type_is_float_any(format) ||
(PyC_StructFmt_type_is_int_any(format) && buffer.itemsize == 4)) {
use_buffer = true;
@@ -589,7 +589,7 @@ static IDProperty *idp_from_PySequence(const char *name, PyObject *ob)
static IDProperty *idp_from_PyMapping(const char *name, PyObject *ob)
{
IDProperty *prop;
- IDPropertyTemplate val = {0};
+ const IDPropertyTemplate val = {0};
PyObject *keys, *vals, *key, *pval;
int i, len;
@@ -1559,8 +1559,8 @@ static int itemsize_by_idarray_type(int array_type)
static int BPy_IDArray_getbuffer(BPy_IDArray *self, Py_buffer *view, int flags)
{
IDProperty *prop = self->prop;
- int itemsize = itemsize_by_idarray_type(prop->subtype);
- int length = itemsize * prop->len;
+ const int itemsize = itemsize_by_idarray_type(prop->subtype);
+ const int length = itemsize * prop->len;
if (PyBuffer_FillInfo(view, (PyObject *)self, IDP_Array(prop), length, false, flags) == -1) {
return -1;
diff --git a/source/blender/python/generic/imbuf_py_api.c b/source/blender/python/generic/imbuf_py_api.c
index 3536236754e..5dc4aa6ce7c 100644
--- a/source/blender/python/generic/imbuf_py_api.c
+++ b/source/blender/python/generic/imbuf_py_api.c
@@ -260,7 +260,7 @@ static int py_imbuf_filepath_set(Py_ImBuf *self, PyObject *value, void *UNUSED(c
}
ImBuf *ibuf = self->ibuf;
- Py_ssize_t value_str_len_max = sizeof(ibuf->name);
+ const Py_ssize_t value_str_len_max = sizeof(ibuf->name);
Py_ssize_t value_str_len;
const char *value_str = _PyUnicode_AsStringAndSize(value, &value_str_len);
if (value_str_len >= value_str_len_max) {
@@ -425,8 +425,8 @@ static PyObject *M_imbuf_new(PyObject *UNUSED(self), PyObject *args, PyObject *k
}
/* TODO, make options */
- uchar planes = 4;
- uint flags = IB_rect;
+ const uchar planes = 4;
+ const uint flags = IB_rect;
ImBuf *ibuf = IMB_allocImBuf(UNPACK2(size), planes, flags);
if (ibuf == NULL) {
@@ -500,7 +500,7 @@ static PyObject *M_imbuf_write(PyObject *UNUSED(self), PyObject *args, PyObject
filepath = py_imb->ibuf->name;
}
- bool ok = IMB_saveiff(py_imb->ibuf, filepath, IB_rect);
+ const bool ok = IMB_saveiff(py_imb->ibuf, filepath, IB_rect);
if (ok == false) {
PyErr_Format(
PyExc_IOError, "write: Unable to write image file (%s) '%s'", strerror(errno), filepath);
diff --git a/source/blender/python/generic/py_capi_utils.c b/source/blender/python/generic/py_capi_utils.c
index 838a1239210..195442d34f6 100644
--- a/source/blender/python/generic/py_capi_utils.c
+++ b/source/blender/python/generic/py_capi_utils.c
@@ -207,7 +207,7 @@ PyObject *PyC_Tuple_PackArray_Bool(const bool *array, uint len)
*/
void PyC_Tuple_Fill(PyObject *tuple, PyObject *value)
{
- uint tot = PyTuple_GET_SIZE(tuple);
+ const uint tot = PyTuple_GET_SIZE(tuple);
uint i;
for (i = 0; i < tot; i++) {
@@ -218,7 +218,7 @@ void PyC_Tuple_Fill(PyObject *tuple, PyObject *value)
void PyC_List_Fill(PyObject *list, PyObject *value)
{
- uint tot = PyList_GET_SIZE(list);
+ const uint tot = PyList_GET_SIZE(list);
uint i;
for (i = 0; i < tot; i++) {
@@ -377,7 +377,7 @@ void PyC_StackSpit(void)
}
/* lame but handy */
- PyGILState_STATE gilstate = PyGILState_Ensure();
+ const PyGILState_STATE gilstate = PyGILState_Ensure();
PyRun_SimpleString("__import__('traceback').print_stack()");
PyGILState_Release(gilstate);
}
@@ -948,7 +948,7 @@ void PyC_RunQuicky(const char *filepath, int n, ...)
FILE *fp = fopen(filepath, "r");
if (fp) {
- PyGILState_STATE gilstate = PyGILState_Ensure();
+ const PyGILState_STATE gilstate = PyGILState_Ensure();
va_list vargs;
@@ -1423,7 +1423,7 @@ bool PyC_RunString_AsString(const char *imports[],
*/
int PyC_Long_AsBool(PyObject *value)
{
- int test = _PyLong_AsInt(value);
+ const int test = _PyLong_AsInt(value);
if (UNLIKELY((uint)test > 1)) {
PyErr_SetString(PyExc_TypeError, "Python number not a bool (0/1)");
return -1;
@@ -1433,7 +1433,7 @@ int PyC_Long_AsBool(PyObject *value)
int8_t PyC_Long_AsI8(PyObject *value)
{
- int test = _PyLong_AsInt(value);
+ const int test = _PyLong_AsInt(value);
if (UNLIKELY(test < INT8_MIN || test > INT8_MAX)) {
PyErr_SetString(PyExc_OverflowError, "Python int too large to convert to C int8");
return -1;
@@ -1443,7 +1443,7 @@ int8_t PyC_Long_AsI8(PyObject *value)
int16_t PyC_Long_AsI16(PyObject *value)
{
- int test = _PyLong_AsInt(value);
+ const int test = _PyLong_AsInt(value);
if (UNLIKELY(test < INT16_MIN || test > INT16_MAX)) {
PyErr_SetString(PyExc_OverflowError, "Python int too large to convert to C int16");
return -1;
@@ -1458,7 +1458,7 @@ int16_t PyC_Long_AsI16(PyObject *value)
uint8_t PyC_Long_AsU8(PyObject *value)
{
- ulong test = PyLong_AsUnsignedLong(value);
+ const ulong test = PyLong_AsUnsignedLong(value);
if (UNLIKELY(test > UINT8_MAX)) {
PyErr_SetString(PyExc_OverflowError, "Python int too large to convert to C uint8");
return (uint8_t)-1;
@@ -1468,7 +1468,7 @@ uint8_t PyC_Long_AsU8(PyObject *value)
uint16_t PyC_Long_AsU16(PyObject *value)
{
- ulong test = PyLong_AsUnsignedLong(value);
+ const ulong test = PyLong_AsUnsignedLong(value);
if (UNLIKELY(test > UINT16_MAX)) {
PyErr_SetString(PyExc_OverflowError, "Python int too large to convert to C uint16");
return (uint16_t)-1;
@@ -1478,7 +1478,7 @@ uint16_t PyC_Long_AsU16(PyObject *value)
uint32_t PyC_Long_AsU32(PyObject *value)
{
- ulong test = PyLong_AsUnsignedLong(value);
+ const ulong test = PyLong_AsUnsignedLong(value);
if (UNLIKELY(test > UINT32_MAX)) {
PyErr_SetString(PyExc_OverflowError, "Python int too large to convert to C uint32");
return (uint32_t)-1;
diff --git a/source/blender/python/gpu/gpu_py_offscreen.c b/source/blender/python/gpu/gpu_py_offscreen.c
index e56f87e6221..15c39de990b 100644
--- a/source/blender/python/gpu/gpu_py_offscreen.c
+++ b/source/blender/python/gpu/gpu_py_offscreen.c
@@ -246,7 +246,7 @@ static PyObject *bpygpu_offscreen_draw_view3d(BPyGPUOffScreen *self,
BLI_assert(BKE_id_is_in_global_main(&scene->id));
- depsgraph = BKE_scene_get_depsgraph(G_MAIN, scene, view_layer, true);
+ depsgraph = BKE_scene_ensure_depsgraph(G_MAIN, scene, view_layer);
rv3d_mats = ED_view3d_mats_rv3d_backup(region->regiondata);
diff --git a/source/blender/python/gpu/gpu_py_shader.c b/source/blender/python/gpu/gpu_py_shader.c
index c1a6ce09d37..f9ff0558570 100644
--- a/source/blender/python/gpu/gpu_py_shader.c
+++ b/source/blender/python/gpu/gpu_py_shader.c
@@ -78,7 +78,7 @@ static int bpygpu_uniform_location_get(GPUShader *shader,
const char *name,
const char *error_prefix)
{
- int uniform = GPU_shader_get_uniform(shader, name);
+ const int uniform = GPU_shader_get_uniform(shader, name);
if (uniform == -1) {
PyErr_Format(PyExc_ValueError, "%s: uniform %.32s not found", error_prefix, name);
@@ -158,7 +158,7 @@ static PyObject *bpygpu_shader_uniform_from_name(BPyGPUShader *self, PyObject *a
return NULL;
}
- int uniform = bpygpu_uniform_location_get(self->shader, name, "GPUShader.get_uniform");
+ const int uniform = bpygpu_uniform_location_get(self->shader, name, "GPUShader.get_uniform");
if (uniform == -1) {
return NULL;
@@ -184,7 +184,7 @@ static PyObject *bpygpu_shader_uniform_block_from_name(BPyGPUShader *self, PyObj
return NULL;
}
- int uniform = GPU_shader_get_uniform_block(self->shader, name);
+ const int uniform = GPU_shader_get_uniform_block(self->shader, name);
if (uniform == -1) {
PyErr_Format(PyExc_ValueError, "GPUShader.get_uniform_block: uniform %.32s not found", name);
@@ -504,7 +504,7 @@ static PyObject *bpygpu_shader_attr_from_name(BPyGPUShader *self, PyObject *arg)
return NULL;
}
- int attr = GPU_shader_get_attribute(self->shader, name);
+ const int attr = GPU_shader_get_attribute(self->shader, name);
if (attr == -1) {
PyErr_Format(PyExc_ValueError, "GPUShader.attr_from_name: attribute %.32s not found", name);
diff --git a/source/blender/python/gpu/gpu_py_vertex_buffer.c b/source/blender/python/gpu/gpu_py_vertex_buffer.c
index 57290fdc3c4..9372770e45e 100644
--- a/source/blender/python/gpu/gpu_py_vertex_buffer.c
+++ b/source/blender/python/gpu/gpu_py_vertex_buffer.c
@@ -134,7 +134,7 @@ static bool bpygpu_vertbuf_fill_impl(GPUVertBuf *vbo,
return false;
}
- uint comp_len = pybuffer.ndim == 1 ? 1 : (uint)pybuffer.shape[1];
+ const uint comp_len = pybuffer.ndim == 1 ? 1 : (uint)pybuffer.shape[1];
if (pybuffer.shape[0] != vbo->vertex_len) {
PyErr_Format(
diff --git a/source/blender/python/gpu/gpu_py_vertex_format.c b/source/blender/python/gpu/gpu_py_vertex_format.c
index d8266be7e2c..1cbcaba6bfb 100644
--- a/source/blender/python/gpu/gpu_py_vertex_format.c
+++ b/source/blender/python/gpu/gpu_py_vertex_format.c
@@ -112,7 +112,7 @@ static int bpygpu_ParseVertCompType(PyObject *o, void *p)
return 0;
}
- int comp_type = bpygpu_parse_component_type(str, length);
+ const int comp_type = bpygpu_parse_component_type(str, length);
if (comp_type == -1) {
PyErr_Format(PyExc_ValueError, "unknown component type: '%s", str);
return 0;
@@ -132,7 +132,7 @@ static int bpygpu_ParseVertFetchMode(PyObject *o, void *p)
return 0;
}
- int fetch_mode = bpygpu_parse_fetch_mode(str, length);
+ const int fetch_mode = bpygpu_parse_fetch_mode(str, length);
if (fetch_mode == -1) {
PyErr_Format(PyExc_ValueError, "unknown type literal: '%s'", str);
return 0;
diff --git a/source/blender/python/intern/bpy_app.c b/source/blender/python/intern/bpy_app.c
index 4ee936aff91..f0de05f95b3 100644
--- a/source/blender/python/intern/bpy_app.c
+++ b/source/blender/python/intern/bpy_app.c
@@ -343,7 +343,7 @@ static PyObject *bpy_app_debug_value_get(PyObject *UNUSED(self), void *UNUSED(cl
static int bpy_app_debug_value_set(PyObject *UNUSED(self), PyObject *value, void *UNUSED(closure))
{
- short param = PyC_Long_AsI16(value);
+ const short param = PyC_Long_AsI16(value);
if (param == -1 && PyErr_Occurred()) {
PyC_Err_SetString_Prefix(PyExc_TypeError,
diff --git a/source/blender/python/intern/bpy_app_handlers.c b/source/blender/python/intern/bpy_app_handlers.c
index cdbd3bc0b9c..a874e23ff32 100644
--- a/source/blender/python/intern/bpy_app_handlers.c
+++ b/source/blender/python/intern/bpy_app_handlers.c
@@ -318,7 +318,7 @@ void bpy_app_generic_callback(struct Main *UNUSED(main),
{
PyObject *cb_list = py_cb_array[POINTER_AS_INT(arg)];
if (PyList_GET_SIZE(cb_list) > 0) {
- PyGILState_STATE gilstate = PyGILState_Ensure();
+ const PyGILState_STATE gilstate = PyGILState_Ensure();
const int num_arguments = 2;
PyObject *args_all = PyTuple_New(num_arguments); /* save python creating each call */
diff --git a/source/blender/python/intern/bpy_app_icons.c b/source/blender/python/intern/bpy_app_icons.c
index 2e688609961..7cca3ae4700 100644
--- a/source/blender/python/intern/bpy_app_icons.c
+++ b/source/blender/python/intern/bpy_app_icons.c
@@ -71,8 +71,8 @@ static PyObject *bpy_app_icons_new_triangles(PyObject *UNUSED(self), PyObject *a
return NULL;
}
- int coords_size = sizeof(uchar[2]) * tris_len * 3;
- int colors_size = sizeof(uchar[4]) * tris_len * 3;
+ const int coords_size = sizeof(uchar[2]) * tris_len * 3;
+ const int colors_size = sizeof(uchar[4]) * tris_len * 3;
uchar(*coords)[2] = MEM_mallocN(coords_size, __func__);
uchar(*colors)[4] = MEM_mallocN(colors_size, __func__);
@@ -86,7 +86,7 @@ static PyObject *bpy_app_icons_new_triangles(PyObject *UNUSED(self), PyObject *a
geom->coords = coords;
geom->colors = colors;
geom->icon_id = 0;
- int icon_id = BKE_icon_geom_ensure(geom);
+ const int icon_id = BKE_icon_geom_ensure(geom);
return PyLong_FromLong(icon_id);
}
@@ -117,7 +117,7 @@ static PyObject *bpy_app_icons_new_triangles_from_file(PyObject *UNUSED(self),
PyErr_SetString(PyExc_ValueError, "Unable to load from file");
return NULL;
}
- int icon_id = BKE_icon_geom_ensure(geom);
+ const int icon_id = BKE_icon_geom_ensure(geom);
return PyLong_FromLong(icon_id);
}
diff --git a/source/blender/python/intern/bpy_app_opensubdiv.c b/source/blender/python/intern/bpy_app_opensubdiv.c
index 3f14c4dca57..09cd6201831 100644
--- a/source/blender/python/intern/bpy_app_opensubdiv.c
+++ b/source/blender/python/intern/bpy_app_opensubdiv.c
@@ -63,7 +63,7 @@ static PyObject *make_opensubdiv_info(void)
#define SetObjItem(obj) PyStructSequence_SET_ITEM(opensubdiv_info, pos++, obj)
#ifdef WITH_OPENSUBDIV
- int curversion = openSubdiv_getVersionHex();
+ const int curversion = openSubdiv_getVersionHex();
SetObjItem(PyBool_FromLong(1));
SetObjItem(PyC_Tuple_Pack_I32(curversion / 10000, (curversion / 100) % 100, curversion % 100));
SetObjItem(PyUnicode_FromFormat(
diff --git a/source/blender/python/intern/bpy_app_timers.c b/source/blender/python/intern/bpy_app_timers.c
index f1dd8b9e803..af299952b72 100644
--- a/source/blender/python/intern/bpy_app_timers.c
+++ b/source/blender/python/intern/bpy_app_timers.c
@@ -65,7 +65,7 @@ static double py_timer_execute(uintptr_t UNUSED(uuid), void *user_data)
gilstate = PyGILState_Ensure();
PyObject *py_ret = PyObject_CallObject(function, NULL);
- double ret = handle_returned_value(function, py_ret);
+ const double ret = handle_returned_value(function, py_ret);
PyGILState_Release(gilstate);
@@ -151,7 +151,7 @@ PyDoc_STRVAR(bpy_app_timers_is_registered_doc,
" :rtype: bool\n");
static PyObject *bpy_app_timers_is_registered(PyObject *UNUSED(self), PyObject *function)
{
- bool ret = BLI_timer_is_registered((intptr_t)function);
+ const bool ret = BLI_timer_is_registered((intptr_t)function);
return PyBool_FromLong(ret);
}
diff --git a/source/blender/python/intern/bpy_app_translations.c b/source/blender/python/intern/bpy_app_translations.c
index c152c920453..f95261df6b2 100644
--- a/source/blender/python/intern/bpy_app_translations.c
+++ b/source/blender/python/intern/bpy_app_translations.c
@@ -92,7 +92,7 @@ static GHashKey *_ghashutil_keyalloc(const void *msgctxt, const void *msgid)
static uint _ghashutil_keyhash(const void *ptr)
{
const GHashKey *key = ptr;
- uint hash = BLI_ghashutil_strhash(key->msgctxt);
+ const uint hash = BLI_ghashutil_strhash(key->msgctxt);
return hash ^ BLI_ghashutil_strhash(key->msgid);
}
diff --git a/source/blender/python/intern/bpy_driver.c b/source/blender/python/intern/bpy_driver.c
index 7fb4b0c469c..4ef685b7987 100644
--- a/source/blender/python/intern/bpy_driver.c
+++ b/source/blender/python/intern/bpy_driver.c
@@ -228,7 +228,7 @@ static void bpy_pydriver_namespace_clear_self(void)
void BPY_driver_reset(void)
{
PyGILState_STATE gilstate;
- bool use_gil = true; /* !PyC_IsInterpreterActive(); */
+ const bool use_gil = true; /* !PyC_IsInterpreterActive(); */
if (use_gil) {
gilstate = PyGILState_Ensure();
@@ -594,7 +594,7 @@ float BPY_driver_exec(struct PathResolvedRNA *anim_rna,
#endif
{
/* try to get variable value */
- float tval = driver_get_variable_value(driver, dvar);
+ const float tval = driver_get_variable_value(driver, dvar);
driver_arg = PyFloat_FromDouble((double)tval);
}
diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c
index b0b36baa839..bc7318e1a15 100644
--- a/source/blender/python/intern/bpy_interface.c
+++ b/source/blender/python/intern/bpy_interface.c
@@ -169,7 +169,7 @@ void BPY_text_free_code(Text *text)
{
if (text->compiled) {
PyGILState_STATE gilstate;
- bool use_gil = !PyC_IsInterpreterActive();
+ const bool use_gil = !PyC_IsInterpreterActive();
if (use_gil) {
gilstate = PyGILState_Ensure();
@@ -446,14 +446,14 @@ void BPY_python_backtrace(FILE *fp)
void BPY_DECREF(void *pyob_ptr)
{
- PyGILState_STATE gilstate = PyGILState_Ensure();
+ const PyGILState_STATE gilstate = PyGILState_Ensure();
Py_DECREF((PyObject *)pyob_ptr);
PyGILState_Release(gilstate);
}
void BPY_DECREF_RNA_INVALIDATE(void *pyob_ptr)
{
- PyGILState_STATE gilstate = PyGILState_Ensure();
+ const PyGILState_STATE gilstate = PyGILState_Ensure();
const int do_invalidate = (Py_REFCNT((PyObject *)pyob_ptr) > 1);
Py_DECREF((PyObject *)pyob_ptr);
if (do_invalidate) {
@@ -509,7 +509,7 @@ void BPY_modules_load_user(bContext *C)
int BPY_context_member_get(bContext *C, const char *member, bContextDataResult *result)
{
PyGILState_STATE gilstate;
- bool use_gil = !PyC_IsInterpreterActive();
+ const bool use_gil = !PyC_IsInterpreterActive();
PyObject *pyctx;
PyObject *item;
@@ -544,7 +544,7 @@ int BPY_context_member_get(bContext *C, const char *member, bContextDataResult *
PyErr_Clear();
}
else {
- int len = PySequence_Fast_GET_SIZE(seq_fast);
+ const int len = PySequence_Fast_GET_SIZE(seq_fast);
PyObject **seq_fast_items = PySequence_Fast_ITEMS(seq_fast);
int i;
diff --git a/source/blender/python/intern/bpy_library_load.c b/source/blender/python/intern/bpy_library_load.c
index bcf13b1d88f..bdad4d03ae7 100644
--- a/source/blender/python/intern/bpy_library_load.c
+++ b/source/blender/python/intern/bpy_library_load.c
@@ -321,7 +321,7 @@ static PyObject *bpy_lib_exit(BPy_Library *self, PyObject *UNUSED(args))
{
Main *bmain = CTX_data_main(BPy_GetContext());
Main *mainl = NULL;
- int err = 0;
+ const int err = 0;
const bool do_append = ((self->flag & FILE_LINK) == 0);
BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true);
@@ -338,7 +338,7 @@ static PyObject *bpy_lib_exit(BPy_Library *self, PyObject *UNUSED(args))
// printf("lib: %s\n", name_plural);
if (ls && PyList_Check(ls)) {
/* loop */
- Py_ssize_t size = PyList_GET_SIZE(ls);
+ const Py_ssize_t size = PyList_GET_SIZE(ls);
Py_ssize_t i;
for (i = 0; i < size; i++) {
@@ -423,7 +423,7 @@ static PyObject *bpy_lib_exit(BPy_Library *self, PyObject *UNUSED(args))
const char *name_plural = BKE_idtype_idcode_to_name_plural(idcode);
PyObject *ls = PyDict_GetItemString(self->dict, name_plural);
if (ls && PyList_Check(ls)) {
- Py_ssize_t size = PyList_GET_SIZE(ls);
+ const Py_ssize_t size = PyList_GET_SIZE(ls);
Py_ssize_t i;
PyObject *item;
diff --git a/source/blender/python/intern/bpy_msgbus.c b/source/blender/python/intern/bpy_msgbus.c
index 45c5aba1e3e..3739f56dc79 100644
--- a/source/blender/python/intern/bpy_msgbus.c
+++ b/source/blender/python/intern/bpy_msgbus.c
@@ -192,7 +192,7 @@ static void bpy_msgbus_notify(bContext *C,
static void bpy_msgbus_subscribe_value_free_data(struct wmMsgSubscribeKey *UNUSED(msg_key),
struct wmMsgSubscribeValue *msg_val)
{
- PyGILState_STATE gilstate = PyGILState_Ensure();
+ const PyGILState_STATE gilstate = PyGILState_Ensure();
Py_DECREF(msg_val->owner);
Py_DECREF(msg_val->user_data);
PyGILState_Release(gilstate);
diff --git a/source/blender/python/intern/bpy_props.c b/source/blender/python/intern/bpy_props.c
index 66c67ca061c..859f0027f14 100644
--- a/source/blender/python/intern/bpy_props.c
+++ b/source/blender/python/intern/bpy_props.c
@@ -351,7 +351,7 @@ static bool bpy_prop_boolean_get_cb(struct PointerRNA *ptr, struct PropertyRNA *
value = false;
}
else {
- int value_i = PyC_Long_AsBool(ret);
+ const int value_i = PyC_Long_AsBool(ret);
if (value_i == -1 && PyErr_Occurred()) {
PyC_Err_PrintWithFunc(py_func);
@@ -443,7 +443,7 @@ static bool bpy_prop_poll_cb(struct PointerRNA *self,
PyObject *ret;
bool result;
const int is_write_ok = pyrna_write_check();
- PyGILState_STATE gilstate = PyGILState_Ensure();
+ const PyGILState_STATE gilstate = PyGILState_Ensure();
BLI_assert(self != NULL);
@@ -560,7 +560,7 @@ static void bpy_prop_boolean_array_set_cb(struct PointerRNA *ptr,
PyGILState_STATE gilstate;
bool use_gil;
const bool is_write_ok = pyrna_write_check();
- int len = RNA_property_array_length(ptr, prop);
+ const int len = RNA_property_array_length(ptr, prop);
BLI_assert(py_data != NULL);
@@ -804,7 +804,7 @@ static void bpy_prop_int_array_set_cb(struct PointerRNA *ptr,
PyGILState_STATE gilstate;
bool use_gil;
const bool is_write_ok = pyrna_write_check();
- int len = RNA_property_array_length(ptr, prop);
+ const int len = RNA_property_array_length(ptr, prop);
BLI_assert(py_data != NULL);
@@ -1048,7 +1048,7 @@ static void bpy_prop_float_array_set_cb(struct PointerRNA *ptr,
PyGILState_STATE gilstate;
bool use_gil;
const bool is_write_ok = pyrna_write_check();
- int len = RNA_property_array_length(ptr, prop);
+ const int len = RNA_property_array_length(ptr, prop);
BLI_assert(py_data != NULL);
diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c
index 955a24bc880..a3ded8813ac 100644
--- a/source/blender/python/intern/bpy_rna.c
+++ b/source/blender/python/intern/bpy_rna.c
@@ -464,7 +464,7 @@ static int mathutils_rna_vector_set(BaseMathObject *bmo, int subtype)
if (subtype == MATHUTILS_CB_SUBTYPE_EUL) {
EulerObject *eul = (EulerObject *)bmo;
PropertyRNA *prop_eul_order = NULL;
- short order = pyrna_rotation_euler_order_get(&self->ptr, eul->order, &prop_eul_order);
+ const short order = pyrna_rotation_euler_order_get(&self->ptr, eul->order, &prop_eul_order);
if (order != eul->order) {
RNA_property_enum_set(&self->ptr, prop_eul_order, eul->order);
if (RNA_property_update_check(prop_eul_order)) {
@@ -599,7 +599,7 @@ static short pyrna_rotation_euler_order_get(PointerRNA *ptr,
}
if (*r_prop_eul_order) {
- short order = RNA_property_enum_get(ptr, *r_prop_eul_order);
+ const short order = RNA_property_enum_get(ptr, *r_prop_eul_order);
/* Could be quat or axisangle. */
if (order >= EULER_ORDER_XYZ && order <= EULER_ORDER_ZYX) {
return order;
@@ -714,7 +714,8 @@ PyObject *pyrna_math_object_from_array(PointerRNA *ptr, PropertyRNA *prop)
/* Attempt to get order,
* only needed for thick types since wrapped with update via callbacks. */
PropertyRNA *prop_eul_order = NULL;
- short order = pyrna_rotation_euler_order_get(ptr, EULER_ORDER_XYZ, &prop_eul_order);
+ const short order = pyrna_rotation_euler_order_get(
+ ptr, EULER_ORDER_XYZ, &prop_eul_order);
ret = Euler_CreatePyObject(NULL, order, NULL); /* TODO, get order from RNA. */
RNA_property_float_get_array(ptr, prop, ((EulerObject *)ret)->eul);
@@ -1725,7 +1726,7 @@ static int pyrna_py_to_prop(
}
case PROP_INT: {
int overflow;
- long param = PyLong_AsLongAndOverflow(value, &overflow);
+ const long param = PyLong_AsLongAndOverflow(value, &overflow);
if (overflow || (param > INT_MAX) || (param < INT_MIN)) {
PyErr_Format(PyExc_ValueError,
"%.200s %.200s.%.200s value not in 'int' range "
@@ -1757,7 +1758,7 @@ static int pyrna_py_to_prop(
break;
}
case PROP_FLOAT: {
- float param = PyFloat_AsDouble(value);
+ const float param = PyFloat_AsDouble(value);
if (PyErr_Occurred()) {
PyErr_Format(PyExc_TypeError,
"%.200s %.200s.%.200s expected a float type, not %.200s",
@@ -1935,8 +1936,8 @@ static int pyrna_py_to_prop(
PyObject *value_new = NULL;
StructRNA *ptr_type = RNA_property_pointer_type(ptr, prop);
- int flag = RNA_property_flag(prop);
- int flag_parameter = RNA_parameter_flag(prop);
+ const int flag = RNA_property_flag(prop);
+ const int flag_parameter = RNA_parameter_flag(prop);
/* This is really nasty! Done so we can fake the operator having direct properties, eg:
* layout.prop(self, "filepath")
@@ -2075,7 +2076,7 @@ static int pyrna_py_to_prop(
BKE_reports_init(&reports, RPT_STORE);
RNA_property_pointer_set(
ptr, prop, value == Py_None ? PointerRNA_NULL : param->ptr, &reports);
- int err = (BPy_reports_to_error(&reports, PyExc_RuntimeError, true));
+ const int err = (BPy_reports_to_error(&reports, PyExc_RuntimeError, true));
if (err == -1) {
Py_XDECREF(value_new);
return -1;
@@ -2233,7 +2234,7 @@ static int pyrna_py_to_prop_array_index(BPy_PropertyArrayRNA *self, int index, P
/* See if we can coerce into a Python type - 'PropertyType'. */
switch (RNA_property_type(prop)) {
case PROP_BOOLEAN: {
- int param = PyC_Long_AsBool(value);
+ const int param = PyC_Long_AsBool(value);
if (param == -1) {
/* Error is set. */
@@ -2698,7 +2699,7 @@ static PyObject *pyrna_prop_collection_subscript(BPy_PropertyRNA *self, PyObject
return pyrna_prop_collection_subscript_str(self, _PyUnicode_AsString(key));
}
if (PyIndex_Check(key)) {
- Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError);
+ const Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError);
if (i == -1 && PyErr_Occurred()) {
return NULL;
}
@@ -2732,7 +2733,7 @@ static PyObject *pyrna_prop_collection_subscript(BPy_PropertyRNA *self, PyObject
if (start < 0 || stop < 0) {
/* Only get the length for negative values. */
- Py_ssize_t len = (Py_ssize_t)RNA_property_collection_length(&self->ptr, self->prop);
+ const Py_ssize_t len = (Py_ssize_t)RNA_property_collection_length(&self->ptr, self->prop);
if (start < 0) {
start += len;
}
@@ -2827,7 +2828,7 @@ static int pyrna_prop_collection_ass_subscript(BPy_PropertyRNA *self,
else
#endif
if (PyIndex_Check(key)) {
- Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError);
+ const Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError);
if (i == -1 && PyErr_Occurred()) {
return -1;
}
@@ -2899,7 +2900,7 @@ static PyObject *pyrna_prop_array_subscript(BPy_PropertyArrayRNA *self, PyObject
else
#endif
if (PyIndex_Check(key)) {
- Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError);
+ const Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError);
if (i == -1 && PyErr_Occurred()) {
return NULL;
}
@@ -2919,11 +2920,11 @@ static PyObject *pyrna_prop_array_subscript(BPy_PropertyArrayRNA *self, PyObject
if (key_slice->start == Py_None && key_slice->stop == Py_None) {
/* Note: no significant advantage with optimizing [:] slice as with collections,
* but include here for consistency with collection slice func */
- Py_ssize_t len = (Py_ssize_t)pyrna_prop_array_length(self);
+ const Py_ssize_t len = (Py_ssize_t)pyrna_prop_array_length(self);
return pyrna_prop_array_subscript_slice(self, &self->ptr, self->prop, 0, len, len);
}
- int len = pyrna_prop_array_length(self);
+ const int len = pyrna_prop_array_length(self);
Py_ssize_t start, stop, slicelength;
if (PySlice_GetIndicesEx(key, len, &start, &stop, &step, &slicelength) < 0) {
@@ -3055,7 +3056,7 @@ static int prop_subscript_ass_array_slice__bool_recursive(PyObject **value_items
BLI_assert(totdim == 1);
int i;
for (i = 0; i != length; i++) {
- int v = PyLong_AsLong(value_items[i]);
+ const int v = PyLong_AsLong(value_items[i]);
value[i] = v;
}
return i;
@@ -3097,7 +3098,7 @@ static int prop_subscript_ass_array_slice(PointerRNA *ptr,
}
int dimsize[3];
- int totdim = RNA_property_array_dimension(ptr, prop, dimsize);
+ const int totdim = RNA_property_array_dimension(ptr, prop, dimsize);
if (totdim > 1) {
BLI_assert(dimsize[arraydim] == length);
}
@@ -3247,7 +3248,7 @@ static int pyrna_prop_array_ass_subscript(BPy_PropertyArrayRNA *self,
}
else if (PyIndex_Check(key)) {
- Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError);
+ const Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError);
if (i == -1 && PyErr_Occurred()) {
ret = -1;
}
@@ -3256,7 +3257,7 @@ static int pyrna_prop_array_ass_subscript(BPy_PropertyArrayRNA *self,
}
}
else if (PySlice_Check(key)) {
- Py_ssize_t len = pyrna_prop_array_length(self);
+ const Py_ssize_t len = pyrna_prop_array_length(self);
Py_ssize_t start, stop, step, slicelength;
if (PySlice_GetIndicesEx(key, len, &start, &stop, &step, &slicelength) < 0) {
@@ -4249,7 +4250,7 @@ static PyObject *pyrna_struct_getattro(BPy_StructRNA *self, PyObject *pyname)
ListBase newlb;
short newtype;
- int done = CTX_data_get(C, name, &newptr, &newlb, &newtype);
+ const int done = CTX_data_get(C, name, &newptr, &newlb, &newtype);
if (done == 1) { /* Found. */
switch (newtype) {
@@ -4401,7 +4402,7 @@ static int pyrna_struct_meta_idprop_setattro(PyObject *cls, PyObject *attr, PyOb
if (value) {
/* Check if the value is a property. */
if (is_deferred_prop) {
- int ret = deferred_register_prop(srna, attr, value);
+ const int ret = deferred_register_prop(srna, attr, value);
if (ret == -1) {
/* Error set. */
return ret;
@@ -4471,7 +4472,7 @@ static int pyrna_struct_setattro(BPy_StructRNA *self, PyObject *pyname, PyObject
ListBase newlb;
short newtype;
- int done = CTX_data_get(C, name, &newptr, &newlb, &newtype);
+ const int done = CTX_data_get(C, name, &newptr, &newlb, &newtype);
if (done == 1) {
PyErr_Format(
@@ -4646,7 +4647,7 @@ static PyObject *pyrna_prop_collection_idprop_add(BPy_PropertyRNA *self)
static PyObject *pyrna_prop_collection_idprop_remove(BPy_PropertyRNA *self, PyObject *value)
{
- int key = PyLong_AsLong(value);
+ const int key = PyLong_AsLong(value);
#ifdef USE_PEDANTIC_WRITE
if (rna_disallow_writes && rna_id_write_error(&self->ptr, NULL)) {
@@ -5172,7 +5173,7 @@ static int foreach_parse_args(BPy_PropertyRNA *self,
static bool foreach_compat_buffer(RawPropertyType raw_type, int attr_signed, const char *format)
{
- char f = format ? *format : 'B'; /* B is assumed when not set */
+ const char f = format ? *format : 'B'; /* B is assumed when not set */
switch (raw_type) {
case PROP_RAW_CHAR:
@@ -5400,7 +5401,7 @@ static PyObject *pyprop_array_foreach_getset(BPy_PropertyArrayRNA *self,
PyObject *item = NULL;
Py_ssize_t i, seq_size, size;
void *array = NULL;
- PropertyType prop_type = RNA_property_type(self->prop);
+ const PropertyType prop_type = RNA_property_type(self->prop);
/* Get/set both take the same args currently. */
PyObject *seq;
@@ -5498,7 +5499,7 @@ static PyObject *pyprop_array_foreach_getset(BPy_PropertyArrayRNA *self,
}
}
else {
- char f = buf.format ? buf.format[0] : 0;
+ const char f = buf.format ? buf.format[0] : 0;
if ((prop_type == PROP_INT && (buf.itemsize != sizeof(int) || (f != 'l' && f != 'i'))) ||
(prop_type == PROP_FLOAT && (buf.itemsize != sizeof(float) || f != 'f'))) {
PyBuffer_Release(&buf);
@@ -8030,7 +8031,7 @@ static int rna_function_arg_count(FunctionRNA *func, int *min_count)
const ListBase *lb = RNA_function_defined_parameters(func);
PropertyRNA *parm;
Link *link;
- int flag = RNA_function_flag(func);
+ const int flag = RNA_function_flag(func);
const bool is_staticmethod = (flag & FUNC_NO_SELF) && !(flag & FUNC_USE_SELF_TYPE);
int count = is_staticmethod ? 0 : 1;
bool done_min_count = false;
@@ -8273,7 +8274,7 @@ static int bpy_class_call(bContext *C, PointerRNA *ptr, FunctionRNA *func, Param
ParameterIterator iter;
PointerRNA funcptr;
int err = 0, i, ret_len = 0, arg_count;
- int flag = RNA_function_flag(func);
+ const int flag = RNA_function_flag(func);
const bool is_staticmethod = (flag & FUNC_NO_SELF) && !(flag & FUNC_USE_SELF_TYPE);
const bool is_classmethod = (flag & FUNC_NO_SELF) && (flag & FUNC_USE_SELF_TYPE);
@@ -9015,7 +9016,7 @@ void pyrna_struct_type_extend_capi(struct StructRNA *srna,
py_method = PyCFunction_New(method, NULL);
}
- int err = PyDict_SetItemString(dict, method->ml_name, py_method);
+ const int err = PyDict_SetItemString(dict, method->ml_name, py_method);
Py_DECREF(py_method);
BLI_assert(!(err < 0));
UNUSED_VARS_NDEBUG(err);
diff --git a/source/blender/python/intern/bpy_rna_anim.c b/source/blender/python/intern/bpy_rna_anim.c
index ae19f89c348..1d52706c5f9 100644
--- a/source/blender/python/intern/bpy_rna_anim.c
+++ b/source/blender/python/intern/bpy_rna_anim.c
@@ -131,7 +131,7 @@ static int pyrna_struct_anim_args_parse_ex(PointerRNA *ptr,
}
}
else {
- int array_len = RNA_property_array_length(&r_ptr, prop);
+ const int array_len = RNA_property_array_length(&r_ptr, prop);
if ((*r_index) < -1 || (*r_index) >= array_len) {
PyErr_Format(PyExc_TypeError,
"%.200s index out of range \"%s\", given %d, array length is %d",
@@ -316,7 +316,7 @@ PyObject *pyrna_struct_keyframe_insert(BPy_StructRNA *self, PyObject *args, PyOb
int index = -1;
float cfra = FLT_MAX;
const char *group_name = NULL;
- char keytype = BEZT_KEYTYPE_KEYFRAME; /* XXX: Expose this as a one-off option... */
+ const char keytype = BEZT_KEYTYPE_KEYFRAME; /* XXX: Expose this as a one-off option... */
int options = 0;
PYRNA_STRUCT_CHECK_OBJ(self);
diff --git a/source/blender/python/intern/bpy_rna_array.c b/source/blender/python/intern/bpy_rna_array.c
index 66e07d556a6..cb3fe9cb600 100644
--- a/source/blender/python/intern/bpy_rna_array.c
+++ b/source/blender/python/intern/bpy_rna_array.c
@@ -361,7 +361,7 @@ static int validate_array(PyObject *rvalue,
const char *error_prefix)
{
int dimsize[MAX_ARRAY_DIMENSION];
- int totdim = RNA_property_array_dimension(ptr, prop, dimsize);
+ const int totdim = RNA_property_array_dimension(ptr, prop, dimsize);
/* validate type first because length validation may modify property array length */
@@ -466,7 +466,7 @@ static char *copy_values(PyObject *seq,
const ItemConvert_FuncArg *convert_item,
RNA_SetIndexFunc rna_set_index)
{
- int totdim = RNA_property_array_dimension(ptr, prop, NULL);
+ const int totdim = RNA_property_array_dimension(ptr, prop, NULL);
const Py_ssize_t seq_size = PySequence_Size(seq);
Py_ssize_t i;
@@ -487,7 +487,7 @@ static char *copy_values(PyObject *seq,
if (dim == 0) {
if (MatrixObject_Check(seq)) {
MatrixObject *pymat = (MatrixObject *)seq;
- size_t allocsize = pymat->num_col * pymat->num_row * sizeof(float);
+ const size_t allocsize = pymat->num_col * pymat->num_row * sizeof(float);
/* read callback already done by validate */
/* since this is the first iteration we can assume data is allocated */
@@ -993,7 +993,7 @@ PyObject *pyrna_py_from_array(PointerRNA *ptr, PropertyRNA *prop)
/* TODO, multi-dimensional arrays */
int pyrna_array_contains_py(PointerRNA *ptr, PropertyRNA *prop, PyObject *value)
{
- int len = RNA_property_array_length(ptr, prop);
+ const int len = RNA_property_array_length(ptr, prop);
int type;
int i;
@@ -1011,7 +1011,7 @@ int pyrna_array_contains_py(PointerRNA *ptr, PropertyRNA *prop, PyObject *value)
switch (type) {
case PROP_FLOAT: {
- float value_f = PyFloat_AsDouble(value);
+ const float value_f = PyFloat_AsDouble(value);
if (value_f == -1 && PyErr_Occurred()) {
PyErr_Clear();
return 0;
@@ -1044,7 +1044,7 @@ int pyrna_array_contains_py(PointerRNA *ptr, PropertyRNA *prop, PyObject *value)
break;
}
case PROP_INT: {
- int value_i = PyC_Long_AsI32(value);
+ const int value_i = PyC_Long_AsI32(value);
if (value_i == -1 && PyErr_Occurred()) {
PyErr_Clear();
return 0;
@@ -1077,7 +1077,7 @@ int pyrna_array_contains_py(PointerRNA *ptr, PropertyRNA *prop, PyObject *value)
break;
}
case PROP_BOOLEAN: {
- int value_i = PyC_Long_AsBool(value);
+ const int value_i = PyC_Long_AsBool(value);
if (value_i == -1 && PyErr_Occurred()) {
PyErr_Clear();
return 0;
diff --git a/source/blender/python/intern/bpy_rna_callback.c b/source/blender/python/intern/bpy_rna_callback.c
index 976b8a65ac7..2f8be0c44e0 100644
--- a/source/blender/python/intern/bpy_rna_callback.c
+++ b/source/blender/python/intern/bpy_rna_callback.c
@@ -84,7 +84,7 @@ static void cb_region_draw(const bContext *C, ARegion *UNUSED(region), void *cus
static PyObject *PyC_Tuple_CopySized(PyObject *src, int len_dst)
{
PyObject *dst = PyTuple_New(len_dst);
- int len_src = PyTuple_GET_SIZE(src);
+ const int len_src = PyTuple_GET_SIZE(src);
BLI_assert(len_src <= len_dst);
for (int i = 0; i < len_src; i++) {
PyObject *item = PyTuple_GET_ITEM(src, i);
diff --git a/source/blender/python/intern/bpy_rna_driver.c b/source/blender/python/intern/bpy_rna_driver.c
index 9240e34bbab..3bddd0ad8c0 100644
--- a/source/blender/python/intern/bpy_rna_driver.c
+++ b/source/blender/python/intern/bpy_rna_driver.c
@@ -57,7 +57,7 @@ PyObject *pyrna_driver_get_variable_value(struct ChannelDriver *driver, struct D
}
else {
/* object & property */
- PropertyType type = RNA_property_type(prop);
+ const PropertyType type = RNA_property_type(prop);
if (type == PROP_ENUM) {
/* Note that enum's are converted to strings by default,
* we want to avoid that, see: T52213 */
diff --git a/source/blender/python/intern/bpy_rna_gizmo.c b/source/blender/python/intern/bpy_rna_gizmo.c
index 4ef718ef023..575824e8a86 100644
--- a/source/blender/python/intern/bpy_rna_gizmo.c
+++ b/source/blender/python/intern/bpy_rna_gizmo.c
@@ -63,7 +63,7 @@ static void py_rna_gizmo_handler_get_cb(const wmGizmo *UNUSED(gz),
wmGizmoProperty *gz_prop,
void *value_p)
{
- PyGILState_STATE gilstate = PyGILState_Ensure();
+ const PyGILState_STATE gilstate = PyGILState_Ensure();
struct BPyGizmoHandlerUserData *data = gz_prop->custom_func.user_data;
PyObject *ret = PyObject_CallObject(data->fn_slots[BPY_GIZMO_FN_SLOT_GET], NULL);
@@ -110,7 +110,7 @@ static void py_rna_gizmo_handler_set_cb(const wmGizmo *UNUSED(gz),
wmGizmoProperty *gz_prop,
const void *value_p)
{
- PyGILState_STATE gilstate = PyGILState_Ensure();
+ const PyGILState_STATE gilstate = PyGILState_Ensure();
struct BPyGizmoHandlerUserData *data = gz_prop->custom_func.user_data;
@@ -159,7 +159,7 @@ static void py_rna_gizmo_handler_range_get_cb(const wmGizmo *UNUSED(gz),
{
struct BPyGizmoHandlerUserData *data = gz_prop->custom_func.user_data;
- PyGILState_STATE gilstate = PyGILState_Ensure();
+ const PyGILState_STATE gilstate = PyGILState_Ensure();
PyObject *ret = PyObject_CallObject(data->fn_slots[BPY_GIZMO_FN_SLOT_RANGE_GET], NULL);
if (ret == NULL) {
@@ -211,7 +211,7 @@ static void py_rna_gizmo_handler_free_cb(const wmGizmo *UNUSED(gz), wmGizmoPrope
{
struct BPyGizmoHandlerUserData *data = gz_prop->custom_func.user_data;
- PyGILState_STATE gilstate = PyGILState_Ensure();
+ const PyGILState_STATE gilstate = PyGILState_Ensure();
for (int i = 0; i < BPY_GIZMO_FN_SLOT_LEN; i++) {
Py_XDECREF(data->fn_slots[i]);
}
@@ -234,7 +234,7 @@ PyDoc_STRVAR(
" :type range: callable\n");
static PyObject *bpy_gizmo_target_set_handler(PyObject *UNUSED(self), PyObject *args, PyObject *kw)
{
- PyGILState_STATE gilstate = PyGILState_Ensure();
+ const PyGILState_STATE gilstate = PyGILState_Ensure();
struct {
PyObject *self;
@@ -368,7 +368,7 @@ static PyObject *bpy_gizmo_target_get_value(PyObject *UNUSED(self), PyObject *ar
return PyC_Tuple_PackArray_F32(value, array_len);
}
- float value = WM_gizmo_target_property_float_get(gz, gz_prop);
+ const float value = WM_gizmo_target_property_float_get(gz, gz_prop);
return PyFloat_FromDouble(value);
break;
diff --git a/source/blender/python/mathutils/mathutils.c b/source/blender/python/mathutils/mathutils.c
index 308d2ef9618..ca38d7008f6 100644
--- a/source/blender/python/mathutils/mathutils.c
+++ b/source/blender/python/mathutils/mathutils.c
@@ -208,7 +208,7 @@ int mathutils_array_parse(
if (size != -1) {
if (flag & MU_ARRAY_ZERO) {
- int size_left = array_max - size;
+ const int size_left = array_max - size;
if (size_left) {
memset(&array[size], 0, sizeof(float) * size_left);
}
@@ -541,9 +541,9 @@ int EXPP_FloatsAreEqual(float af, float bf, int maxDiff)
{
/* solid, fast routine across all platforms
* with constant time behavior */
- int ai = *(int *)(&af);
- int bi = *(int *)(&bf);
- int test = SIGNMASK(ai ^ bi);
+ const int ai = *(int *)(&af);
+ const int bi = *(int *)(&bf);
+ const int test = SIGNMASK(ai ^ bi);
int diff, v1, v2;
assert((0 == test) || (0xFFFFFFFF == test));
diff --git a/source/blender/python/mathutils/mathutils_Color.c b/source/blender/python/mathutils/mathutils_Color.c
index 6bffff467cd..8a7f782de3c 100644
--- a/source/blender/python/mathutils/mathutils_Color.c
+++ b/source/blender/python/mathutils/mathutils_Color.c
@@ -749,7 +749,7 @@ PyDoc_STRVAR(Color_channel_hsv_v_doc, "HSV Value component in [0, 1].\n\n:type:
static PyObject *Color_channel_hsv_get(ColorObject *self, void *type)
{
float hsv[3];
- int i = POINTER_AS_INT(type);
+ const int i = POINTER_AS_INT(type);
if (BaseMath_ReadCallback(self) == -1) {
return NULL;
@@ -763,7 +763,7 @@ static PyObject *Color_channel_hsv_get(ColorObject *self, void *type)
static int Color_channel_hsv_set(ColorObject *self, PyObject *value, void *type)
{
float hsv[3];
- int i = POINTER_AS_INT(type);
+ const int i = POINTER_AS_INT(type);
float f = PyFloat_AsDouble(value);
if (f == -1 && PyErr_Occurred()) {
diff --git a/source/blender/python/mathutils/mathutils_Matrix.c b/source/blender/python/mathutils/mathutils_Matrix.c
index 236bb1de29d..0a524cbf24c 100644
--- a/source/blender/python/mathutils/mathutils_Matrix.c
+++ b/source/blender/python/mathutils/mathutils_Matrix.c
@@ -809,7 +809,7 @@ static PyObject *C_Matrix_OrthoProjection(PyObject *cls, PyObject *args)
else {
/* arbitrary plane */
- int vec_size = (matSize == 2 ? 2 : 3);
+ const int vec_size = (matSize == 2 ? 2 : 3);
float tvec[4];
if (mathutils_array_parse(tvec,
@@ -2156,7 +2156,8 @@ static PyObject *Matrix_str(MatrixObject *self)
for (col = 0; col < self->num_col; col++) {
maxsize[col] = 0;
for (row = 0; row < self->num_row; row++) {
- int size = BLI_snprintf(dummy_buf, sizeof(dummy_buf), "%.4f", MATRIX_ITEM(self, row, col));
+ const int size = BLI_snprintf(
+ dummy_buf, sizeof(dummy_buf), "%.4f", MATRIX_ITEM(self, row, col));
maxsize[col] = max_ii(maxsize[col], size);
}
}
diff --git a/source/blender/python/mathutils/mathutils_Vector.c b/source/blender/python/mathutils/mathutils_Vector.c
index 3ee6e766413..9bc8c0dffed 100644
--- a/source/blender/python/mathutils/mathutils_Vector.c
+++ b/source/blender/python/mathutils/mathutils_Vector.c
@@ -356,7 +356,7 @@ PyDoc_STRVAR(Vector_normalize_doc,
" however 4D Vectors w axis is left untouched.\n");
static PyObject *Vector_normalize(VectorObject *self)
{
- int size = (self->size == 4 ? 3 : self->size);
+ const int size = (self->size == 4 ? 3 : self->size);
if (BaseMath_ReadCallback_ForWrite(self) == -1) {
return NULL;
}
@@ -2027,7 +2027,7 @@ static PyObject *Vector_richcmpr(PyObject *objectA, PyObject *objectB, int compa
{
VectorObject *vecA = NULL, *vecB = NULL;
int result = 0;
- double epsilon = 0.000001f;
+ const double epsilon = 0.000001f;
double lenA, lenB;
if (!VectorObject_Check(objectA) || !VectorObject_Check(objectB)) {
diff --git a/source/blender/python/mathutils/mathutils_bvhtree.c b/source/blender/python/mathutils/mathutils_bvhtree.c
index 16ea05771d0..1d477421e30 100644
--- a/source/blender/python/mathutils/mathutils_bvhtree.c
+++ b/source/blender/python/mathutils/mathutils_bvhtree.c
@@ -589,7 +589,7 @@ static PyObject *py_bvhtree_overlap(PyBVHTree *self, PyBVHTree *other)
/* pass */
}
else {
- bool use_unique = (self->orig_index || other->orig_index);
+ const bool use_unique = (self->orig_index || other->orig_index);
GSet *pair_test = use_unique ?
BLI_gset_new_ex(overlap_hash, overlap_cmp, __func__, overlap_len) :
NULL;
@@ -1037,7 +1037,7 @@ static Mesh *bvh_get_mesh(const char *funcname,
{
Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob);
/* we only need minimum mesh data for topology and vertex locations */
- CustomData_MeshMasks data_masks = CD_MASK_BAREMESH;
+ const CustomData_MeshMasks data_masks = CD_MASK_BAREMESH;
const bool use_render = DEG_get_mode(depsgraph) == DAG_EVAL_RENDER;
*r_free_mesh = false;
diff --git a/source/blender/python/mathutils/mathutils_geometry.c b/source/blender/python/mathutils/mathutils_geometry.c
index 37997e9f912..1a161924f96 100644
--- a/source/blender/python/mathutils/mathutils_geometry.c
+++ b/source/blender/python/mathutils/mathutils_geometry.c
@@ -315,7 +315,7 @@ static PyObject *M_Geometry_intersect_tri_tri_2d(PyObject *UNUSED(self), PyObjec
}
}
- bool ret = isect_tri_tri_v2(UNPACK3(tri_pair[0]), UNPACK3(tri_pair[1]));
+ const bool ret = isect_tri_tri_v2(UNPACK3(tri_pair[0]), UNPACK3(tri_pair[1]));
return PyBool_FromLong(ret);
}
@@ -492,7 +492,7 @@ static PyObject *M_Geometry_intersect_line_plane(PyObject *UNUSED(self), PyObjec
PyObject *py_line_a, *py_line_b, *py_plane_co, *py_plane_no;
float line_a[3], line_b[3], plane_co[3], plane_no[3];
float isect[3];
- bool no_flip = false;
+ const bool no_flip = false;
if (!PyArg_ParseTuple(args,
"OOOO|O&:intersect_line_plane",
@@ -1639,7 +1639,6 @@ static PyObject *M_Geometry_delaunay_2d_cdt(PyObject *UNUSED(self), PyObject *ar
in.faces_start_table = in_faces_start_table;
in.faces_len_table = in_faces_len_table;
in.epsilon = epsilon;
- in.skip_input_modify = false;
res = BLI_delaunay_2d_cdt_calc(&in, output_type);
diff --git a/source/blender/python/mathutils/mathutils_kdtree.c b/source/blender/python/mathutils/mathutils_kdtree.c
index c3e66546dae..1de3c23838f 100644
--- a/source/blender/python/mathutils/mathutils_kdtree.c
+++ b/source/blender/python/mathutils/mathutils_kdtree.c
@@ -191,7 +191,7 @@ static int py_find_nearest_cb(void *user_data, int index, const float co[3], flo
if (result) {
bool use_node;
- int ok = PyC_ParseBool(result, &use_node);
+ const int ok = PyC_ParseBool(result, &use_node);
Py_DECREF(result);
if (ok) {
return (int)use_node;
diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c
index 86c9c64098b..41d20fa994a 100644
--- a/source/blender/render/intern/source/pipeline.c
+++ b/source/blender/render/intern/source/pipeline.c
@@ -1857,7 +1857,7 @@ static void update_physics_cache(Render *re,
baker.bmain = re->main;
baker.scene = scene;
baker.view_layer = view_layer;
- baker.depsgraph = BKE_scene_get_depsgraph(re->main, scene, view_layer, true);
+ baker.depsgraph = BKE_scene_ensure_depsgraph(re->main, scene, view_layer);
baker.bake = 0;
baker.render = 1;
baker.anim_init = 1;
diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h
index efe600a846a..48f8c9b6fb3 100644
--- a/source/blender/windowmanager/WM_types.h
+++ b/source/blender/windowmanager/WM_types.h
@@ -329,7 +329,10 @@ typedef struct wmNotifier {
#define ND_RENDER_OPTIONS (4 << 16)
#define ND_NODES (5 << 16)
#define ND_SEQUENCER (6 << 16)
+/* Note: If an object was added, removed, merged/joined, ..., it is not enough to notify with
+ * this. This affects the layer so also send a layer change notifier (e.g. ND_LAYER_CONTENT)! */
#define ND_OB_ACTIVE (7 << 16)
+/* See comment on ND_OB_ACTIVE. */
#define ND_OB_SELECT (8 << 16)
#define ND_OB_VISIBLE (9 << 16)
#define ND_OB_RENDER (10 << 16)
@@ -438,7 +441,10 @@ typedef struct wmNotifier {
/* subtype 3d view editing */
#define NS_VIEW3D_GPU (16 << 8)
-#define NS_VIEW3D_SHADING (16 << 9)
+#define NS_VIEW3D_SHADING (17 << 8)
+
+/* subtype layer editing */
+#define NS_LAYER_COLLECTION (24 << 8)
/* action classification */
#define NOTE_ACTION (0x000000FF)
@@ -448,7 +454,8 @@ typedef struct wmNotifier {
#define NA_REMOVED 4
#define NA_RENAME 5
#define NA_SELECTED 6
-#define NA_PAINTING 7
+#define NA_ACTIVATED 7
+#define NA_PAINTING 8
/* ************** Gesture Manager data ************** */
diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
index cecd324ff28..479768c3536 100644
--- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
+++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
@@ -470,10 +470,10 @@ static void gizmos_draw_list(const wmGizmoMap *gzmap, const bContext *C, ListBas
}
else {
if (is_depth) {
- GPU_depth_test(true);
+ GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
}
else {
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
}
is_depth_prev = is_depth;
}
@@ -492,7 +492,7 @@ static void gizmos_draw_list(const wmGizmoMap *gzmap, const bContext *C, ListBas
}
if (is_depth_prev) {
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
}
}
@@ -534,10 +534,10 @@ static void gizmo_draw_select_3d_loop(const bContext *C,
}
else {
if (is_depth) {
- GPU_depth_test(true);
+ GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
}
else {
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
}
is_depth_prev = is_depth;
}
@@ -560,7 +560,7 @@ static void gizmo_draw_select_3d_loop(const bContext *C,
}
if (is_depth_prev) {
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
}
if (is_depth_skip_prev) {
GPU_depth_mask(true);
diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c
index 5d0520bb674..a14ccfa104c 100644
--- a/source/blender/windowmanager/intern/wm.c
+++ b/source/blender/windowmanager/intern/wm.c
@@ -113,6 +113,12 @@ IDTypeInfo IDType_ID_WM = {
.free_data = window_manager_free_data,
.make_local = NULL,
.foreach_id = window_manager_foreach_id,
+ .foreach_cache = NULL,
+
+ .blend_write = NULL,
+ .blend_read_data = NULL,
+ .blend_read_lib = NULL,
+ .blend_read_expand = NULL,
};
#define MAX_OP_REGISTERED 32
diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c
index 6f3fbf77987..6c9bba0791c 100644
--- a/source/blender/windowmanager/intern/wm_draw.c
+++ b/source/blender/windowmanager/intern/wm_draw.c
@@ -720,8 +720,7 @@ static void wm_draw_window_offscreen(bContext *C, wmWindow *win, bool stereo)
wm_draw_region_buffer_create(region, false, false);
wm_draw_region_bind(region, 0);
- GPU_clear_color(0, 0, 0, 0);
- GPU_clear(GPU_COLOR_BIT);
+ GPU_clear_color(0.0f, 0.0f, 0.0f, 0.0f);
ED_region_do_draw(C, region);
wm_draw_region_unbind(region);
@@ -744,7 +743,6 @@ static void wm_draw_window_onscreen(bContext *C, wmWindow *win, int view)
* If it becomes a problem we should clear only when window size changes. */
#if 0
GPU_clear_color(0, 0, 0, 0);
- GPU_clear(GPU_COLOR_BIT);
#endif
/* Blit non-overlapping area regions. */
@@ -910,7 +908,7 @@ static bool wm_draw_update_test_window(Main *bmain, bContext *C, wmWindow *win)
const wmWindowManager *wm = CTX_wm_manager(C);
Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
- struct Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, true);
+ struct Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer);
bScreen *screen = WM_window_get_active_screen(win);
ARegion *region;
bool do_draw = false;
diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c
index c363fdcc9d4..6f7c074c704 100644
--- a/source/blender/windowmanager/intern/wm_event_system.c
+++ b/source/blender/windowmanager/intern/wm_event_system.c
@@ -350,7 +350,7 @@ void wm_event_do_depsgraph(bContext *C, bool is_after_open_file)
* and for until then we have to accept ambiguities when object is shared
* across visible view layers and has overrides on it.
*/
- Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, true);
+ Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer);
if (is_after_open_file) {
DEG_graph_relations_update(depsgraph);
DEG_graph_on_visible_update(bmain, depsgraph, true);
@@ -390,6 +390,9 @@ void wm_event_do_refresh_wm_and_depsgraph(bContext *C)
static void wm_event_execute_timers(bContext *C)
{
wmWindowManager *wm = CTX_wm_manager(C);
+ if (UNLIKELY(wm == NULL)) {
+ return;
+ }
/* Set the first window as context, so that there is some minimal context. This avoids crashes
* when calling code that assumes that there is always a window in the context (which many
@@ -402,16 +405,17 @@ static void wm_event_execute_timers(bContext *C)
/* called in mainloop */
void wm_event_do_notifiers(bContext *C)
{
- wmWindowManager *wm = CTX_wm_manager(C);
wmNotifier *note, *next;
wmWindow *win;
+ /* Run the timer before assigning 'wm' in the unlikely case a timer loads a file, see T80028. */
+ wm_event_execute_timers(C);
+
+ wmWindowManager *wm = CTX_wm_manager(C);
if (wm == NULL) {
return;
}
- wm_event_execute_timers(C);
-
/* disable? - keep for now since its used for window level notifiers. */
#if 1
/* cache & catch WM level notifiers, such as frame change, scene/screen set */
@@ -3189,10 +3193,9 @@ void wm_event_do_handlers(bContext *C)
wm_event_free_all(win);
}
else {
- Main *bmain = CTX_data_main(C);
Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
- Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, false);
+ Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer);
Scene *scene_eval = (depsgraph != NULL) ? DEG_get_evaluated_scene(depsgraph) : NULL;
if (scene_eval != NULL) {
diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c
index 62d9c099cd5..f53a3d6bf35 100644
--- a/source/blender/windowmanager/intern/wm_files.c
+++ b/source/blender/windowmanager/intern/wm_files.c
@@ -1576,7 +1576,7 @@ void wm_autosave_location(char *filepath)
* Blender installed on D:\ drive, D:\ drive has D:\tmp\
* Now, BLI_exists() will find '/tmp/' exists, but
* BLI_make_file_string will create string that has it most likely on C:\
- * through get_default_root().
+ * through BLI_windows_get_default_root_dir().
* If there is no C:\tmp autosave fails. */
if (!BLI_exists(BKE_tempdir_base())) {
savedir = BKE_appdir_folder_id_create(BLENDER_USER_AUTOSAVE, NULL);
diff --git a/source/blender/windowmanager/intern/wm_gesture.c b/source/blender/windowmanager/intern/wm_gesture.c
index 67733ffc673..b245bbe054d 100644
--- a/source/blender/windowmanager/intern/wm_gesture.c
+++ b/source/blender/windowmanager/intern/wm_gesture.c
@@ -353,7 +353,7 @@ static void draw_filled_lasso(wmGesture *gt)
draw_filled_lasso_px_cb,
&lasso_fill_data);
- GPU_blend(GPU_BLEND_ADDITIVE);
+ GPU_blend(GPU_BLEND_ADDITIVE_PREMULT);
IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR);
GPU_shader_bind(state.shader);
@@ -361,7 +361,7 @@ static void draw_filled_lasso(wmGesture *gt)
state.shader, GPU_shader_get_uniform(state.shader, "shuffle"), 4, 1, red);
immDrawPixelsTex(
- &state, rect.xmin, rect.ymin, w, h, GL_R8, false, pixel_buf, 1.0f, 1.0f, NULL);
+ &state, rect.xmin, rect.ymin, w, h, GPU_R8, false, pixel_buf, 1.0f, 1.0f, NULL);
GPU_shader_unbind();
diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c
index e8f663b0c4a..b85bf8cb323 100644
--- a/source/blender/windowmanager/intern/wm_init_exit.c
+++ b/source/blender/windowmanager/intern/wm_init_exit.c
@@ -210,7 +210,7 @@ static void sound_jack_sync_callback(Main *bmain, int mode, double time)
continue;
}
ViewLayer *view_layer = WM_window_get_active_view_layer(window);
- Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, false);
+ Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer);
if (depsgraph == NULL) {
continue;
}
diff --git a/source/blender/windowmanager/intern/wm_playanim.c b/source/blender/windowmanager/intern/wm_playanim.c
index 9d472ed4597..86d3f7f35dc 100644
--- a/source/blender/windowmanager/intern/wm_playanim.c
+++ b/source/blender/windowmanager/intern/wm_playanim.c
@@ -310,8 +310,7 @@ static void playanim_toscreen(
CLAMP(offs_x, 0.0f, 1.0f);
CLAMP(offs_y, 0.0f, 1.0f);
- GPU_clear_color(0.1, 0.1, 0.1, 0.0);
- GPU_clear(GPU_COLOR_BIT);
+ GPU_clear_color(0.1f, 0.1f, 0.1f, 0.0f);
/* checkerboard for case alpha */
if (ibuf->planes == 32) {
@@ -1313,8 +1312,7 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
maxwiny = ibuf->y * (1 + (maxwiny / ibuf->y));
}
- GPU_clear_color(0.1, 0.1, 0.1, 0.0);
- GPU_clear(GPU_COLOR_BIT);
+ GPU_clear_color(0.1f, 0.1f, 0.1f, 0.0f);
int win_x, win_y;
playanim_window_get_size(&win_x, &win_y);
diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c
index ac00fc36a89..69c08427c4a 100644
--- a/source/blender/windowmanager/intern/wm_window.c
+++ b/source/blender/windowmanager/intern/wm_window.c
@@ -645,11 +645,10 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm,
GHOST_SetWindowState(ghostwin, (GHOST_TWindowState)win->windowstate);
}
#endif
- /* until screens get drawn, make it nice gray */
- GPU_clear_color(0.55, 0.55, 0.55, 1.0f);
/* Crash on OSS ATI: bugs.launchpad.net/ubuntu/+source/mesa/+bug/656100 */
if (!GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_OPENSOURCE)) {
- GPU_clear(GPU_COLOR_BIT);
+ /* until screens get drawn, make it nice gray */
+ GPU_clear_color(0.55f, 0.55f, 0.55f, 1.0f);
}
/* needed here, because it's used before it reads userdef */
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_draw.c b/source/blender/windowmanager/xr/intern/wm_xr_draw.c
index 6f96d2ea6a0..5630d294e8d 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr_draw.c
+++ b/source/blender/windowmanager/xr/intern/wm_xr_draw.c
@@ -126,7 +126,7 @@ void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata)
/* In case a framebuffer is still bound from drawing the last eye. */
GPU_framebuffer_restore();
/* Some systems have drawing glitches without this. */
- GPU_clear(GPU_DEPTH_BIT);
+ GPU_clear_depth(1.0f);
/* Draws the view into the surface_data->viewport's framebuffers */
ED_view3d_draw_offscreen_simple(draw_data->depsgraph,
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c
index 78dadbf9513..b9ef40e3398 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr_session.c
+++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c
@@ -195,7 +195,7 @@ static void wm_xr_session_scene_and_evaluated_depsgraph_get(Main *bmain,
Scene *scene = WM_window_get_active_scene(root_win);
ViewLayer *view_layer = WM_window_get_active_view_layer(root_win);
- Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, false);
+ Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer);
BLI_assert(scene && view_layer && depsgraph);
BKE_scene_graph_evaluated_ensure(depsgraph, bmain);
*r_scene = scene;
diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt
index f923c834e93..d00285adb02 100644
--- a/source/creator/CMakeLists.txt
+++ b/source/creator/CMakeLists.txt
@@ -115,6 +115,10 @@ if(WITH_XR_OPENXR)
add_definitions(-DWITH_XR_OPENXR)
endif()
+if(WITH_GMP)
+ add_definitions(-DWITH_GMP)
+endif()
+
# Setup the exe sources and buildinfo
set(SRC
creator.c
@@ -191,6 +195,12 @@ if(WITH_BUILDINFO)
message(FATAL_ERROR "File \"${buildinfo_h_fake}\" found, this should never be created, remove!")
endif()
+ # From the cmake documentation "If the output of the custom command is not actually created as a
+ # file on disk it should be marked with the SYMBOLIC source file property."
+ #
+ # Not doing this leads to build warnings for the not generated file on windows when using msbuild
+ SET_SOURCE_FILES_PROPERTIES(${buildinfo_h_fake} PROPERTIES SYMBOLIC TRUE)
+
# a custom target that is always built
add_custom_target(buildinfo ALL
DEPENDS ${buildinfo_h_fake})
diff --git a/source/creator/creator_args.c b/source/creator/creator_args.c
index 164e670c444..0d1c932d2d2 100644
--- a/source/creator/creator_args.c
+++ b/source/creator/creator_args.c
@@ -751,6 +751,25 @@ static int arg_handle_abort_handler_disable(int UNUSED(argc),
return 0;
}
+static void clog_abort_on_error_callback(void *fp)
+{
+ BLI_system_backtrace(fp);
+ fflush(fp);
+ abort();
+}
+
+static const char arg_handle_debug_exit_on_error_doc[] =
+ "\n\t"
+ "Immediately exit when internal errors are detected.";
+static int arg_handle_debug_exit_on_error(int UNUSED(argc),
+ const char **UNUSED(argv),
+ void *UNUSED(data))
+{
+ MEM_enable_fail_on_memleak();
+ CLG_error_fn_set(clog_abort_on_error_callback);
+ return 0;
+}
+
static const char arg_handle_background_mode_set_doc[] =
"\n\t"
"Run in background (often used for UI-less rendering).";
@@ -1300,7 +1319,7 @@ static int arg_handle_register_extension(int UNUSED(argc), const char **UNUSED(a
if (data) {
G.background = 1;
}
- RegisterBlendExtension();
+ BLI_windows_register_blend_extension(G.background);
# else
(void)data; /* unused */
# endif
@@ -2214,6 +2233,7 @@ void main_args_setup(bContext *C, bArgs *ba)
"--debug-gpu-force-workarounds",
CB_EX(arg_handle_debug_mode_generic_set, gpumem),
(void *)G_DEBUG_GPU_FORCE_WORKAROUNDS);
+ BLI_argsAdd(ba, 1, NULL, "--debug-exit-on-error", CB(arg_handle_debug_exit_on_error), NULL);
BLI_argsAdd(ba, 1, NULL, "--verbose", CB(arg_handle_verbosity_set), NULL);
diff --git a/source/tools b/source/tools
-Subproject ea2a2fa54ecde677aaf4babf54ef4811adc82cc
+Subproject f4aa2de034d359bf85c6a0f90c0d045c5858eb8
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 0ee3b500fdf..8f72db07d92 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -44,7 +44,7 @@ unset(_default_test_python_exe)
# Standard Blender arguments for running tests.
# Specify exit code so that if a Python script error happens, the test fails.
-set(TEST_BLENDER_EXE_PARAMS --background -noaudio --factory-startup --python-exit-code 1)
+set(TEST_BLENDER_EXE_PARAMS --background -noaudio --factory-startup --debug-memory --debug-exit-on-error --python-exit-code 1)
# Python CTests
if(WITH_BLENDER AND WITH_PYTHON)
diff --git a/tests/gtests/testing/testing_main.cc b/tests/gtests/testing/testing_main.cc
index 36d39a1b3b9..6238101b319 100644
--- a/tests/gtests/testing/testing_main.cc
+++ b/tests/gtests/testing/testing_main.cc
@@ -48,7 +48,9 @@ const std::string &flags_test_release_dir()
int main(int argc, char **argv)
{
+ MEM_use_guarded_allocator();
MEM_init_memleak_detection();
+ MEM_enable_fail_on_memleak();
testing::InitGoogleTest(&argc, argv);
BLENDER_GFLAGS_NAMESPACE::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt
index 18f61d83c3c..617313ee1c3 100644
--- a/tests/python/CMakeLists.txt
+++ b/tests/python/CMakeLists.txt
@@ -154,13 +154,13 @@ add_blender_test(
--run-all-tests
)
-add_blender_test(
- bmesh_boolean
- ${TEST_SRC_DIR}/modeling/bool_regression.blend
- --python ${TEST_PYTHON_DIR}/boolean_operator.py
- --
- --run-all-tests
-)
+# add_blender_test(
+# bmesh_boolean
+# ${TEST_SRC_DIR}/modeling/bool_regression.blend
+# --python ${TEST_PYTHON_DIR}/boolean_operator.py
+# --
+# --run-all-tests
+#)
add_blender_test(
bmesh_split_faces
@@ -176,13 +176,13 @@ add_blender_test(
--python-text run_tests.py
)
-add_blender_test(
- modifiers
- ${TEST_SRC_DIR}/modeling/modifiers.blend
- --python ${TEST_PYTHON_DIR}/modifiers.py
- --
- --run-all-tests
-)
+#add_blender_test(
+# modifiers
+# ${TEST_SRC_DIR}/modeling/modifiers.blend
+# --python ${TEST_PYTHON_DIR}/modifiers.py
+# --
+# --run-all-tests
+#)
add_blender_test(
physics_cloth
diff --git a/tests/python/collada/CMakeLists.txt b/tests/python/collada/CMakeLists.txt
index bf22de6b762..1b24875578e 100644
--- a/tests/python/collada/CMakeLists.txt
+++ b/tests/python/collada/CMakeLists.txt
@@ -36,12 +36,12 @@ execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${TEST_OUT_DIR})
# all calls to blender use this
if(APPLE)
if(${CMAKE_GENERATOR} MATCHES "Xcode")
- set(TEST_BLENDER_EXE_PARAMS --background -noaudio --factory-startup)
+ set(TEST_BLENDER_EXE_PARAMS --background -noaudio --factory-startup --debug-memory --debug-exit-on-error)
else()
- set(TEST_BLENDER_EXE_PARAMS --background -noaudio --factory-startup --env-system-scripts ${CMAKE_SOURCE_DIR}/release/scripts)
+ set(TEST_BLENDER_EXE_PARAMS --background -noaudio --factory-startup --debug-memory --debug-exit-on-error --env-system-scripts ${CMAKE_SOURCE_DIR}/release/scripts)
endif()
else()
- set(TEST_BLENDER_EXE_PARAMS --background -noaudio --factory-startup --env-system-scripts ${CMAKE_SOURCE_DIR}/release/scripts)
+ set(TEST_BLENDER_EXE_PARAMS --background -noaudio --factory-startup --debug-memory --debug-exit-on-error --env-system-scripts ${CMAKE_SOURCE_DIR}/release/scripts)
endif()
# for testing with valgrind prefix: valgrind --track-origins=yes --error-limit=no
diff --git a/tests/python/cycles_render_tests.py b/tests/python/cycles_render_tests.py
index bdf4283eb3e..cc949248ce6 100644
--- a/tests/python/cycles_render_tests.py
+++ b/tests/python/cycles_render_tests.py
@@ -20,6 +20,8 @@ def get_arguments(filepath, output_filepath):
"-noaudio",
"--factory-startup",
"--enable-autoexec",
+ "--debug-memory",
+ "--debug-exit-on-error",
filepath,
"-E", "CYCLES",
"-o", output_filepath,
diff --git a/tests/python/eevee_render_tests.py b/tests/python/eevee_render_tests.py
index a7130136d0a..a90d7730ace 100644
--- a/tests/python/eevee_render_tests.py
+++ b/tests/python/eevee_render_tests.py
@@ -103,6 +103,8 @@ def get_arguments(filepath, output_filepath):
"-noaudio",
"--factory-startup",
"--enable-autoexec",
+ "--debug-memory",
+ "--debug-exit-on-error",
filepath,
"-E", "BLENDER_EEVEE",
"-P",
diff --git a/tests/python/modules/render_report.py b/tests/python/modules/render_report.py
index 506c1a1518a..b6cafc2ee24 100755
--- a/tests/python/modules/render_report.py
+++ b/tests/python/modules/render_report.py
@@ -448,16 +448,17 @@ class Report:
crash = False
output = None
try:
- output = subprocess.check_output(command)
- except subprocess.CalledProcessError as e:
- crash = True
+ completed_process = subprocess.run(command, stdout=subprocess.PIPE)
+ if completed_process.returncode != 0:
+ crash = True
+ output = completed_process.stdout
except BaseException as e:
crash = True
if verbose:
print(" ".join(command))
- if output:
- print(output.decode("utf-8"))
+ if (verbose or crash) and output:
+ print(output.decode("utf-8"))
# Detect missing filepaths and consider those errors
for filepath, output_filepath in zip(remaining_filepaths[:], output_filepaths):
diff --git a/tests/python/modules/test_utils.py b/tests/python/modules/test_utils.py
index e31db05ba61..9c23d511813 100755
--- a/tests/python/modules/test_utils.py
+++ b/tests/python/modules/test_utils.py
@@ -79,6 +79,8 @@ class AbstractBlenderRunnerTest(unittest.TestCase):
'-noaudio',
'--factory-startup',
'--enable-autoexec',
+ '--debug-memory',
+ '--debug-exit-on-error',
]
if blendfile:
diff --git a/tests/python/opengl_draw_tests.py b/tests/python/opengl_draw_tests.py
index ab4df63afd9..a2d9d510382 100644
--- a/tests/python/opengl_draw_tests.py
+++ b/tests/python/opengl_draw_tests.py
@@ -39,6 +39,8 @@ def get_arguments(filepath, output_filepath):
"-noaudio",
"--factory-startup",
"--enable-autoexec",
+ "--debug-memory",
+ "--debug-exit-on-error",
filepath,
"-P",
os.path.realpath(__file__),
diff --git a/tests/python/view_layer/CMakeLists.txt b/tests/python/view_layer/CMakeLists.txt
index 3f38ee4675f..3cac2b6d85f 100644
--- a/tests/python/view_layer/CMakeLists.txt
+++ b/tests/python/view_layer/CMakeLists.txt
@@ -30,7 +30,7 @@ execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${TEST_OUT_DIR})
# endif()
# for testing with valgrind prefix: valgrind --track-origins=yes --error-limit=no
-set(TEST_BLENDER_EXE $<TARGET_FILE:blender> --background -noaudio --factory-startup --env-system-scripts ${CMAKE_SOURCE_DIR}/release/scripts)
+set(TEST_BLENDER_EXE $<TARGET_FILE:blender> --background -noaudio --factory-startup --debug-memory --debug-exit-on-error --env-system-scripts ${CMAKE_SOURCE_DIR}/release/scripts)
# ------------------------------------------------------------------------------
diff --git a/tests/python/workbench_render_tests.py b/tests/python/workbench_render_tests.py
index 155b54098a8..7c9842d5733 100644
--- a/tests/python/workbench_render_tests.py
+++ b/tests/python/workbench_render_tests.py
@@ -39,6 +39,8 @@ def get_arguments(filepath, output_filepath):
"-noaudio",
"--factory-startup",
"--enable-autoexec",
+ "--debug-memory",
+ "--debug-exit-on-error",
filepath,
"-E", "BLENDER_WORKBENCH",
"-P",