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:
authorHans Goudey <h.goudey@me.com>2020-09-01 20:35:14 +0300
committerHans Goudey <h.goudey@me.com>2020-09-01 20:38:05 +0300
commitbaca8611e5fe4b3dcd6f5065fb125bc0a9d65934 (patch)
treebb1230387cd53b15f9621f10c4d0e5e2050b5580
parent31705201dddebf7e3be5c4533b89f380aad1ede1 (diff)
parent2930d4fcea405985f2212c5f28c061af7c4849f8 (diff)
Merge branch 'master' into active-fcurve-keyframeactive-fcurve-keyframe
-rw-r--r--.clang-format5
-rw-r--r--CMakeLists.txt17
-rw-r--r--build_files/build_environment/cmake/boost.cmake6
-rw-r--r--build_files/build_environment/cmake/ffmpeg.cmake2
-rw-r--r--build_files/build_environment/cmake/openvdb.cmake18
-rw-r--r--build_files/build_environment/cmake/versions.cmake1
-rwxr-xr-xbuild_files/build_environment/install_deps.sh10
-rw-r--r--build_files/build_environment/patches/hdf5.diff11
-rw-r--r--build_files/build_environment/patches/openvdb.diff115
-rw-r--r--build_files/build_environment/patches/usd.diff71
-rw-r--r--build_files/cmake/Modules/FindEmbree.cmake8
-rw-r--r--build_files/cmake/Modules/FindGMP.cmake2
-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.cmake12
-rw-r--r--build_files/cmake/platform/platform_unix.cmake17
-rw-r--r--build_files/cmake/platform/platform_win32.cmake15
-rw-r--r--doc/python_api/rst/change_log.rst3210
-rw-r--r--extern/audaspace/bindings/python/PySound.cpp2
-rw-r--r--extern/bullet2/CMakeLists.txt3
-rw-r--r--extern/mantaflow/CMakeLists.txt1
-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--intern/clog/CLG_log.h3
-rw-r--r--intern/clog/clog.c23
-rw-r--r--intern/cycles/app/cycles_xml.cpp2
-rw-r--r--intern/cycles/blender/CMakeLists.txt4
-rw-r--r--intern/cycles/blender/blender_curves.cpp10
-rw-r--r--intern/cycles/blender/blender_geometry.cpp33
-rw-r--r--intern/cycles/blender/blender_id_map.h49
-rw-r--r--intern/cycles/blender/blender_light.cpp6
-rw-r--r--intern/cycles/blender/blender_mesh.cpp46
-rw-r--r--intern/cycles/blender/blender_object.cpp26
-rw-r--r--intern/cycles/blender/blender_particles.cpp3
-rw-r--r--intern/cycles/blender/blender_session.cpp7
-rw-r--r--intern/cycles/blender/blender_shader.cpp205
-rw-r--r--intern/cycles/blender/blender_sync.cpp22
-rw-r--r--intern/cycles/blender/blender_sync.h2
-rw-r--r--intern/cycles/blender/blender_volume.cpp49
-rw-r--r--intern/cycles/bvh/bvh_build.cpp4
-rw-r--r--intern/cycles/bvh/bvh_embree.cpp12
-rw-r--r--intern/cycles/bvh/bvh_optix.cpp2
-rw-r--r--intern/cycles/bvh/bvh_split.cpp2
-rw-r--r--intern/cycles/device/CMakeLists.txt1
-rw-r--r--intern/cycles/device/device.h4
-rw-r--r--intern/cycles/device/device_optix.cpp2
-rw-r--r--intern/cycles/device/opencl/device_opencl_impl.cpp8
-rw-r--r--intern/cycles/graph/node.cpp17
-rw-r--r--intern/cycles/graph/node.h10
-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/kernel/svm/svm_sky.h2
-rw-r--r--intern/cycles/render/CMakeLists.txt3
-rw-r--r--intern/cycles/render/attribute.cpp29
-rw-r--r--intern/cycles/render/bake.cpp8
-rw-r--r--intern/cycles/render/buffers.cpp15
-rw-r--r--intern/cycles/render/buffers.h2
-rw-r--r--intern/cycles/render/film.cpp81
-rw-r--r--intern/cycles/render/film.h14
-rw-r--r--intern/cycles/render/geometry.cpp31
-rw-r--r--intern/cycles/render/geometry.h4
-rw-r--r--intern/cycles/render/graph.cpp46
-rw-r--r--intern/cycles/render/graph.h30
-rw-r--r--intern/cycles/render/integrator.cpp2
-rw-r--r--intern/cycles/render/light.cpp4
-rw-r--r--intern/cycles/render/mesh.cpp11
-rw-r--r--intern/cycles/render/mesh.h7
-rw-r--r--intern/cycles/render/nodes.cpp59
-rw-r--r--intern/cycles/render/nodes.h18
-rw-r--r--intern/cycles/render/object.cpp71
-rw-r--r--intern/cycles/render/osl.cpp5
-rw-r--r--intern/cycles/render/osl.h3
-rw-r--r--intern/cycles/render/particles.cpp8
-rw-r--r--intern/cycles/render/particles.h6
-rw-r--r--intern/cycles/render/scene.cpp302
-rw-r--r--intern/cycles/render/scene.h92
-rw-r--r--intern/cycles/render/session.cpp160
-rw-r--r--intern/cycles/render/session.h14
-rw-r--r--intern/cycles/render/shader.cpp21
-rw-r--r--intern/cycles/render/volume.cpp (renamed from intern/cycles/render/mesh_volume.cpp)114
-rw-r--r--intern/cycles/render/volume.h38
-rw-r--r--intern/ghost/GHOST_C-api.h3
-rw-r--r--intern/ghost/GHOST_ISystem.h2
-rw-r--r--intern/ghost/intern/GHOST_C-api.cpp5
-rw-r--r--intern/ghost/intern/GHOST_IXrGraphicsBinding.h19
-rw-r--r--intern/ghost/intern/GHOST_System.h2
-rw-r--r--intern/ghost/intern/GHOST_SystemCocoa.h2
-rw-r--r--intern/ghost/intern/GHOST_SystemCocoa.mm2
-rw-r--r--intern/ghost/intern/GHOST_SystemNULL.h2
-rw-r--r--intern/ghost/intern/GHOST_SystemSDL.cpp2
-rw-r--r--intern/ghost/intern/GHOST_SystemSDL.h2
-rw-r--r--intern/ghost/intern/GHOST_SystemWayland.cpp2
-rw-r--r--intern/ghost/intern/GHOST_SystemWayland.h2
-rw-r--r--intern/ghost/intern/GHOST_SystemWin32.cpp4
-rw-r--r--intern/ghost/intern/GHOST_SystemWin32.h2
-rw-r--r--intern/ghost/intern/GHOST_SystemX11.cpp12
-rw-r--r--intern/ghost/intern/GHOST_SystemX11.h2
-rw-r--r--intern/ghost/intern/GHOST_WindowCocoa.mm2
-rw-r--r--intern/ghost/intern/GHOST_Xr.cpp7
-rw-r--r--intern/ghost/intern/GHOST_XrContext.cpp91
-rw-r--r--intern/ghost/intern/GHOST_XrContext.h11
-rw-r--r--intern/ghost/intern/GHOST_XrEvent.cpp14
-rw-r--r--intern/ghost/intern/GHOST_XrGraphicsBinding.cpp128
-rw-r--r--intern/ghost/intern/GHOST_XrSession.cpp56
-rw-r--r--intern/ghost/intern/GHOST_XrSession.h6
-rw-r--r--intern/ghost/intern/GHOST_XrSwapchain.cpp12
-rw-r--r--intern/glew-mx/intern/gl-deprecated.h10
-rw-r--r--intern/guardedalloc/MEM_guardedalloc.h5
-rw-r--r--intern/guardedalloc/intern/leak_detector.cc19
-rw-r--r--intern/mantaflow/intern/MANTA_main.cpp54
-rw-r--r--intern/mantaflow/intern/manta_fluid_API.cpp4
-rw-r--r--intern/numaapi/source/build_config.h2
-rw-r--r--intern/opencolorio/ocio_impl_glsl.cc18
-rw-r--r--intern/rigidbody/rb_bullet_api.cpp36
-rw-r--r--release/datafiles/icons/brush.gpencil_draw.tint.datbin3158 -> 1970 bytes
-rw-r--r--release/datafiles/icons/brush.paint_texture.draw.datbin3680 -> 2312 bytes
-rw-r--r--release/datafiles/icons/brush.paint_texture.soften.datbin2636 -> 1304 bytes
-rw-r--r--release/datafiles/icons/brush.paint_vertex.blur.datbin2636 -> 1304 bytes
-rw-r--r--release/datafiles/icons/brush.paint_vertex.draw.datbin2258 -> 1574 bytes
-rw-r--r--release/datafiles/icons/brush.paint_vertex.replace.datbin4130 -> 2942 bytes
-rw-r--r--release/datafiles/icons/brush.paint_weight.blur.datbin2636 -> 1304 bytes
-rw-r--r--release/datafiles/icons/brush.paint_weight.draw.datbin2258 -> 1574 bytes
-rw-r--r--release/datafiles/icons/brush.particle.add.datbin2420 -> 2132 bytes
-rw-r--r--release/datafiles/icons/brush.particle.comb.datbin4904 -> 4472 bytes
-rw-r--r--release/datafiles/icons/brush.particle.cut.datbin2600 -> 2456 bytes
-rw-r--r--release/datafiles/icons/brush.particle.length.datbin2096 -> 1736 bytes
-rw-r--r--release/datafiles/icons/brush.particle.puff.datbin2150 -> 1718 bytes
-rw-r--r--release/datafiles/icons/brush.particle.smooth.datbin1394 -> 746 bytes
-rw-r--r--release/datafiles/icons/brush.sculpt.cloth.datbin20258 -> 3698 bytes
-rw-r--r--release/datafiles/icons/brush.sculpt.draw.datbin2420 -> 1844 bytes
-rw-r--r--release/datafiles/icons/brush.sculpt.draw_sharp.datbin2474 -> 2492 bytes
-rw-r--r--release/datafiles/icons/brush.sculpt.elastic_deform.datbin19340 -> 2564 bytes
-rw-r--r--release/datafiles/icons/brush.sculpt.layer.datbin4724 -> 3068 bytes
-rw-r--r--release/datafiles/icons/brush.sculpt.nudge.datbin5516 -> 3212 bytes
-rw-r--r--release/datafiles/icons/brush.sculpt.rotate.datbin55448 -> 7694 bytes
-rw-r--r--release/datafiles/icons/brush.sculpt.snake_hook.datbin5624 -> 2492 bytes
-rw-r--r--release/datafiles/icons/brush.sculpt.thumb.datbin1790 -> 1610 bytes
-rw-r--r--release/datafiles/icons/ops.curve.draw.datbin2564 -> 2564 bytes
-rw-r--r--release/datafiles/icons/ops.gpencil.draw.datbin2060 -> 1268 bytes
-rw-r--r--release/datafiles/icons/ops.gpencil.draw.eraser.datbin2060 -> 1268 bytes
-rw-r--r--release/datafiles/icons/ops.gpencil.draw.line.datbin422 -> 422 bytes
-rw-r--r--release/datafiles/icons/ops.gpencil.draw.poly.datbin548 -> 548 bytes
-rw-r--r--release/datafiles/icons/ops.gpencil.sculpt_smooth.datbin3572 -> 3374 bytes
-rw-r--r--release/datafiles/icons/ops.gpencil.sculpt_strength.datbin3428 -> 3374 bytes
-rw-r--r--release/datafiles/icons/ops.gpencil.sculpt_weight.datbin1358 -> 1358 bytes
-rw-r--r--release/datafiles/icons/ops.paint.weight_sample.datbin1574 -> 1502 bytes
-rw-r--r--release/datafiles/icons/ops.paint.weight_sample_group.datbin1610 -> 1538 bytes
-rw-r--r--release/datafiles/icons/ops.sculpt.cloth_filter.datbin0 -> 5570 bytes
m---------release/datafiles/locale0
-rw-r--r--release/datafiles/userdef/userdef_default_theme.c10
-rw-r--r--release/freedesktop/org.blender.Blender.appdata.xml25
m---------release/scripts/addons0
m---------release/scripts/addons_contrib0
-rw-r--r--release/scripts/modules/addon_utils.py2
-rw-r--r--release/scripts/modules/bpy/ops.py5
-rw-r--r--release/scripts/modules/nodeitems_utils.py8
-rw-r--r--release/scripts/modules/rna_manual_reference.py48
-rw-r--r--release/scripts/modules/rna_prop_ui.py8
-rw-r--r--release/scripts/presets/interface_theme/blender_light.xml6
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/blender_default.py16
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py10
-rw-r--r--release/scripts/startup/bl_app_templates_system/2D_Animation/__init__.py7
-rw-r--r--release/scripts/startup/bl_operators/sequencer.py8
-rw-r--r--release/scripts/startup/bl_operators/userpref.py2
-rw-r--r--release/scripts/startup/bl_operators/wm.py36
-rw-r--r--release/scripts/startup/bl_ui/properties_data_camera.py31
-rw-r--r--release/scripts/startup/bl_ui/properties_data_mesh.py2
-rw-r--r--release/scripts/startup/bl_ui/properties_paint_common.py7
-rw-r--r--release/scripts/startup/bl_ui/properties_view_layer.py4
-rw-r--r--release/scripts/startup/bl_ui/space_image.py3
-rw-r--r--release/scripts/startup/bl_ui/space_node.py12
-rw-r--r--release/scripts/startup/bl_ui/space_sequencer.py1
-rw-r--r--release/scripts/startup/bl_ui/space_toolsystem_toolbar.py7
-rw-r--r--release/scripts/startup/bl_ui/space_userpref.py10
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py42
-rw-r--r--release/scripts/startup/bl_ui/space_view3d_toolbar.py7
-rw-r--r--release/scripts/startup/nodeitems_builtins.py2
-rw-r--r--source/blender/blenfont/BLF_api.h3
-rw-r--r--source/blender/blenfont/intern/blf.c5
-rw-r--r--source/blender/blenfont/intern/blf_font.c13
-rw-r--r--source/blender/blenfont/intern/blf_internal.h1
-rw-r--r--source/blender/blenkernel/BKE_anim_data.h11
-rw-r--r--source/blender/blenkernel/BKE_blender_version.h5
-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_gpencil.h5
-rw-r--r--source/blender/blenkernel/BKE_gpencil_curve.h4
-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_layer.h29
-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_runtime.h13
-rw-r--r--source/blender/blenkernel/BKE_nla.h13
-rw-r--r--source/blender/blenkernel/BKE_paint.h18
-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_screen.h10
-rw-r--r--source/blender/blenkernel/BKE_sequencer.h24
-rw-r--r--source/blender/blenkernel/CMakeLists.txt6
-rw-r--r--source/blender/blenkernel/intern/DerivedMesh.c37
-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/blender.c6
-rw-r--r--source/blender/blenkernel/intern/bpath.c4
-rw-r--r--source/blender/blenkernel/intern/brush.c68
-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/cloth.c13
-rw-r--r--source/blender/blenkernel/intern/collection.c28
-rw-r--r--source/blender/blenkernel/intern/collision.c301
-rw-r--r--source/blender/blenkernel/intern/constraint.c13
-rw-r--r--source/blender/blenkernel/intern/context.c5
-rw-r--r--source/blender/blenkernel/intern/curve.c11
-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.c154
-rw-r--r--source/blender/blenkernel/intern/gpencil_curve.c311
-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/layer.c12
-rw-r--r--source/blender/blenkernel/intern/layer_utils.c73
-rw-r--r--source/blender/blenkernel/intern/lib_id.c40
-rw-r--r--source/blender/blenkernel/intern/lib_override.c459
-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.c64
-rw-r--r--source/blender/blenkernel/intern/mball_tessellate.c8
-rw-r--r--source/blender/blenkernel/intern/mesh.c165
-rw-r--r--source/blender/blenkernel/intern/mesh_convert.c10
-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.c55
-rw-r--r--source/blender/blenkernel/intern/object.c24
-rw-r--r--source/blender/blenkernel/intern/object_dupli.c736
-rw-r--r--source/blender/blenkernel/intern/paint.c22
-rw-r--r--source/blender/blenkernel/intern/particle.c6
-rw-r--r--source/blender/blenkernel/intern/pointcache.c5
-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.c137
-rw-r--r--source/blender/blenkernel/intern/screen.c6
-rw-r--r--source/blender/blenkernel/intern/seqcache.c1
-rw-r--r--source/blender/blenkernel/intern/seqeffects.c67
-rw-r--r--source/blender/blenkernel/intern/seqprefetch.c4
-rw-r--r--source/blender/blenkernel/intern/sequencer.c29
-rw-r--r--source/blender/blenkernel/intern/simulation.cc6
-rw-r--r--source/blender/blenkernel/intern/softbody.c5
-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/unit.c271
-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.hh144
-rw-r--r--source/blender/blenlib/BLI_compiler_compat.h1
-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_listbase.h9
-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_color.h2
-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.h12
-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_span.hh20
-rw-r--r--source/blender/blenlib/BLI_stack.hh95
-rw-r--r--source/blender/blenlib/BLI_utildefines.h6
-rw-r--r--source/blender/blenlib/BLI_vector.hh226
-rw-r--r--source/blender/blenlib/BLI_winstuff.h12
-rw-r--r--source/blender/blenlib/CMakeLists.txt31
-rw-r--r--source/blender/blenlib/intern/BLI_kdopbvh.c39
-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/freetypefont.c3
-rw-r--r--source/blender/blenlib/intern/math_boolean.cc2533
-rw-r--r--source/blender/blenlib/intern/math_color.c2
-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.c50
-rw-r--r--source/blender/blenlib/intern/mesh_boolean.cc3382
-rw-r--r--source/blender/blenlib/intern/mesh_intersect.cc3322
-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.cc77
-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_span_test.cc31
-rw-r--r--source/blender/blenlib/tests/BLI_stack_cxx_test.cc56
-rw-r--r--source/blender/blenlib/tests/BLI_vector_test.cc193
-rw-r--r--source/blender/blenloader/BLO_read_write.h7
-rw-r--r--source/blender/blenloader/BLO_readfile.h3
-rw-r--r--source/blender/blenloader/CMakeLists.txt2
-rw-r--r--source/blender/blenloader/intern/readfile.c1625
-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.c12
-rw-r--r--source/blender/blenloader/intern/versioning_290.c107
-rw-r--r--source/blender/blenloader/intern/versioning_defaults.c2
-rw-r--r--source/blender/blenloader/intern/versioning_legacy.c12
-rw-r--r--source/blender/blenloader/intern/versioning_userdef.c36
-rw-r--r--source/blender/blenloader/intern/writefile.c791
-rw-r--r--source/blender/blenloader/tests/blendfile_loading_base_test.cc2
-rw-r--r--source/blender/blenloader/tests/blendfile_loading_base_test.h4
-rw-r--r--source/blender/blentranslation/intern/blt_lang.c21
-rw-r--r--source/blender/bmesh/CMakeLists.txt19
-rw-r--r--source/blender/bmesh/bmesh_tools.h1
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh_convert.c15
-rw-r--r--source/blender/bmesh/tools/bmesh_boolean.cc479
-rw-r--r--source/blender/bmesh/tools/bmesh_boolean.h (renamed from source/blender/gpu/intern/gpu_shader_private.h)45
-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/compositor/COM_compositor.h1
-rw-r--r--source/blender/depsgraph/CMakeLists.txt4
-rw-r--r--source/blender/depsgraph/DEG_depsgraph.h21
-rw-r--r--source/blender/depsgraph/DEG_depsgraph_build.h33
-rw-r--r--source/blender/depsgraph/DEG_depsgraph_query.h3
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes.cc6
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.cc71
-rw-r--r--source/blender/depsgraph/intern/builder/pipeline.cc11
-rw-r--r--source/blender/depsgraph/intern/builder/pipeline.h2
-rw-r--r--source/blender/depsgraph/intern/builder/pipeline_all_objects.cc77
-rw-r--r--source/blender/depsgraph/intern/builder/pipeline_all_objects.h44
-rw-r--r--source/blender/depsgraph/intern/builder/pipeline_compositor.cc5
-rw-r--r--source/blender/depsgraph/intern/builder/pipeline_compositor.h3
-rw-r--r--source/blender/depsgraph/intern/builder/pipeline_from_ids.cc44
-rw-r--r--source/blender/depsgraph/intern/builder/pipeline_from_ids.h6
-rw-r--r--source/blender/depsgraph/intern/builder/pipeline_render.cc6
-rw-r--r--source/blender/depsgraph/intern/builder/pipeline_render.h2
-rw-r--r--source/blender/depsgraph/intern/builder/pipeline_view_layer.cc7
-rw-r--r--source/blender/depsgraph/intern/builder/pipeline_view_layer.h2
-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_build.cc39
-rw-r--r--source/blender/depsgraph/intern/depsgraph_debug.cc2
-rw-r--r--source/blender/depsgraph/intern/depsgraph_eval.cc45
-rw-r--r--source/blender/depsgraph/intern/depsgraph_query.cc6
-rw-r--r--source/blender/depsgraph/intern/depsgraph_query_iter.cc4
-rw-r--r--source/blender/depsgraph/intern/depsgraph_tag.cc29
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval.cc4
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_flush.cc16
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_flush.h2
-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.h8
-rw-r--r--source/blender/draw/CMakeLists.txt22
-rw-r--r--source/blender/draw/engines/eevee/eevee_bloom.c64
-rw-r--r--source/blender/draw/engines/eevee/eevee_data.c2
-rw-r--r--source/blender/draw/engines/eevee/eevee_effects.c2
-rw-r--r--source/blender/draw/engines/eevee/eevee_engine.c3
-rw-r--r--source/blender/draw/engines/eevee/eevee_lightcache.c16
-rw-r--r--source/blender/draw/engines/eevee/eevee_lightprobes.c26
-rw-r--r--source/blender/draw/engines/eevee/eevee_lights.c2
-rw-r--r--source/blender/draw/engines/eevee/eevee_lookdev.c4
-rw-r--r--source/blender/draw/engines/eevee/eevee_materials.c57
-rw-r--r--source/blender/draw/engines/eevee/eevee_motion_blur.c2
-rw-r--r--source/blender/draw/engines/eevee/eevee_occlusion.c77
-rw-r--r--source/blender/draw/engines/eevee/eevee_private.h48
-rw-r--r--source/blender/draw/engines/eevee/eevee_render.c7
-rw-r--r--source/blender/draw/engines/eevee/eevee_renderpasses.c20
-rw-r--r--source/blender/draw/engines/eevee/eevee_shaders.c69
-rw-r--r--source/blender/draw/engines/eevee/eevee_shadows.c12
-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.c5
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_draw_data.c8
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_engine.c16
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_engine.h16
-rw-r--r--source/blender/draw/engines/overlay/overlay_edit_mesh.c16
-rw-r--r--source/blender/draw/engines/overlay/overlay_private.h12
-rw-r--r--source/blender/draw/engines/overlay/shaders/edit_curve_handle_geom.glsl2
-rw-r--r--source/blender/draw/engines/overlay/shaders/edit_gpencil_vert.glsl2
-rw-r--r--source/blender/draw/engines/select/select_engine.c2
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl1
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_effect_dof_frag.glsl2
-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/engines/workbench/workbench_shader.c34
-rw-r--r--source/blender/draw/intern/DRW_render.h65
-rw-r--r--source/blender/draw/intern/draw_cache.c12
-rw-r--r--source/blender/draw/intern/draw_cache.h1
-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.c18
-rw-r--r--source/blender/draw/intern/draw_cache_impl_metaball.c10
-rw-r--r--source/blender/draw/intern/draw_cache_impl_volume.c2
-rw-r--r--source/blender/draw/intern/draw_cache_inline.h12
-rw-r--r--source/blender/draw/intern/draw_common.c6
-rw-r--r--source/blender/draw/intern/draw_common.h7
-rw-r--r--source/blender/draw/intern/draw_instance_data.c153
-rw-r--r--source/blender/draw/intern/draw_manager.c67
-rw-r--r--source/blender/draw/intern/draw_manager.h7
-rw-r--r--source/blender/draw/intern/draw_manager_data.c48
-rw-r--r--source/blender/draw/intern/draw_manager_exec.c614
-rw-r--r--source/blender/draw/intern/draw_manager_shader.c45
-rw-r--r--source/blender/draw/intern/draw_manager_testing.h (renamed from source/blender/gpu/intern/gpu_primitive_private.h)16
-rw-r--r--source/blender/draw/intern/draw_view.c6
-rw-r--r--source/blender/draw/intern/shaders/common_globals_lib.glsl3
-rw-r--r--source/blender/draw/intern/smaa_textures.h1
-rw-r--r--source/blender/draw/tests/shaders_test.cc279
-rw-r--r--source/blender/editors/animation/anim_channels_defines.c6
-rw-r--r--source/blender/editors/animation/anim_draw.c12
-rw-r--r--source/blender/editors/animation/anim_filter.c2
-rw-r--r--source/blender/editors/animation/anim_markers.c12
-rw-r--r--source/blender/editors/animation/anim_motion_paths.c12
-rw-r--r--source/blender/editors/animation/keyframes_draw.c4
-rw-r--r--source/blender/editors/animation/time_scrub_ui.c6
-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/armature/pose_transform.c44
-rw-r--r--source/blender/editors/curve/editcurve.c111
-rw-r--r--source/blender/editors/curve/editcurve_paint.c12
-rw-r--r--source/blender/editors/datafiles/CMakeLists.txt1
-rw-r--r--source/blender/editors/gizmo_library/gizmo_draw_utils.c4
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c8
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c12
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c8
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c8
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c4
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c12
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/primitive3d_gizmo.c8
-rw-r--r--source/blender/editors/gpencil/annotate_draw.c12
-rw-r--r--source/blender/editors/gpencil/annotate_paint.c10
-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_edit.c5
-rw-r--r--source/blender/editors/gpencil/gpencil_fill.c19
-rw-r--r--source/blender/editors/gpencil/gpencil_merge.c34
-rw-r--r--source/blender/editors/gpencil/gpencil_mesh.c4
-rw-r--r--source/blender/editors/gpencil/gpencil_paint.c6
-rw-r--r--source/blender/editors/gpencil/gpencil_primitive.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_select.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_utils.c14
-rw-r--r--source/blender/editors/include/ED_info.h9
-rw-r--r--source/blender/editors/include/ED_mesh.h2
-rw-r--r--source/blender/editors/include/ED_object.h5
-rw-r--r--source/blender/editors/include/ED_screen.h13
-rw-r--r--source/blender/editors/include/ED_sculpt.h4
-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/include/UI_view2d.h6
-rw-r--r--source/blender/editors/interface/interface.c172
-rw-r--r--source/blender/editors/interface/interface_align.c10
-rw-r--r--source/blender/editors/interface/interface_anim.c4
-rw-r--r--source/blender/editors/interface/interface_context_menu.c17
-rw-r--r--source/blender/editors/interface/interface_draw.c382
-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_eyedropper_gpencil_color.c9
-rw-r--r--source/blender/editors/interface/interface_handlers.c887
-rw-r--r--source/blender/editors/interface/interface_icons.c95
-rw-r--r--source/blender/editors/interface/interface_icons_event.c8
-rw-r--r--source/blender/editors/interface/interface_intern.h32
-rw-r--r--source/blender/editors/interface/interface_layout.c143
-rw-r--r--source/blender/editors/interface/interface_ops.c30
-rw-r--r--source/blender/editors/interface/interface_panel.c1852
-rw-r--r--source/blender/editors/interface/interface_query.c16
-rw-r--r--source/blender/editors/interface/interface_region_color_picker.c9
-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_menu_popup.c37
-rw-r--r--source/blender/editors/interface/interface_region_popover.c25
-rw-r--r--source/blender/editors/interface/interface_region_popup.c40
-rw-r--r--source/blender/editors/interface/interface_region_search.c32
-rw-r--r--source/blender/editors/interface/interface_region_tooltip.c20
-rw-r--r--source/blender/editors/interface/interface_style.c28
-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.c123
-rw-r--r--source/blender/editors/interface/interface_utils.c43
-rw-r--r--source/blender/editors/interface/interface_widgets.c275
-rw-r--r--source/blender/editors/interface/resources.c10
-rw-r--r--source/blender/editors/interface/view2d.c49
-rw-r--r--source/blender/editors/interface/view2d_draw.c54
-rw-r--r--source/blender/editors/interface/view2d_ops.c76
-rw-r--r--source/blender/editors/io/io_alembic.c2
-rw-r--r--source/blender/editors/io/io_usd.c10
-rw-r--r--source/blender/editors/mask/mask_add.c6
-rw-r--r--source/blender/editors/mask/mask_draw.c13
-rw-r--r--source/blender/editors/mesh/CMakeLists.txt4
-rw-r--r--source/blender/editors/mesh/editmesh_intersect.c177
-rw-r--r--source/blender/editors/mesh/editmesh_knife.c18
-rw-r--r--source/blender/editors/mesh/editmesh_mask_extract.c16
-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.c36
-rw-r--r--source/blender/editors/mesh/editmesh_tools.c6
-rw-r--r--source/blender/editors/mesh/editmesh_utils.c73
-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.c57
-rw-r--r--source/blender/editors/object/object_bake_api.c7
-rw-r--r--source/blender/editors/object/object_constraint.c41
-rw-r--r--source/blender/editors/object/object_edit.c66
-rw-r--r--source/blender/editors/object/object_facemap_ops.c128
-rw-r--r--source/blender/editors/object/object_modifier.c2
-rw-r--r--source/blender/editors/object/object_relations.c131
-rw-r--r--source/blender/editors/object/object_remesh.c4
-rw-r--r--source/blender/editors/object/object_vgroup.c183
-rw-r--r--source/blender/editors/physics/particle_edit.c4
-rw-r--r--source/blender/editors/render/render_opengl.c10
-rw-r--r--source/blender/editors/render/render_shading.c387
-rw-r--r--source/blender/editors/render/render_update.c2
-rw-r--r--source/blender/editors/scene/scene_edit.c4
-rw-r--r--source/blender/editors/screen/area.c467
-rw-r--r--source/blender/editors/screen/screen_context.c38
-rw-r--r--source/blender/editors/screen/screen_draw.c36
-rw-r--r--source/blender/editors/screen/screen_edit.c73
-rw-r--r--source/blender/editors/screen/screen_geometry.c9
-rw-r--r--source/blender/editors/screen/screen_ops.c41
-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.c41
-rw-r--r--source/blender/editors/sculpt_paint/paint_image.c4
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_proj.c27
-rw-r--r--source/blender/editors/sculpt_paint/paint_mask.c604
-rw-r--r--source/blender/editors/sculpt_paint/paint_stroke.c26
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c68
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_boundary.c419
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_cloth.c372
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_face_set.c51
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_color.c2
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_mesh.c280
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h75
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_pose.c5
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_smooth.c125
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_transform.c8
-rw-r--r--source/blender/editors/sound/sound_ops.c9
-rw-r--r--source/blender/editors/space_action/action_draw.c8
-rw-r--r--source/blender/editors/space_action/space_action.c2
-rw-r--r--source/blender/editors/space_buttons/CMakeLists.txt5
-rw-r--r--source/blender/editors/space_buttons/buttons_context.c12
-rw-r--r--source/blender/editors/space_buttons/space_buttons.c4
-rw-r--r--source/blender/editors/space_clip/clip_dopesheet_draw.c8
-rw-r--r--source/blender/editors/space_clip/clip_draw.c18
-rw-r--r--source/blender/editors/space_clip/clip_graph_draw.c4
-rw-r--r--source/blender/editors/space_clip/clip_utils.c6
-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.c14
-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/graph_draw.c26
-rw-r--r--source/blender/editors/space_graph/space_graph.c18
-rw-r--r--source/blender/editors/space_image/image_draw.c38
-rw-r--r--source/blender/editors/space_image/space_image.c9
-rw-r--r--source/blender/editors/space_info/info_draw.c1
-rw-r--r--source/blender/editors/space_info/info_stats.c35
-rw-r--r--source/blender/editors/space_info/space_info.c1
-rw-r--r--source/blender/editors/space_info/textview.c10
-rw-r--r--source/blender/editors/space_nla/nla_draw.c26
-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/drawnode.c23
-rw-r--r--source/blender/editors/space_node/node_draw.c118
-rw-r--r--source/blender/editors/space_node/node_edit.c5
-rw-r--r--source/blender/editors/space_node/node_templates.c4
-rw-r--r--source/blender/editors/space_outliner/outliner_draw.c127
-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/script_edit.c6
-rw-r--r--source/blender/editors/space_script/space_script.c1
-rw-r--r--source/blender/editors/space_sequencer/sequencer_buttons.c4
-rw-r--r--source/blender/editors/space_sequencer/sequencer_draw.c133
-rw-r--r--source/blender/editors/space_sequencer/sequencer_edit.c82
-rw-r--r--source/blender/editors/space_sequencer/sequencer_intern.h1
-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_sequencer/sequencer_view.c8
-rw-r--r--source/blender/editors/space_sequencer/space_sequencer.c21
-rw-r--r--source/blender/editors/space_text/space_text.c1
-rw-r--r--source/blender/editors/space_text/text_draw.c27
-rw-r--r--source/blender/editors/space_text/text_ops.c3
-rw-r--r--source/blender/editors/space_userpref/space_userpref.c3
-rw-r--r--source/blender/editors/space_view3d/drawobject.c6
-rw-r--r--source/blender/editors/space_view3d/space_view3d.c6
-rw-r--r--source/blender/editors/space_view3d/view3d_draw.c34
-rw-r--r--source/blender/editors/space_view3d/view3d_edit.c32
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c59
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_ruler.c18
-rw-r--r--source/blender/editors/space_view3d/view3d_placement.c32
-rw-r--r--source/blender/editors/space_view3d/view3d_select.c9
-rw-r--r--source/blender/editors/space_view3d/view3d_view.c8
-rw-r--r--source/blender/editors/transform/transform.c264
-rw-r--r--source/blender/editors/transform/transform.h4
-rw-r--r--source/blender/editors/transform/transform_constraints.c48
-rw-r--r--source/blender/editors/transform/transform_convert_mesh.c2
-rw-r--r--source/blender/editors/transform/transform_convert_object.c6
-rw-r--r--source/blender/editors/transform/transform_draw_cursors.c4
-rw-r--r--source/blender/editors/transform/transform_gizmo_3d.c8
-rw-r--r--source/blender/editors/transform/transform_mode.c2
-rw-r--r--source/blender/editors/transform/transform_mode_baketime.c2
-rw-r--r--source/blender/editors/transform/transform_mode_bbone_resize.c2
-rw-r--r--source/blender/editors/transform/transform_mode_bend.c2
-rw-r--r--source/blender/editors/transform/transform_mode_boneenvelope.c2
-rw-r--r--source/blender/editors/transform/transform_mode_boneroll.c2
-rw-r--r--source/blender/editors/transform/transform_mode_curveshrinkfatten.c2
-rw-r--r--source/blender/editors/transform/transform_mode_edge_bevelweight.c2
-rw-r--r--source/blender/editors/transform/transform_mode_edge_crease.c2
-rw-r--r--source/blender/editors/transform/transform_mode_edge_rotate_normal.c2
-rw-r--r--source/blender/editors/transform/transform_mode_edge_seq_slide.c2
-rw-r--r--source/blender/editors/transform/transform_mode_edge_slide.c14
-rw-r--r--source/blender/editors/transform/transform_mode_gpopacity.c2
-rw-r--r--source/blender/editors/transform/transform_mode_gpshrinkfatten.c2
-rw-r--r--source/blender/editors/transform/transform_mode_maskshrinkfatten.c2
-rw-r--r--source/blender/editors/transform/transform_mode_push_pull.c2
-rw-r--r--source/blender/editors/transform/transform_mode_resize.c2
-rw-r--r--source/blender/editors/transform/transform_mode_rotate.c2
-rw-r--r--source/blender/editors/transform/transform_mode_shear.c2
-rw-r--r--source/blender/editors/transform/transform_mode_shrink_fatten.c2
-rw-r--r--source/blender/editors/transform/transform_mode_skin_resize.c2
-rw-r--r--source/blender/editors/transform/transform_mode_tilt.c2
-rw-r--r--source/blender/editors/transform/transform_mode_tosphere.c2
-rw-r--r--source/blender/editors/transform/transform_mode_trackball.c2
-rw-r--r--source/blender/editors/transform/transform_mode_translate.c23
-rw-r--r--source/blender/editors/transform/transform_mode_vert_slide.c12
-rw-r--r--source/blender/editors/transform/transform_ops.c3
-rw-r--r--source/blender/editors/transform/transform_snap.c302
-rw-r--r--source/blender/editors/transform/transform_snap.h10
-rw-r--r--source/blender/editors/util/ed_util_imbuf.c2
-rw-r--r--source/blender/editors/util/numinput.c6
-rw-r--r--source/blender/editors/uvedit/uvedit_draw.c58
-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/editors/uvedit/uvedit_smart_stitch.c4
-rw-r--r--source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp3
-rw-r--r--source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp2
-rw-r--r--source/blender/freestyle/intern/system/PythonInterpreter.h10
-rw-r--r--source/blender/functions/tests/FN_array_spans_test.cc4
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c9
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c9
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c9
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c9
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c9
-rw-r--r--source/blender/gpu/CMakeLists.txt44
-rw-r--r--source/blender/gpu/GPU_batch.h189
-rw-r--r--source/blender/gpu/GPU_batch_presets.h5
-rw-r--r--source/blender/gpu/GPU_context.h1
-rw-r--r--source/blender/gpu/GPU_debug.h3
-rw-r--r--source/blender/gpu/GPU_drawlist.h (renamed from source/blender/gpu/intern/gpu_attr_binding_private.h)23
-rw-r--r--source/blender/gpu/GPU_element.h2
-rw-r--r--source/blender/gpu/GPU_extensions.h1
-rw-r--r--source/blender/gpu/GPU_framebuffer.h49
-rw-r--r--source/blender/gpu/GPU_immediate.h3
-rw-r--r--source/blender/gpu/GPU_material.h29
-rw-r--r--source/blender/gpu/GPU_matrix.h4
-rw-r--r--source/blender/gpu/GPU_primitive.h7
-rw-r--r--source/blender/gpu/GPU_shader.h75
-rw-r--r--source/blender/gpu/GPU_shader_interface.h117
-rw-r--r--source/blender/gpu/GPU_state.h158
-rw-r--r--source/blender/gpu/GPU_texture.h4
-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_vertex_buffer.h6
-rw-r--r--source/blender/gpu/GPU_viewport.h4
-rw-r--r--source/blender/gpu/intern/gpu_attr_binding.cc83
-rw-r--r--source/blender/gpu/intern/gpu_backend.hh21
-rw-r--r--source/blender/gpu/intern/gpu_batch.cc918
-rw-r--r--source/blender/gpu/intern/gpu_batch_presets.c45
-rw-r--r--source/blender/gpu/intern/gpu_batch_private.hh (renamed from source/blender/gpu/intern/gpu_batch_private.h)23
-rw-r--r--source/blender/gpu/intern/gpu_batch_utils.c1
-rw-r--r--source/blender/gpu/intern/gpu_codegen.c11
-rw-r--r--source/blender/gpu/intern/gpu_context.cc76
-rw-r--r--source/blender/gpu/intern/gpu_context_private.hh34
-rw-r--r--source/blender/gpu/intern/gpu_debug.cc220
-rw-r--r--source/blender/gpu/intern/gpu_drawlist.cc (renamed from source/blender/gpu/intern/gpu_primitive.c)54
-rw-r--r--source/blender/gpu/intern/gpu_drawlist_private.hh44
-rw-r--r--source/blender/gpu/intern/gpu_element.cc5
-rw-r--r--source/blender/gpu/intern/gpu_extensions.cc23
-rw-r--r--source/blender/gpu/intern/gpu_framebuffer.cc1066
-rw-r--r--source/blender/gpu/intern/gpu_framebuffer_private.hh211
-rw-r--r--source/blender/gpu/intern/gpu_immediate.cc653
-rw-r--r--source/blender/gpu/intern/gpu_immediate_private.hh66
-rw-r--r--source/blender/gpu/intern/gpu_init_exit.c10
-rw-r--r--source/blender/gpu/intern/gpu_material.c37
-rw-r--r--source/blender/gpu/intern/gpu_matrix.cc35
-rw-r--r--source/blender/gpu/intern/gpu_node_graph.c8
-rw-r--r--source/blender/gpu/intern/gpu_private.h8
-rw-r--r--source/blender/gpu/intern/gpu_select_pick.c30
-rw-r--r--source/blender/gpu/intern/gpu_select_sample_query.c51
-rw-r--r--source/blender/gpu/intern/gpu_shader.cc899
-rw-r--r--source/blender/gpu/intern/gpu_shader_builtin.c4
-rw-r--r--source/blender/gpu/intern/gpu_shader_interface.cc526
-rw-r--r--source/blender/gpu/intern/gpu_shader_interface.hh255
-rw-r--r--source/blender/gpu/intern/gpu_shader_private.hh80
-rw-r--r--source/blender/gpu/intern/gpu_state.cc468
-rw-r--r--source/blender/gpu/intern/gpu_state_private.hh166
-rw-r--r--source/blender/gpu/intern/gpu_texture.cc42
-rw-r--r--source/blender/gpu/intern/gpu_texture_private.hh53
-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_buffer.cc19
-rw-r--r--source/blender/gpu/intern/gpu_vertex_format.cc117
-rw-r--r--source/blender/gpu/intern/gpu_viewport.c6
-rw-r--r--source/blender/gpu/opengl/gl_backend.hh35
-rw-r--r--source/blender/gpu/opengl/gl_batch.cc381
-rw-r--r--source/blender/gpu/opengl/gl_batch.hh106
-rw-r--r--source/blender/gpu/opengl/gl_context.cc116
-rw-r--r--source/blender/gpu/opengl/gl_context.hh25
-rw-r--r--source/blender/gpu/opengl/gl_debug.cc207
-rw-r--r--source/blender/gpu/opengl/gl_debug.hh46
-rw-r--r--source/blender/gpu/opengl/gl_drawlist.cc247
-rw-r--r--source/blender/gpu/opengl/gl_drawlist.hh86
-rw-r--r--source/blender/gpu/opengl/gl_framebuffer.cc460
-rw-r--r--source/blender/gpu/opengl/gl_framebuffer.hh148
-rw-r--r--source/blender/gpu/opengl/gl_immediate.cc194
-rw-r--r--source/blender/gpu/opengl/gl_immediate.hh81
-rw-r--r--source/blender/gpu/opengl/gl_primitive.hh65
-rw-r--r--source/blender/gpu/opengl/gl_shader.cc456
-rw-r--r--source/blender/gpu/opengl/gl_shader.hh83
-rw-r--r--source/blender/gpu/opengl/gl_shader_interface.cc299
-rw-r--r--source/blender/gpu/opengl/gl_shader_interface.hh61
-rw-r--r--source/blender/gpu/opengl/gl_state.cc421
-rw-r--r--source/blender/gpu/opengl/gl_state.hh77
-rw-r--r--source/blender/gpu/opengl/gl_texture.hh81
-rw-r--r--source/blender/gpu/opengl/gl_uniform_buffer.cc133
-rw-r--r--source/blender/gpu/opengl/gl_uniform_buffer.hh60
-rw-r--r--source/blender/gpu/opengl/gl_vertex_array.cc171
-rw-r--r--source/blender/gpu/opengl/gl_vertex_array.hh49
-rw-r--r--source/blender/gpu/shaders/gpu_shader_2D_image_multi_rect_vert.glsl31
-rw-r--r--source/blender/gpu/shaders/gpu_shader_2D_image_rect_vert.glsl8
-rw-r--r--source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl7
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_output_world.glsl3
-rw-r--r--source/blender/imbuf/intern/colormanagement.c4
-rw-r--r--source/blender/io/alembic/exporter/abc_export_capi.cc17
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_abstract.cc16
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_abstract.h5
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_mesh.cc23
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_transform.cc2
-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/io/common/IO_abstract_hierarchy_iterator.h4
-rw-r--r--source/blender/io/common/intern/abstract_hierarchy_iterator.cc24
-rw-r--r--source/blender/io/common/intern/abstract_hierarchy_iterator_test.cc49
-rw-r--r--source/blender/io/usd/intern/usd_capi.cc31
-rw-r--r--source/blender/io/usd/intern/usd_writer_abstract.cc14
-rw-r--r--source/blender/io/usd/intern/usd_writer_abstract.h4
-rw-r--r--source/blender/io/usd/intern/usd_writer_mesh.cc27
-rw-r--r--source/blender/io/usd/tests/usd_stage_creation_test.cc23
-rw-r--r--source/blender/io/usd/usd.h1
-rw-r--r--source/blender/makesdna/DNA_ID.h4
-rw-r--r--source/blender/makesdna/DNA_brush_types.h19
-rw-r--r--source/blender/makesdna/DNA_modifier_types.h11
-rw-r--r--source/blender/makesdna/DNA_object_types.h15
-rw-r--r--source/blender/makesdna/DNA_outliner_types.h2
-rw-r--r--source/blender/makesdna/DNA_scene_defaults.h1
-rw-r--r--source/blender/makesdna/DNA_screen_types.h16
-rw-r--r--source/blender/makesdna/DNA_space_types.h1
-rw-r--r--source/blender/makesdna/DNA_userdef_types.h11
-rw-r--r--source/blender/makesdna/intern/makesdna.c1
-rw-r--r--source/blender/makesrna/RNA_enum_types.h2
-rw-r--r--source/blender/makesrna/intern/CMakeLists.txt17
-rw-r--r--source/blender/makesrna/intern/makesrna.c8
-rw-r--r--source/blender/makesrna/intern/rna_ID.c18
-rw-r--r--source/blender/makesrna/intern/rna_access_compare_override.c4
-rw-r--r--source/blender/makesrna/intern/rna_armature.c2
-rw-r--r--source/blender/makesrna/intern/rna_brush.c76
-rw-r--r--source/blender/makesrna/intern/rna_color.c10
-rw-r--r--source/blender/makesrna/intern/rna_gpencil.c2
-rw-r--r--source/blender/makesrna/intern/rna_internal.h6
-rw-r--r--source/blender/makesrna/intern/rna_layer.c7
-rw-r--r--source/blender/makesrna/intern/rna_main.c12
-rw-r--r--source/blender/makesrna/intern/rna_main_api.c18
-rw-r--r--source/blender/makesrna/intern/rna_modifier.c32
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c3
-rw-r--r--source/blender/makesrna/intern/rna_object.c8
-rw-r--r--source/blender/makesrna/intern/rna_object_api.c23
-rw-r--r--source/blender/makesrna/intern/rna_object_force.c4
-rw-r--r--source/blender/makesrna/intern/rna_scene.c39
-rw-r--r--source/blender/makesrna/intern/rna_scene_api.c4
-rw-r--r--source/blender/makesrna/intern/rna_screen.c6
-rw-r--r--source/blender/makesrna/intern/rna_sequencer.c2
-rw-r--r--source/blender/makesrna/intern/rna_space.c31
-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/makesrna/intern/rna_wm.c8
-rw-r--r--source/blender/modifiers/CMakeLists.txt4
-rw-r--r--source/blender/modifiers/intern/MOD_boolean.c56
-rw-r--r--source/blender/modifiers/intern/MOD_meshdeform.c37
-rw-r--r--source/blender/modifiers/intern/MOD_multires.c15
-rw-r--r--source/blender/modifiers/intern/MOD_particlesystem.c2
-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/BPY_extern.h40
-rw-r--r--source/blender/python/BPY_extern_python.h (renamed from source/blender/gpu/GPU_attr_binding.h)26
-rw-r--r--source/blender/python/BPY_extern_run.h70
-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.c10
-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/CMakeLists.txt4
-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.c34
-rw-r--r--source/blender/python/generic/py_capi_utils.h1
-rw-r--r--source/blender/python/gpu/gpu_py_batch.c8
-rw-r--r--source/blender/python/gpu/gpu_py_offscreen.c2
-rw-r--r--source/blender/python/gpu/gpu_py_shader.c11
-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/CMakeLists.txt1
-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_capi_utils.c11
-rw-r--r--source/blender/python/intern/bpy_driver.c4
-rw-r--r--source/blender/python/intern/bpy_interface.c367
-rw-r--r--source/blender/python/intern/bpy_interface_run.c422
-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_operator.c10
-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.c9
-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/external_engine.c30
-rw-r--r--source/blender/render/intern/source/pipeline.c6
-rw-r--r--source/blender/simulation/CMakeLists.txt4
-rw-r--r--source/blender/windowmanager/WM_types.h11
-rw-r--r--source/blender/windowmanager/gizmo/WM_gizmo_types.h2
-rw-r--r--source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c12
-rw-r--r--source/blender/windowmanager/intern/wm.c9
-rw-r--r--source/blender/windowmanager/intern/wm_dragdrop.c4
-rw-r--r--source/blender/windowmanager/intern/wm_draw.c29
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c20
-rw-r--r--source/blender/windowmanager/intern/wm_files.c11
-rw-r--r--source/blender/windowmanager/intern/wm_gesture.c17
-rw-r--r--source/blender/windowmanager/intern/wm_init_exit.c5
-rw-r--r--source/blender/windowmanager/intern/wm_operators.c12
-rw-r--r--source/blender/windowmanager/intern/wm_playanim.c12
-rw-r--r--source/blender/windowmanager/intern/wm_surface.c6
-rw-r--r--source/blender/windowmanager/intern/wm_window.c30
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_draw.c2
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_session.c13
-rw-r--r--source/creator/CMakeLists.txt23
-rw-r--r--source/creator/creator.c17
-rw-r--r--source/creator/creator_args.c35
-rw-r--r--source/creator/creator_signals.c2
m---------source/tools0
-rw-r--r--tests/CMakeLists.txt2
-rw-r--r--tests/gtests/testing/testing_main.cc2
-rw-r--r--tests/python/CMakeLists.txt4
-rw-r--r--tests/python/alembic_export_tests.py (renamed from tests/python/alembic_tests.py)90
-rw-r--r--tests/python/boolean_operator.py18
-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
-rw-r--r--tests/python/modifiers.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
927 files changed, 41647 insertions, 26380 deletions
diff --git a/.clang-format b/.clang-format
index cb5cdab496f..8a992fea3a9 100644
--- a/.clang-format
+++ b/.clang-format
@@ -238,6 +238,7 @@ ForEachMacros:
- LISTBASE_FOREACH_BACKWARD
- LISTBASE_FOREACH_MUTABLE
- LISTBASE_FOREACH_BACKWARD_MUTABLE
+ - LISTBASE_FOREACH_INDEX
- MAN_ITER_AXES_BEGIN
- NODE_INSTANCE_HASH_ITER
- NODE_SOCKET_TYPES_BEGIN
@@ -252,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 cd518227e27..76d2d578dc3 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)
@@ -430,6 +436,10 @@ if(WIN32)
option(WITH_TBB_MALLOC_PROXY "Enable the TBB malloc replacement" ON)
endif()
+# This should be turned off when Blender enter beta/rc/release
+option(WITH_EXPERIMENTAL_FEATURES "Enable experimental features (still need to enable them in the user preferences)" ON)
+mark_as_advanced(WITH_EXPERIMENTAL_FEATURES)
+
# Unit testsing
option(WITH_GTESTS "Enable GTest unit testing" OFF)
option(WITH_OPENGL_RENDER_TESTS "Enable OpenGL render related unit testing (Experimental)" OFF)
@@ -1562,6 +1572,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
@@ -1724,6 +1740,7 @@ if(FIRST_RUN)
info_cfg_option(WITH_QUADRIFLOW)
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/cmake/boost.cmake b/build_files/build_environment/cmake/boost.cmake
index 22b5f33e690..d01c62b679c 100644
--- a/build_files/build_environment/cmake/boost.cmake
+++ b/build_files/build_environment/cmake/boost.cmake
@@ -56,6 +56,12 @@ if(WITH_BOOST_PYTHON)
--with-python
--user-config=${JAM_FILE}
)
+ if(WIN32 AND BUILD_MODE STREQUAL Debug)
+ set(BOOST_PYTHON_OPTIONS
+ ${BOOST_PYTHON_OPTIONS}
+ define=BOOST_DEBUG_PYTHON
+ )
+ endif()
endif()
set(BOOST_OPTIONS
diff --git a/build_files/build_environment/cmake/ffmpeg.cmake b/build_files/build_environment/cmake/ffmpeg.cmake
index 164997b9aa5..7fbd613b25d 100644
--- a/build_files/build_environment/cmake/ffmpeg.cmake
+++ b/build_files/build_environment/cmake/ffmpeg.cmake
@@ -95,8 +95,6 @@ ExternalProject_Add(external_ffmpeg
--disable-version3
--disable-debug
--enable-optimizations
- --disable-sse
- --disable-ssse3
--enable-ffplay
--disable-openssl
--disable-securetransport
diff --git a/build_files/build_environment/cmake/openvdb.cmake b/build_files/build_environment/cmake/openvdb.cmake
index f432ade7dd1..f27d3f408a8 100644
--- a/build_files/build_environment/cmake/openvdb.cmake
+++ b/build_files/build_environment/cmake/openvdb.cmake
@@ -20,6 +20,14 @@ if(BUILD_MODE STREQUAL Debug)
set(BLOSC_POST _d)
endif()
+if(WIN32)
+ set(OPENVDB_SHARED ON)
+ set(OPENVDB_STATIC OFF)
+else()
+ set(OPENVDB_SHARED OFF)
+ set(OPENVDB_STATIC ON)
+endif()
+
set(OPENVDB_EXTRA_ARGS
-DBoost_COMPILER:STRING=${BOOST_COMPILER_STRING}
-DBoost_USE_MULTITHREADED=ON
@@ -42,8 +50,10 @@ set(OPENVDB_EXTRA_ARGS
-DOPENEXR_LIBRARYDIR=${LIBDIR}/openexr/lib
# All libs live in openexr, even the ilmbase ones
-DILMBASE_LIBRARYDIR=${LIBDIR}/openexr/lib
- -DOPENVDB_CORE_SHARED=Off
+ -DOPENVDB_CORE_SHARED=${OPENVDB_SHARED}
+ -DOPENVDB_CORE_STATIC=${OPENVDB_STATIC}
-DOPENVDB_BUILD_BINARIES=Off
+ -DCMAKE_DEBUG_POSTFIX=_d
)
if(WIN32)
@@ -87,13 +97,15 @@ if(WIN32)
if(BUILD_MODE STREQUAL Release)
ExternalProject_Add_Step(openvdb after_install
COMMAND ${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/openvdb/include ${HARVEST_TARGET}/openvdb/include
- COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/openvdb/lib/libopenvdb.lib ${HARVEST_TARGET}/openvdb/lib/openvdb.lib
+ COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/openvdb/lib/openvdb.lib ${HARVEST_TARGET}/openvdb/lib/openvdb.lib
+ COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/openvdb/bin/openvdb.dll ${HARVEST_TARGET}/openvdb/bin/openvdb.dll
DEPENDEES install
)
endif()
if(BUILD_MODE STREQUAL Debug)
ExternalProject_Add_Step(openvdb after_install
- COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/openvdb/lib/libopenvdb.lib ${HARVEST_TARGET}/openvdb/lib/openvdb_d.lib
+ COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/openvdb/lib/openvdb_d.lib ${HARVEST_TARGET}/openvdb/lib/openvdb_d.lib
+ COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/openvdb/bin/openvdb_d.dll ${HARVEST_TARGET}/openvdb/bin/openvdb_d.dll
DEPENDEES install
)
endif()
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/build_environment/patches/hdf5.diff b/build_files/build_environment/patches/hdf5.diff
deleted file mode 100644
index cb84625810d..00000000000
--- a/build_files/build_environment/patches/hdf5.diff
+++ /dev/null
@@ -1,11 +0,0 @@
---- UserMacros.cmake
-+++ UserMacros.cmake
-@@ -16,6 +16,8 @@
- if (BUILD_USER_DEFINED_LIBS)
- MACRO_USER_DEFINED_LIBS ()
- endif (BUILD_USER_DEFINED_LIBS)
-+
-+include(Config/cmake/usermacros/windows_mt.cmake)
- #-----------------------------------------------------------------------------
- #------------------- E X A M P L E E N D -----------------------------------
- #-----------------------------------------------------------------------------
diff --git a/build_files/build_environment/patches/openvdb.diff b/build_files/build_environment/patches/openvdb.diff
index a9360cbafd3..db4506025ea 100644
--- a/build_files/build_environment/patches/openvdb.diff
+++ b/build_files/build_environment/patches/openvdb.diff
@@ -1,6 +1,6 @@
diff -Naur orig/cmake/FindIlmBase.cmake openvdb/cmake/FindIlmBase.cmake
---- orig/cmake/FindIlmBase.cmake 2019-12-06 13:11:33 -0700
-+++ openvdb/cmake/FindIlmBase.cmake 2020-01-16 09:06:32 -0700
+--- orig/cmake/FindIlmBase.cmake 2019-12-06 12:11:33 -0700
++++ openvdb/cmake/FindIlmBase.cmake 2020-08-12 12:48:44 -0600
@@ -225,6 +225,12 @@
list(APPEND CMAKE_FIND_LIBRARY_SUFFIXES
"-${IlmBase_VERSION_MAJOR}_${IlmBase_VERSION_MINOR}.lib"
@@ -15,8 +15,8 @@ diff -Naur orig/cmake/FindIlmBase.cmake openvdb/cmake/FindIlmBase.cmake
if(ILMBASE_USE_STATIC_LIBS)
list(APPEND CMAKE_FIND_LIBRARY_SUFFIXES
diff -Naur orig/cmake/FindOpenEXR.cmake openvdb/cmake/FindOpenEXR.cmake
---- orig/cmake/FindOpenEXR.cmake 2019-12-06 13:11:33 -0700
-+++ openvdb/cmake/FindOpenEXR.cmake 2020-01-16 09:06:39 -0700
+--- orig/cmake/FindOpenEXR.cmake 2019-12-06 12:11:33 -0700
++++ openvdb/cmake/FindOpenEXR.cmake 2020-08-12 12:48:44 -0600
@@ -218,6 +218,12 @@
list(APPEND CMAKE_FIND_LIBRARY_SUFFIXES
"-${OpenEXR_VERSION_MAJOR}_${OpenEXR_VERSION_MINOR}.lib"
@@ -31,9 +31,20 @@ diff -Naur orig/cmake/FindOpenEXR.cmake openvdb/cmake/FindOpenEXR.cmake
if(OPENEXR_USE_STATIC_LIBS)
list(APPEND CMAKE_FIND_LIBRARY_SUFFIXES
diff -Naur orig/openvdb/CMakeLists.txt openvdb/openvdb/CMakeLists.txt
---- orig/openvdb/CMakeLists.txt 2019-12-06 13:11:33 -0700
-+++ openvdb/openvdb/CMakeLists.txt 2020-01-16 08:56:25 -0700
-@@ -193,11 +193,12 @@
+--- orig/openvdb/CMakeLists.txt 2019-12-06 12:11:33 -0700
++++ openvdb/openvdb/CMakeLists.txt 2020-08-12 14:12:26 -0600
+@@ -105,7 +105,9 @@
+ # http://boost.2283326.n4.nabble.com/CMake-config-scripts-broken-in-1-70-td4708957.html
+ # https://github.com/boostorg/boost_install/commit/160c7cb2b2c720e74463865ef0454d4c4cd9ae7c
+ set(BUILD_SHARED_LIBS ON)
+- set(Boost_USE_STATIC_LIBS OFF)
++ if(NOT WIN32) # blender links boost statically on windows
++ set(Boost_USE_STATIC_LIBS OFF)
++ endif()
+ endif()
+
+ find_package(Boost ${MINIMUM_BOOST_VERSION} REQUIRED COMPONENTS iostreams system)
+@@ -193,6 +195,7 @@
if(OPENVDB_DISABLE_BOOST_IMPLICIT_LINKING)
add_definitions(-DBOOST_ALL_NO_LIB)
endif()
@@ -41,33 +52,69 @@ diff -Naur orig/openvdb/CMakeLists.txt openvdb/openvdb/CMakeLists.txt
endif()
# @todo Should be target definitions
- if(WIN32)
-- add_definitions(-D_WIN32 -DNOMINMAX -DOPENVDB_DLL)
-+ add_definitions(-D_WIN32 -DNOMINMAX -DOPENVDB_STATICLIB)
- endif()
-
- ##### Core library configuration
-diff -Naur orig/openvdb/cmd/CMakeLists.txt openvdb/openvdb/cmd/CMakeLists.txt
---- orig/openvdb/cmd/CMakeLists.txt 2019-12-06 13:11:33 -0700
-+++ openvdb/openvdb/cmd/CMakeLists.txt 2020-01-16 08:56:25 -0700
-@@ -53,7 +53,7 @@
- endif()
-
- if(WIN32)
-- add_definitions(-D_WIN32 -DNOMINMAX -DOPENVDB_DLL)
-+ add_definitions(-D_WIN32 -DNOMINMAX -DOPENVDB_STATICLIB)
- endif()
-
- # rpath handling
-diff -Naur orig/openvdb/unittest/CMakeLists.txt openvdb/openvdb/unittest/CMakeLists.txt
---- orig/openvdb/unittest/CMakeLists.txt 2019-12-06 13:11:33 -0700
-+++ openvdb/openvdb/unittest/CMakeLists.txt 2020-01-16 08:56:25 -0700
-@@ -49,7 +49,7 @@
- endif()
+@@ -383,7 +386,12 @@
+ # imported targets.
- if(WIN32)
-- add_definitions(-D_WIN32 -DNOMINMAX -DOPENVDB_DLL)
-+ add_definitions(-D_WIN32 -DNOMINMAX -DOPENVDB_STATICLIB)
+ if(OPENVDB_CORE_SHARED)
+- add_library(openvdb_shared SHARED ${OPENVDB_LIBRARY_SOURCE_FILES})
++ if(WIN32)
++ configure_file(version.rc.in ${CMAKE_CURRENT_BINARY_DIR}/version.rc @ONLY)
++ add_library(openvdb_shared SHARED ${OPENVDB_LIBRARY_SOURCE_FILES} ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
++ else()
++ add_library(openvdb_shared SHARED ${OPENVDB_LIBRARY_SOURCE_FILES})
++ endif()
endif()
- ##### VDB unit tests
+ if(OPENVDB_CORE_STATIC)
+diff -Naur orig/openvdb/version.rc.in openvdb/openvdb/version.rc.in
+--- orig/openvdb/version.rc.in 1969-12-31 17:00:00 -0700
++++ openvdb/openvdb/version.rc.in 2020-08-12 14:15:01 -0600
+@@ -0,0 +1,48 @@
++#include <winver.h>
++
++#define VER_FILEVERSION @OpenVDB_MAJOR_VERSION@,@OpenVDB_MINOR_VERSION@,@OpenVDB_PATCH_VERSION@,0
++#define VER_FILEVERSION_STR "@OpenVDB_MAJOR_VERSION@.@OpenVDB_MINOR_VERSION@.@OpenVDB_PATCH_VERSION@.0\0"
++
++#define VER_PRODUCTVERSION @OpenVDB_MAJOR_VERSION@,@OpenVDB_MINOR_VERSION@,@OpenVDB_PATCH_VERSION@,0
++#define VER_PRODUCTVERSION_STR "@OpenVDB_MAJOR_VERSION@.@OpenVDB_MINOR_VERSION@\0"
++
++#ifndef DEBUG
++#define VER_DEBUG 0
++#else
++#define VER_DEBUG VS_FF_DEBUG
++#endif
++
++VS_VERSION_INFO VERSIONINFO
++FILEVERSION VER_FILEVERSION
++PRODUCTVERSION VER_PRODUCTVERSION
++FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
++FILEFLAGS (VER_DEBUG)
++FILEOS VOS__WINDOWS32
++FILETYPE VFT_DLL
++FILESUBTYPE VFT2_UNKNOWN
++BEGIN
++ BLOCK "StringFileInfo"
++ BEGIN
++ BLOCK "040904E4"
++ BEGIN
++ VALUE "FileDescription", "OpenVDB"
++ VALUE "FileVersion", VER_FILEVERSION_STR
++ VALUE "InternalName", "OpenVDB"
++ VALUE "ProductName", "OpenVDB"
++ VALUE "ProductVersion", VER_PRODUCTVERSION_STR
++ END
++ END
++
++ BLOCK "VarFileInfo"
++ BEGIN
++ /* The following line should only be modified for localized versions. */
++ /* It consists of any number of WORD,WORD pairs, with each pair */
++ /* describing a language,codepage combination supported by the file. */
++ /* */
++ /* For example, a file might have values "0x409,1252" indicating that it */
++ /* supports English language (0x409) in the Windows ANSI codepage (1252). */
++
++ VALUE "Translation", 0x409, 1252
++
++ END
++END
diff --git a/build_files/build_environment/patches/usd.diff b/build_files/build_environment/patches/usd.diff
index fe767829a70..27b4955f849 100644
--- a/build_files/build_environment/patches/usd.diff
+++ b/build_files/build_environment/patches/usd.diff
@@ -10,77 +10,6 @@ diff -x .git -ur usd.orig/cmake/defaults/Packages.cmake external_usd/cmake/defau
add_definitions(${TBB_DEFINITIONS})
# --math
-diff -x .git -ur usd.orig/pxr/base/plug/initConfig.cpp external_usd/pxr/base/plug/initConfig.cpp
---- usd.orig/pxr/base/plug/initConfig.cpp.orig 2020-06-12 17:20:07.478199779 +0200
-+++ external_usd/pxr/base/plug/initConfig.cpp 2020-06-12 17:25:28.648588552 +0200
-@@ -69,10 +69,40 @@
-
- ARCH_CONSTRUCTOR(Plug_InitConfig, 2, void)
- {
-+ /* The contents of this constructor have been moved to usd_initialise_plugin_path(...) */
-+}
-+
-+}; // end of anonymous namespace
-+
-+/**
-+ * The contents of this function used to be in the static constructor Plug_InitConfig.
-+ * This static constructor made it impossible for Blender to pass a path to the USD
-+ * library at runtime, as the constructor would run before Blender's main() function.
-+ *
-+ * This function is wrapped in a C function of the same name (defined below),
-+ * so that it can be called from Blender's main() function.
-+ *
-+ * The datafiles_usd_path path is used to point to the USD plugin path when Blender
-+ * has been installed. The fallback_usd_path path should point to the build-time
-+ * location of the USD plugin files so that Blender can be run on a development machine
-+ * without requiring an installation step.
-+ */
-+void
-+usd_initialise_plugin_path(const char *datafiles_usd_path)
-+{
- std::vector<std::string> result;
-
- std::vector<std::string> debugMessages;
-
-+ // Add Blender-specific paths. They MUST end in a slash, or symlinks will not be treated as directory.
-+ if (datafiles_usd_path != NULL && datafiles_usd_path[0] != '\0') {
-+ std::string datafiles_usd_path_str(datafiles_usd_path);
-+ if (datafiles_usd_path_str.back() != '/') {
-+ datafiles_usd_path_str += "/";
-+ }
-+ result.push_back(datafiles_usd_path_str);
-+ }
-+
- // Determine the absolute path to the Plug shared library. Any relative
- // paths specified in the plugin search path will be anchored to this
- // directory, to allow for relocatability. Note that this can fail when pxr
-@@ -114,9 +144,24 @@
- _AppendPathList(&result, installLocation, binaryPath);
- #endif // PXR_INSTALL_LOCATION
-
-- Plug_SetPaths(result, debugMessages);
--}
-+ if (!TfGetenv("PXR_PATH_DEBUG").empty()) {
-+ printf("USD Plugin paths: (%zu in total):\n", result.size());
-+ for(const std::string &path : result) {
-+ printf(" %s\n", path.c_str());
-+ }
-+ }
-
-+ Plug_SetPaths(result, debugMessages);
- }
-
- PXR_NAMESPACE_CLOSE_SCOPE
-+
-+/* Workaround to make it possible to pass a path at runtime to USD. */
-+extern "C" {
-+void
-+usd_initialise_plugin_path(
-+ const char *datafiles_usd_path)
-+{
-+ PXR_NS::usd_initialise_plugin_path(datafiles_usd_path);
-+}
-+}
diff -Naur external_usd_base/cmake/macros/Public.cmake external_usd/cmake/macros/Public.cmake
--- external_usd_base/cmake/macros/Public.cmake 2019-10-24 14:39:53 -0600
+++ external_usd/cmake/macros/Public.cmake 2020-01-11 13:33:29 -0700
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/FindGMP.cmake b/build_files/cmake/Modules/FindGMP.cmake
index e1795984985..a06b8737648 100644
--- a/build_files/cmake/Modules/FindGMP.cmake
+++ b/build_files/cmake/Modules/FindGMP.cmake
@@ -80,7 +80,7 @@ FIND_PACKAGE_HANDLE_STANDARD_ARGS(GMP DEFAULT_MSG
GMP_LIBRARY GMPXX_LIBRARY GMP_INCLUDE_DIR GMPXX_INCLUDE_DIR)
IF(GMP_FOUND)
- SET(GMP_LIBRARIES ${GMP_LIBRARY} ${GMPXX_LIBRARY})
+ SET(GMP_LIBRARIES ${GMPXX_LIBRARY} ${GMP_LIBRARY})
SET(GMP_INCLUDE_DIRS ${GMP_INCLUDE_DIR} ${GMPXX_INCLUDE_DIR})
ENDIF(GMP_FOUND)
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..95f27b65ce6 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..c6aa359c82c 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 5949e761551..822110cb88f 100644
--- a/build_files/cmake/platform/platform_apple.cmake
+++ b/build_files/cmake/platform/platform_apple.cmake
@@ -373,8 +373,9 @@ if(WITH_CYCLES_OSL)
list(APPEND OSL_LIBRARIES ${OSL_LIB_COMP} -force_load ${OSL_LIB_EXEC} ${OSL_LIB_QUERY})
find_path(OSL_INCLUDE_DIR OSL/oslclosure.h PATHS ${CYCLES_OSL}/include)
find_program(OSL_COMPILER NAMES oslc PATHS ${CYCLES_OSL}/bin)
+ find_path(OSL_SHADER_DIR NAMES stdosl.h PATHS ${CYCLES_OSL}/shaders)
- if(OSL_INCLUDE_DIR AND OSL_LIBRARIES AND OSL_COMPILER)
+ if(OSL_INCLUDE_DIR AND OSL_LIBRARIES AND OSL_COMPILER AND OSL_SHADER_DIR)
set(OSL_FOUND TRUE)
else()
message(STATUS "OSL not found")
@@ -406,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 dfcd5d75444..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)
@@ -416,9 +423,6 @@ if(WITH_BOOST)
if(WITH_INTERNATIONAL)
list(APPEND boost_extra_libs locale)
endif()
- if(WITH_OPENVDB)
- list(APPEND boost_extra_libs iostreams)
- endif()
set(Boost_USE_STATIC_RUNTIME ON) # prefix lib
set(Boost_USE_MULTITHREADED ON) # suffix -mt
set(Boost_USE_STATIC_LIBS ON) # suffix -s
@@ -524,12 +528,11 @@ if(WITH_OPENCOLORIO)
endif()
if(WITH_OPENVDB)
- set(BLOSC_LIBRARIES optimized ${LIBDIR}/blosc/lib/libblosc.lib debug ${LIBDIR}/blosc/lib/libblosc_d.lib)
set(OPENVDB ${LIBDIR}/openVDB)
set(OPENVDB_LIBPATH ${OPENVDB}/lib)
set(OPENVDB_INCLUDE_DIRS ${OPENVDB}/include)
- set(OPENVDB_LIBRARIES optimized ${OPENVDB_LIBPATH}/openvdb.lib debug ${OPENVDB_LIBPATH}/openvdb_d.lib ${BLOSC_LIBRARIES})
- set(OPENVDB_DEFINITIONS -DNOMINMAX -DOPENVDB_STATICLIB -D_USE_MATH_DEFINES)
+ set(OPENVDB_LIBRARIES optimized ${OPENVDB_LIBPATH}/openvdb.lib debug ${OPENVDB_LIBPATH}/openvdb_d.lib )
+ set(OPENVDB_DEFINITIONS -DNOMINMAX -D_USE_MATH_DEFINES)
endif()
if(WITH_OPENIMAGEDENOISE)
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/audaspace/bindings/python/PySound.cpp b/extern/audaspace/bindings/python/PySound.cpp
index f2debccfd33..33628307249 100644
--- a/extern/audaspace/bindings/python/PySound.cpp
+++ b/extern/audaspace/bindings/python/PySound.cpp
@@ -2016,7 +2016,7 @@ AUD_API Sound* checkSound(PyObject* sound)
bool initializeSound()
{
- import_array();
+ import_array1(false);
return PyType_Ready(&SoundType) >= 0;
}
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/mantaflow/CMakeLists.txt b/extern/mantaflow/CMakeLists.txt
index 9f66b42c6bf..8bb0bc69f98 100644
--- a/extern/mantaflow/CMakeLists.txt
+++ b/extern/mantaflow/CMakeLists.txt
@@ -64,7 +64,6 @@ endif()
if(WITH_OPENVDB)
add_definitions(-DOPENVDB=1)
- add_definitions(-DOPENVDB_STATICLIB)
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/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/app/cycles_xml.cpp b/intern/cycles/app/cycles_xml.cpp
index aec00f845f3..a86e06f92d1 100644
--- a/intern/cycles/app/cycles_xml.cpp
+++ b/intern/cycles/app/cycles_xml.cpp
@@ -292,7 +292,7 @@ static void xml_read_shader_graph(XMLReadState &state, Shader *shader, xml_node
filepath = path_join(state.base, filepath);
}
- snode = OSLShaderManager::osl_node(manager, filepath);
+ snode = OSLShaderManager::osl_node(graph, manager, filepath, "");
if (!snode) {
fprintf(stderr, "Failed to create OSL node from \"%s\".\n", filepath.c_str());
diff --git a/intern/cycles/blender/CMakeLists.txt b/intern/cycles/blender/CMakeLists.txt
index 2316800e21e..e0fa45dffbc 100644
--- a/intern/cycles/blender/CMakeLists.txt
+++ b/intern/cycles/blender/CMakeLists.txt
@@ -109,6 +109,10 @@ if(WITH_OPENIMAGEDENOISE)
)
endif()
+if(WITH_EXPERIMENTAL_FEATURES)
+ add_definitions(-DWITH_HAIR_NODES)
+endif()
+
blender_add_lib(bf_intern_cycles "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
# avoid link failure with clang 3.4 debug
diff --git a/intern/cycles/blender/blender_curves.cpp b/intern/cycles/blender/blender_curves.cpp
index 82c99631a89..6288c370567 100644
--- a/intern/cycles/blender/blender_curves.cpp
+++ b/intern/cycles/blender/blender_curves.cpp
@@ -628,6 +628,7 @@ void BlenderSync::sync_particle_hair(
}
}
+#ifdef WITH_HAIR_NODES
static float4 hair_point_as_float4(BL::HairPoint b_point)
{
float4 mP = float3_to_float4(get_float3(b_point.co()));
@@ -806,6 +807,15 @@ void BlenderSync::sync_hair(Hair *hair, BL::Object &b_ob, bool motion, int motio
export_hair_curves(scene, hair, b_hair);
}
}
+#else
+void BlenderSync::sync_hair(Hair *hair, BL::Object &b_ob, bool motion, int motion_step)
+{
+ (void)hair;
+ (void)b_ob;
+ (void)motion;
+ (void)motion_step;
+}
+#endif
void BlenderSync::sync_hair(BL::Depsgraph b_depsgraph,
BL::Object b_ob,
diff --git a/intern/cycles/blender/blender_geometry.cpp b/intern/cycles/blender/blender_geometry.cpp
index f7e4623024d..002f5e0fdb7 100644
--- a/intern/cycles/blender/blender_geometry.cpp
+++ b/intern/cycles/blender/blender_geometry.cpp
@@ -19,6 +19,7 @@
#include "render/hair.h"
#include "render/mesh.h"
#include "render/object.h"
+#include "render/volume.h"
#include "blender/blender_sync.h"
#include "blender/blender_util.h"
@@ -27,6 +28,19 @@
CCL_NAMESPACE_BEGIN
+static Geometry::Type determine_geom_type(BL::Object &b_ob, bool use_particle_hair)
+{
+ if (b_ob.type() == BL::Object::type_HAIR || use_particle_hair) {
+ return Geometry::HAIR;
+ }
+
+ if (b_ob.type() == BL::Object::type_VOLUME || object_fluid_gas_domain_find(b_ob)) {
+ return Geometry::VOLUME;
+ }
+
+ return Geometry::MESH;
+}
+
Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph,
BL::Object &b_ob,
BL::Object &b_ob_instance,
@@ -40,9 +54,7 @@ Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph,
BL::Material material_override = view_layer.material_override;
Shader *default_shader = (b_ob.type() == BL::Object::type_VOLUME) ? scene->default_volume :
scene->default_surface;
- Geometry::Type geom_type = (b_ob.type() == BL::Object::type_HAIR || use_particle_hair) ?
- Geometry::HAIR :
- Geometry::MESH;
+ Geometry::Type geom_type = determine_geom_type(b_ob, use_particle_hair);
/* Find shader indices. */
vector<Shader *> used_shaders;
@@ -71,10 +83,13 @@ Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph,
if (geom == NULL) {
/* Add new geometry if it did not exist yet. */
if (geom_type == Geometry::HAIR) {
- geom = new Hair();
+ geom = scene->create_node<Hair>();
+ }
+ else if (geom_type == Geometry::VOLUME) {
+ geom = scene->create_node<Volume>();
}
else {
- geom = new Mesh();
+ geom = scene->create_node<Mesh>();
}
geometry_map.add(key, geom);
}
@@ -121,13 +136,13 @@ Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph,
geom->name = ustring(b_ob_data.name().c_str());
- if (b_ob.type() == BL::Object::type_HAIR || use_particle_hair) {
+ if (geom_type == Geometry::HAIR) {
Hair *hair = static_cast<Hair *>(geom);
sync_hair(b_depsgraph, b_ob, hair, used_shaders);
}
- else if (b_ob.type() == BL::Object::type_VOLUME || object_fluid_gas_domain_find(b_ob)) {
- Mesh *mesh = static_cast<Mesh *>(geom);
- sync_volume(b_ob, mesh, used_shaders);
+ else if (geom_type == Geometry::VOLUME) {
+ Volume *volume = static_cast<Volume *>(geom);
+ sync_volume(b_ob, volume, used_shaders);
}
else {
Mesh *mesh = static_cast<Mesh *>(geom);
diff --git a/intern/cycles/blender/blender_id_map.h b/intern/cycles/blender/blender_id_map.h
index b5f6aaa67a8..f9f201c2e4e 100644
--- a/intern/cycles/blender/blender_id_map.h
+++ b/intern/cycles/blender/blender_id_map.h
@@ -19,6 +19,8 @@
#include <string.h>
+#include "render/scene.h"
+
#include "util/util_map.h"
#include "util/util_set.h"
#include "util/util_vector.h"
@@ -32,9 +34,8 @@ CCL_NAMESPACE_BEGIN
template<typename K, typename T> class id_map {
public:
- id_map(vector<T *> *scene_data_)
+ id_map()
{
- scene_data = scene_data_;
}
T *find(const BL::ID &id)
@@ -76,7 +77,6 @@ template<typename K, typename T> class id_map {
void add(const K &key, T *data)
{
assert(find(key) == NULL);
- scene_data->push_back(data);
b_map[key] = data;
used(data);
}
@@ -97,22 +97,23 @@ template<typename K, typename T> class id_map {
}
/* Combined add and update as needed. */
- bool add_or_update(T **r_data, const BL::ID &id)
+ bool add_or_update(Scene *scene, T **r_data, const BL::ID &id)
{
- return add_or_update(r_data, id, id, id.ptr.owner_id);
+ return add_or_update(scene, r_data, id, id, id.ptr.owner_id);
}
- bool add_or_update(T **r_data, const BL::ID &id, const K &key)
+ bool add_or_update(Scene *scene, T **r_data, const BL::ID &id, const K &key)
{
- return add_or_update(r_data, id, id, key);
+ return add_or_update(scene, r_data, id, id, key);
}
- bool add_or_update(T **r_data, const BL::ID &id, const BL::ID &parent, const K &key)
+ bool add_or_update(
+ Scene *scene, T **r_data, const BL::ID &id, const BL::ID &parent, const K &key)
{
T *data = find(key);
bool recalc;
if (!data) {
/* Add data if it didn't exist yet. */
- data = new T();
+ data = scene->create_node<T>();
add(key, data);
recalc = true;
}
@@ -144,27 +145,8 @@ template<typename K, typename T> class id_map {
b_map[NULL] = data;
}
- bool post_sync(bool do_delete = true)
+ void post_sync(Scene *scene, bool do_delete = true)
{
- /* remove unused data */
- vector<T *> new_scene_data;
- typename vector<T *>::iterator it;
- bool deleted = false;
-
- for (it = scene_data->begin(); it != scene_data->end(); it++) {
- T *data = *it;
-
- if (do_delete && used_set.find(data) == used_set.end()) {
- delete data;
- deleted = true;
- }
- else
- new_scene_data.push_back(data);
- }
-
- *scene_data = new_scene_data;
-
- /* update mapping */
map<K, T *> new_map;
typedef pair<const K, T *> TMapPair;
typename map<K, T *>::iterator jt;
@@ -172,15 +154,17 @@ template<typename K, typename T> class id_map {
for (jt = b_map.begin(); jt != b_map.end(); jt++) {
TMapPair &pair = *jt;
- if (used_set.find(pair.second) != used_set.end())
+ if (do_delete && used_set.find(pair.second) == used_set.end()) {
+ scene->delete_node(pair.second);
+ }
+ else {
new_map[pair.first] = pair.second;
+ }
}
used_set.clear();
b_recalc.clear();
b_map = new_map;
-
- return deleted;
}
const map<K, T *> &key_to_scene_data()
@@ -189,7 +173,6 @@ template<typename K, typename T> class id_map {
}
protected:
- vector<T *> *scene_data;
map<K, T *> b_map;
set<T *> used_set;
set<void *> b_recalc;
diff --git a/intern/cycles/blender/blender_light.cpp b/intern/cycles/blender/blender_light.cpp
index 6f95821e31e..117e9214e5a 100644
--- a/intern/cycles/blender/blender_light.cpp
+++ b/intern/cycles/blender/blender_light.cpp
@@ -39,9 +39,9 @@ void BlenderSync::sync_light(BL::Object &b_parent,
BL::Light b_light(b_ob.data());
/* Update if either object or light data changed. */
- if (!light_map.add_or_update(&light, b_ob, b_parent, key)) {
+ if (!light_map.add_or_update(scene, &light, b_ob, b_parent, key)) {
Shader *shader;
- if (!shader_map.add_or_update(&shader, b_light)) {
+ if (!shader_map.add_or_update(scene, &shader, b_light)) {
if (light->is_portal)
*use_portal = true;
return;
@@ -176,7 +176,7 @@ void BlenderSync::sync_background_light(BL::SpaceView3D &b_v3d, bool use_portal)
Light *light;
ObjectKey key(b_world, 0, b_world, false);
- if (light_map.add_or_update(&light, b_world, b_world, key) || world_recalc ||
+ if (light_map.add_or_update(scene, &light, b_world, b_world, key) || world_recalc ||
b_world.ptr.data != world_map) {
light->type = LIGHT_BACKGROUND;
if (sampling_method == SAMPLING_MANUAL) {
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_object.cpp b/intern/cycles/blender/blender_object.cpp
index 3ea6892a349..212b9cbe103 100644
--- a/intern/cycles/blender/blender_object.cpp
+++ b/intern/cycles/blender/blender_object.cpp
@@ -207,7 +207,7 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph,
/* test if we need to sync */
bool object_updated = false;
- if (object_map.add_or_update(&object, b_ob, b_parent, key))
+ if (object_map.add_or_update(scene, &object, b_ob, b_parent, key))
object_updated = true;
/* mesh sync */
@@ -405,14 +405,10 @@ void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph,
sync_background_light(b_v3d, use_portal);
/* handle removed data and modified pointers */
- if (light_map.post_sync())
- scene->light_manager->tag_update(scene);
- if (geometry_map.post_sync())
- scene->geometry_manager->tag_update(scene);
- if (object_map.post_sync())
- scene->object_manager->tag_update(scene);
- if (particle_system_map.post_sync())
- scene->particle_system_manager->tag_update(scene);
+ light_map.post_sync(scene);
+ geometry_map.post_sync(scene);
+ object_map.post_sync(scene);
+ particle_system_map.post_sync(scene);
}
if (motion)
@@ -458,15 +454,19 @@ void BlenderSync::sync_motion(BL::RenderSettings &b_render,
python_thread_state_restore(python_thread_state);
b_engine.frame_set(frame, subframe);
python_thread_state_save(python_thread_state);
- sync_camera_motion(b_render, b_cam, width, height, 0.0f);
+ if (b_cam) {
+ sync_camera_motion(b_render, b_cam, width, height, 0.0f);
+ }
sync_objects(b_depsgraph, b_v3d, 0.0f);
}
/* Insert motion times from camera. Motion times from other objects
* have already been added in a sync_objects call. */
- uint camera_motion_steps = object_motion_steps(b_cam, b_cam);
- for (size_t step = 0; step < camera_motion_steps; step++) {
- motion_times.insert(scene->camera->motion_time(step));
+ if (b_cam) {
+ uint camera_motion_steps = object_motion_steps(b_cam, b_cam);
+ for (size_t step = 0; step < camera_motion_steps; step++) {
+ motion_times.insert(scene->camera->motion_time(step));
+ }
}
/* note iteration over motion_times set happens in sorted order */
diff --git a/intern/cycles/blender/blender_particles.cpp b/intern/cycles/blender/blender_particles.cpp
index e5eab1ae62b..ee14217988b 100644
--- a/intern/cycles/blender/blender_particles.cpp
+++ b/intern/cycles/blender/blender_particles.cpp
@@ -53,7 +53,8 @@ bool BlenderSync::sync_dupli_particle(BL::Object &b_ob,
ParticleSystem *psys;
bool first_use = !particle_system_map.is_used(key);
- bool need_update = particle_system_map.add_or_update(&psys, b_ob, b_instance.object(), key);
+ bool need_update = particle_system_map.add_or_update(
+ scene, &psys, b_ob, b_instance.object(), key);
/* no update needed? */
if (!need_update && !object->geometry->need_update && !scene->object_manager->need_update)
diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp
index a06030c8b7d..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);
@@ -645,7 +646,7 @@ void BlenderSession::bake(BL::Depsgraph &b_depsgraph_,
/* Passes are identified by name, so in order to return the combined pass we need to set the
* name. */
- Pass::add(PASS_COMBINED, scene->film->passes, "Combined");
+ Pass::add(PASS_COMBINED, scene->passes, "Combined");
session->read_bake_tile_cb = function_bind(&BlenderSession::read_render_tile, this, _1);
session->write_render_tile_cb = function_bind(&BlenderSession::write_render_tile, this, _1);
@@ -678,7 +679,7 @@ void BlenderSession::bake(BL::Depsgraph &b_depsgraph_,
BufferParams buffer_params;
buffer_params.width = bake_width;
buffer_params.height = bake_height;
- buffer_params.passes = scene->film->passes;
+ buffer_params.passes = scene->passes;
/* Update session. */
session->tile_manager.set_samples(session_params.samples);
diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp
index ae681432a43..03ed88ea9ec 100644
--- a/intern/cycles/blender/blender_shader.cpp
+++ b/intern/cycles/blender/blender_shader.cpp
@@ -224,7 +224,7 @@ static ShaderNode *add_node(Scene *scene,
if (b_node.is_a(&RNA_ShaderNodeRGBCurve)) {
BL::ShaderNodeRGBCurve b_curve_node(b_node);
BL::CurveMapping mapping(b_curve_node.mapping());
- RGBCurvesNode *curves = new RGBCurvesNode();
+ RGBCurvesNode *curves = graph->create_node<RGBCurvesNode>();
curvemapping_color_to_array(mapping, curves->curves, RAMP_TABLE_SIZE, true);
curvemapping_minmax(mapping, true, &curves->min_x, &curves->max_x);
node = curves;
@@ -232,13 +232,13 @@ static ShaderNode *add_node(Scene *scene,
if (b_node.is_a(&RNA_ShaderNodeVectorCurve)) {
BL::ShaderNodeVectorCurve b_curve_node(b_node);
BL::CurveMapping mapping(b_curve_node.mapping());
- VectorCurvesNode *curves = new VectorCurvesNode();
+ VectorCurvesNode *curves = graph->create_node<VectorCurvesNode>();
curvemapping_color_to_array(mapping, curves->curves, RAMP_TABLE_SIZE, false);
curvemapping_minmax(mapping, false, &curves->min_x, &curves->max_x);
node = curves;
}
else if (b_node.is_a(&RNA_ShaderNodeValToRGB)) {
- RGBRampNode *ramp = new RGBRampNode();
+ RGBRampNode *ramp = graph->create_node<RGBRampNode>();
BL::ShaderNodeValToRGB b_ramp_node(b_node);
BL::ColorRamp b_color_ramp(b_ramp_node.color_ramp());
colorramp_to_array(b_color_ramp, ramp->ramp, ramp->ramp_alpha, RAMP_TABLE_SIZE);
@@ -246,94 +246,94 @@ static ShaderNode *add_node(Scene *scene,
node = ramp;
}
else if (b_node.is_a(&RNA_ShaderNodeRGB)) {
- ColorNode *color = new ColorNode();
+ ColorNode *color = graph->create_node<ColorNode>();
color->value = get_node_output_rgba(b_node, "Color");
node = color;
}
else if (b_node.is_a(&RNA_ShaderNodeValue)) {
- ValueNode *value = new ValueNode();
+ ValueNode *value = graph->create_node<ValueNode>();
value->value = get_node_output_value(b_node, "Value");
node = value;
}
else if (b_node.is_a(&RNA_ShaderNodeCameraData)) {
- node = new CameraNode();
+ node = graph->create_node<CameraNode>();
}
else if (b_node.is_a(&RNA_ShaderNodeInvert)) {
- node = new InvertNode();
+ node = graph->create_node<InvertNode>();
}
else if (b_node.is_a(&RNA_ShaderNodeGamma)) {
- node = new GammaNode();
+ node = graph->create_node<GammaNode>();
}
else if (b_node.is_a(&RNA_ShaderNodeBrightContrast)) {
- node = new BrightContrastNode();
+ node = graph->create_node<BrightContrastNode>();
}
else if (b_node.is_a(&RNA_ShaderNodeMixRGB)) {
BL::ShaderNodeMixRGB b_mix_node(b_node);
- MixNode *mix = new MixNode();
+ MixNode *mix = graph->create_node<MixNode>();
mix->type = (NodeMix)b_mix_node.blend_type();
mix->use_clamp = b_mix_node.use_clamp();
node = mix;
}
else if (b_node.is_a(&RNA_ShaderNodeSeparateRGB)) {
- node = new SeparateRGBNode();
+ node = graph->create_node<SeparateRGBNode>();
}
else if (b_node.is_a(&RNA_ShaderNodeCombineRGB)) {
- node = new CombineRGBNode();
+ node = graph->create_node<CombineRGBNode>();
}
else if (b_node.is_a(&RNA_ShaderNodeSeparateHSV)) {
- node = new SeparateHSVNode();
+ node = graph->create_node<SeparateHSVNode>();
}
else if (b_node.is_a(&RNA_ShaderNodeCombineHSV)) {
- node = new CombineHSVNode();
+ node = graph->create_node<CombineHSVNode>();
}
else if (b_node.is_a(&RNA_ShaderNodeSeparateXYZ)) {
- node = new SeparateXYZNode();
+ node = graph->create_node<SeparateXYZNode>();
}
else if (b_node.is_a(&RNA_ShaderNodeCombineXYZ)) {
- node = new CombineXYZNode();
+ node = graph->create_node<CombineXYZNode>();
}
else if (b_node.is_a(&RNA_ShaderNodeHueSaturation)) {
- node = new HSVNode();
+ node = graph->create_node<HSVNode>();
}
else if (b_node.is_a(&RNA_ShaderNodeRGBToBW)) {
- node = new RGBToBWNode();
+ node = graph->create_node<RGBToBWNode>();
}
else if (b_node.is_a(&RNA_ShaderNodeMapRange)) {
BL::ShaderNodeMapRange b_map_range_node(b_node);
- MapRangeNode *map_range_node = new MapRangeNode();
+ MapRangeNode *map_range_node = graph->create_node<MapRangeNode>();
map_range_node->clamp = b_map_range_node.clamp();
map_range_node->type = (NodeMapRangeType)b_map_range_node.interpolation_type();
node = map_range_node;
}
else if (b_node.is_a(&RNA_ShaderNodeClamp)) {
BL::ShaderNodeClamp b_clamp_node(b_node);
- ClampNode *clamp_node = new ClampNode();
+ ClampNode *clamp_node = graph->create_node<ClampNode>();
clamp_node->type = (NodeClampType)b_clamp_node.clamp_type();
node = clamp_node;
}
else if (b_node.is_a(&RNA_ShaderNodeMath)) {
BL::ShaderNodeMath b_math_node(b_node);
- MathNode *math_node = new MathNode();
+ MathNode *math_node = graph->create_node<MathNode>();
math_node->type = (NodeMathType)b_math_node.operation();
math_node->use_clamp = b_math_node.use_clamp();
node = math_node;
}
else if (b_node.is_a(&RNA_ShaderNodeVectorMath)) {
BL::ShaderNodeVectorMath b_vector_math_node(b_node);
- VectorMathNode *vector_math_node = new VectorMathNode();
+ VectorMathNode *vector_math_node = graph->create_node<VectorMathNode>();
vector_math_node->type = (NodeVectorMathType)b_vector_math_node.operation();
node = vector_math_node;
}
else if (b_node.is_a(&RNA_ShaderNodeVectorRotate)) {
BL::ShaderNodeVectorRotate b_vector_rotate_node(b_node);
- VectorRotateNode *vector_rotate_node = new VectorRotateNode();
+ VectorRotateNode *vector_rotate_node = graph->create_node<VectorRotateNode>();
vector_rotate_node->type = (NodeVectorRotateType)b_vector_rotate_node.rotation_type();
vector_rotate_node->invert = b_vector_rotate_node.invert();
node = vector_rotate_node;
}
else if (b_node.is_a(&RNA_ShaderNodeVectorTransform)) {
BL::ShaderNodeVectorTransform b_vector_transform_node(b_node);
- VectorTransformNode *vtransform = new VectorTransformNode();
+ VectorTransformNode *vtransform = graph->create_node<VectorTransformNode>();
vtransform->type = (NodeVectorTransformType)b_vector_transform_node.vector_type();
vtransform->convert_from = (NodeVectorTransformConvertSpace)
b_vector_transform_node.convert_from();
@@ -344,43 +344,43 @@ static ShaderNode *add_node(Scene *scene,
BL::Node::outputs_iterator out_it;
b_node.outputs.begin(out_it);
- NormalNode *norm = new NormalNode();
+ NormalNode *norm = graph->create_node<NormalNode>();
norm->direction = get_node_output_vector(b_node, "Normal");
node = norm;
}
else if (b_node.is_a(&RNA_ShaderNodeMapping)) {
BL::ShaderNodeMapping b_mapping_node(b_node);
- MappingNode *mapping = new MappingNode();
+ MappingNode *mapping = graph->create_node<MappingNode>();
mapping->type = (NodeMappingType)b_mapping_node.vector_type();
node = mapping;
}
else if (b_node.is_a(&RNA_ShaderNodeFresnel)) {
- node = new FresnelNode();
+ node = graph->create_node<FresnelNode>();
}
else if (b_node.is_a(&RNA_ShaderNodeLayerWeight)) {
- node = new LayerWeightNode();
+ node = graph->create_node<LayerWeightNode>();
}
else if (b_node.is_a(&RNA_ShaderNodeAddShader)) {
- node = new AddClosureNode();
+ node = graph->create_node<AddClosureNode>();
}
else if (b_node.is_a(&RNA_ShaderNodeMixShader)) {
- node = new MixClosureNode();
+ node = graph->create_node<MixClosureNode>();
}
else if (b_node.is_a(&RNA_ShaderNodeAttribute)) {
BL::ShaderNodeAttribute b_attr_node(b_node);
- AttributeNode *attr = new AttributeNode();
+ AttributeNode *attr = graph->create_node<AttributeNode>();
attr->attribute = b_attr_node.attribute_name();
node = attr;
}
else if (b_node.is_a(&RNA_ShaderNodeBackground)) {
- node = new BackgroundNode();
+ node = graph->create_node<BackgroundNode>();
}
else if (b_node.is_a(&RNA_ShaderNodeHoldout)) {
- node = new HoldoutNode();
+ node = graph->create_node<HoldoutNode>();
}
else if (b_node.is_a(&RNA_ShaderNodeBsdfAnisotropic)) {
BL::ShaderNodeBsdfAnisotropic b_aniso_node(b_node);
- AnisotropicBsdfNode *aniso = new AnisotropicBsdfNode();
+ AnisotropicBsdfNode *aniso = graph->create_node<AnisotropicBsdfNode>();
switch (b_aniso_node.distribution()) {
case BL::ShaderNodeBsdfAnisotropic::distribution_BECKMANN:
@@ -400,12 +400,12 @@ static ShaderNode *add_node(Scene *scene,
node = aniso;
}
else if (b_node.is_a(&RNA_ShaderNodeBsdfDiffuse)) {
- node = new DiffuseBsdfNode();
+ node = graph->create_node<DiffuseBsdfNode>();
}
else if (b_node.is_a(&RNA_ShaderNodeSubsurfaceScattering)) {
BL::ShaderNodeSubsurfaceScattering b_subsurface_node(b_node);
- SubsurfaceScatteringNode *subsurface = new SubsurfaceScatteringNode();
+ SubsurfaceScatteringNode *subsurface = graph->create_node<SubsurfaceScatteringNode>();
switch (b_subsurface_node.falloff()) {
case BL::ShaderNodeSubsurfaceScattering::falloff_CUBIC:
@@ -426,7 +426,7 @@ static ShaderNode *add_node(Scene *scene,
}
else if (b_node.is_a(&RNA_ShaderNodeBsdfGlossy)) {
BL::ShaderNodeBsdfGlossy b_glossy_node(b_node);
- GlossyBsdfNode *glossy = new GlossyBsdfNode();
+ GlossyBsdfNode *glossy = graph->create_node<GlossyBsdfNode>();
switch (b_glossy_node.distribution()) {
case BL::ShaderNodeBsdfGlossy::distribution_SHARP:
@@ -449,7 +449,7 @@ static ShaderNode *add_node(Scene *scene,
}
else if (b_node.is_a(&RNA_ShaderNodeBsdfGlass)) {
BL::ShaderNodeBsdfGlass b_glass_node(b_node);
- GlassBsdfNode *glass = new GlassBsdfNode();
+ GlassBsdfNode *glass = graph->create_node<GlassBsdfNode>();
switch (b_glass_node.distribution()) {
case BL::ShaderNodeBsdfGlass::distribution_SHARP:
glass->distribution = CLOSURE_BSDF_SHARP_GLASS_ID;
@@ -468,7 +468,7 @@ static ShaderNode *add_node(Scene *scene,
}
else if (b_node.is_a(&RNA_ShaderNodeBsdfRefraction)) {
BL::ShaderNodeBsdfRefraction b_refraction_node(b_node);
- RefractionBsdfNode *refraction = new RefractionBsdfNode();
+ RefractionBsdfNode *refraction = graph->create_node<RefractionBsdfNode>();
switch (b_refraction_node.distribution()) {
case BL::ShaderNodeBsdfRefraction::distribution_SHARP:
refraction->distribution = CLOSURE_BSDF_REFRACTION_ID;
@@ -484,7 +484,7 @@ static ShaderNode *add_node(Scene *scene,
}
else if (b_node.is_a(&RNA_ShaderNodeBsdfToon)) {
BL::ShaderNodeBsdfToon b_toon_node(b_node);
- ToonBsdfNode *toon = new ToonBsdfNode();
+ ToonBsdfNode *toon = graph->create_node<ToonBsdfNode>();
switch (b_toon_node.component()) {
case BL::ShaderNodeBsdfToon::component_DIFFUSE:
toon->component = CLOSURE_BSDF_DIFFUSE_TOON_ID;
@@ -497,7 +497,7 @@ static ShaderNode *add_node(Scene *scene,
}
else if (b_node.is_a(&RNA_ShaderNodeBsdfHair)) {
BL::ShaderNodeBsdfHair b_hair_node(b_node);
- HairBsdfNode *hair = new HairBsdfNode();
+ HairBsdfNode *hair = graph->create_node<HairBsdfNode>();
switch (b_hair_node.component()) {
case BL::ShaderNodeBsdfHair::component_Reflection:
hair->component = CLOSURE_BSDF_HAIR_REFLECTION_ID;
@@ -510,7 +510,7 @@ static ShaderNode *add_node(Scene *scene,
}
else if (b_node.is_a(&RNA_ShaderNodeBsdfHairPrincipled)) {
BL::ShaderNodeBsdfHairPrincipled b_principled_hair_node(b_node);
- PrincipledHairBsdfNode *principled_hair = new PrincipledHairBsdfNode();
+ PrincipledHairBsdfNode *principled_hair = graph->create_node<PrincipledHairBsdfNode>();
principled_hair->parametrization = (NodePrincipledHairParametrization)get_enum(
b_principled_hair_node.ptr,
"parametrization",
@@ -520,7 +520,7 @@ static ShaderNode *add_node(Scene *scene,
}
else if (b_node.is_a(&RNA_ShaderNodeBsdfPrincipled)) {
BL::ShaderNodeBsdfPrincipled b_principled_node(b_node);
- PrincipledBsdfNode *principled = new PrincipledBsdfNode();
+ PrincipledBsdfNode *principled = graph->create_node<PrincipledBsdfNode>();
switch (b_principled_node.distribution()) {
case BL::ShaderNodeBsdfPrincipled::distribution_GGX:
principled->distribution = CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID;
@@ -540,77 +540,77 @@ static ShaderNode *add_node(Scene *scene,
node = principled;
}
else if (b_node.is_a(&RNA_ShaderNodeBsdfTranslucent)) {
- node = new TranslucentBsdfNode();
+ node = graph->create_node<TranslucentBsdfNode>();
}
else if (b_node.is_a(&RNA_ShaderNodeBsdfTransparent)) {
- node = new TransparentBsdfNode();
+ node = graph->create_node<TransparentBsdfNode>();
}
else if (b_node.is_a(&RNA_ShaderNodeBsdfVelvet)) {
- node = new VelvetBsdfNode();
+ node = graph->create_node<VelvetBsdfNode>();
}
else if (b_node.is_a(&RNA_ShaderNodeEmission)) {
- node = new EmissionNode();
+ node = graph->create_node<EmissionNode>();
}
else if (b_node.is_a(&RNA_ShaderNodeAmbientOcclusion)) {
BL::ShaderNodeAmbientOcclusion b_ao_node(b_node);
- AmbientOcclusionNode *ao = new AmbientOcclusionNode();
+ AmbientOcclusionNode *ao = graph->create_node<AmbientOcclusionNode>();
ao->samples = b_ao_node.samples();
ao->inside = b_ao_node.inside();
ao->only_local = b_ao_node.only_local();
node = ao;
}
else if (b_node.is_a(&RNA_ShaderNodeVolumeScatter)) {
- node = new ScatterVolumeNode();
+ node = graph->create_node<ScatterVolumeNode>();
}
else if (b_node.is_a(&RNA_ShaderNodeVolumeAbsorption)) {
- node = new AbsorptionVolumeNode();
+ node = graph->create_node<AbsorptionVolumeNode>();
}
else if (b_node.is_a(&RNA_ShaderNodeVolumePrincipled)) {
- PrincipledVolumeNode *principled = new PrincipledVolumeNode();
+ PrincipledVolumeNode *principled = graph->create_node<PrincipledVolumeNode>();
node = principled;
}
else if (b_node.is_a(&RNA_ShaderNodeNewGeometry)) {
- node = new GeometryNode();
+ node = graph->create_node<GeometryNode>();
}
else if (b_node.is_a(&RNA_ShaderNodeWireframe)) {
BL::ShaderNodeWireframe b_wireframe_node(b_node);
- WireframeNode *wire = new WireframeNode();
+ WireframeNode *wire = graph->create_node<WireframeNode>();
wire->use_pixel_size = b_wireframe_node.use_pixel_size();
node = wire;
}
else if (b_node.is_a(&RNA_ShaderNodeWavelength)) {
- node = new WavelengthNode();
+ node = graph->create_node<WavelengthNode>();
}
else if (b_node.is_a(&RNA_ShaderNodeBlackbody)) {
- node = new BlackbodyNode();
+ node = graph->create_node<BlackbodyNode>();
}
else if (b_node.is_a(&RNA_ShaderNodeLightPath)) {
- node = new LightPathNode();
+ node = graph->create_node<LightPathNode>();
}
else if (b_node.is_a(&RNA_ShaderNodeLightFalloff)) {
- node = new LightFalloffNode();
+ node = graph->create_node<LightFalloffNode>();
}
else if (b_node.is_a(&RNA_ShaderNodeObjectInfo)) {
- node = new ObjectInfoNode();
+ node = graph->create_node<ObjectInfoNode>();
}
else if (b_node.is_a(&RNA_ShaderNodeParticleInfo)) {
- node = new ParticleInfoNode();
+ node = graph->create_node<ParticleInfoNode>();
}
else if (b_node.is_a(&RNA_ShaderNodeHairInfo)) {
- node = new HairInfoNode();
+ node = graph->create_node<HairInfoNode>();
}
else if (b_node.is_a(&RNA_ShaderNodeVolumeInfo)) {
- node = new VolumeInfoNode();
+ node = graph->create_node<VolumeInfoNode>();
}
else if (b_node.is_a(&RNA_ShaderNodeVertexColor)) {
BL::ShaderNodeVertexColor b_vertex_color_node(b_node);
- VertexColorNode *vertex_color_node = new VertexColorNode();
+ VertexColorNode *vertex_color_node = graph->create_node<VertexColorNode>();
vertex_color_node->layer_name = b_vertex_color_node.layer_name();
node = vertex_color_node;
}
else if (b_node.is_a(&RNA_ShaderNodeBump)) {
BL::ShaderNodeBump b_bump_node(b_node);
- BumpNode *bump = new BumpNode();
+ BumpNode *bump = graph->create_node<BumpNode>();
bump->invert = b_bump_node.invert();
node = bump;
}
@@ -624,12 +624,13 @@ static ShaderNode *add_node(Scene *scene,
string bytecode_hash = b_script_node.bytecode_hash();
if (!bytecode_hash.empty()) {
- node = OSLShaderManager::osl_node(manager, "", bytecode_hash, b_script_node.bytecode());
+ node = OSLShaderManager::osl_node(
+ graph, manager, "", bytecode_hash, b_script_node.bytecode());
}
else {
string absolute_filepath = blender_absolute_path(
b_data, b_ntree, b_script_node.filepath());
- node = OSLShaderManager::osl_node(manager, absolute_filepath, "");
+ node = OSLShaderManager::osl_node(graph, manager, absolute_filepath, "");
}
}
#else
@@ -641,7 +642,7 @@ static ShaderNode *add_node(Scene *scene,
BL::ShaderNodeTexImage b_image_node(b_node);
BL::Image b_image(b_image_node.image());
BL::ImageUser b_image_user(b_image_node.image_user());
- ImageTextureNode *image = new ImageTextureNode();
+ ImageTextureNode *image = graph->create_node<ImageTextureNode>();
image->interpolation = get_image_interpolation(b_image_node);
image->extension = get_image_extension(b_image_node);
@@ -693,7 +694,7 @@ static ShaderNode *add_node(Scene *scene,
BL::ShaderNodeTexEnvironment b_env_node(b_node);
BL::Image b_image(b_env_node.image());
BL::ImageUser b_image_user(b_env_node.image_user());
- EnvironmentTextureNode *env = new EnvironmentTextureNode();
+ EnvironmentTextureNode *env = graph->create_node<EnvironmentTextureNode>();
env->interpolation = get_image_interpolation(b_env_node);
env->projection = (NodeEnvironmentProjection)b_env_node.projection();
@@ -726,7 +727,7 @@ static ShaderNode *add_node(Scene *scene,
}
else if (b_node.is_a(&RNA_ShaderNodeTexGradient)) {
BL::ShaderNodeTexGradient b_gradient_node(b_node);
- GradientTextureNode *gradient = new GradientTextureNode();
+ GradientTextureNode *gradient = graph->create_node<GradientTextureNode>();
gradient->type = (NodeGradientType)b_gradient_node.gradient_type();
BL::TexMapping b_texture_mapping(b_gradient_node.texture_mapping());
get_tex_mapping(&gradient->tex_mapping, b_texture_mapping);
@@ -734,7 +735,7 @@ static ShaderNode *add_node(Scene *scene,
}
else if (b_node.is_a(&RNA_ShaderNodeTexVoronoi)) {
BL::ShaderNodeTexVoronoi b_voronoi_node(b_node);
- VoronoiTextureNode *voronoi = new VoronoiTextureNode();
+ VoronoiTextureNode *voronoi = graph->create_node<VoronoiTextureNode>();
voronoi->dimensions = b_voronoi_node.voronoi_dimensions();
voronoi->feature = (NodeVoronoiFeature)b_voronoi_node.feature();
voronoi->metric = (NodeVoronoiDistanceMetric)b_voronoi_node.distance();
@@ -744,7 +745,7 @@ static ShaderNode *add_node(Scene *scene,
}
else if (b_node.is_a(&RNA_ShaderNodeTexMagic)) {
BL::ShaderNodeTexMagic b_magic_node(b_node);
- MagicTextureNode *magic = new MagicTextureNode();
+ MagicTextureNode *magic = graph->create_node<MagicTextureNode>();
magic->depth = b_magic_node.turbulence_depth();
BL::TexMapping b_texture_mapping(b_magic_node.texture_mapping());
get_tex_mapping(&magic->tex_mapping, b_texture_mapping);
@@ -752,7 +753,7 @@ static ShaderNode *add_node(Scene *scene,
}
else if (b_node.is_a(&RNA_ShaderNodeTexWave)) {
BL::ShaderNodeTexWave b_wave_node(b_node);
- WaveTextureNode *wave = new WaveTextureNode();
+ WaveTextureNode *wave = graph->create_node<WaveTextureNode>();
wave->type = (NodeWaveType)b_wave_node.wave_type();
wave->bands_direction = (NodeWaveBandsDirection)b_wave_node.bands_direction();
wave->rings_direction = (NodeWaveRingsDirection)b_wave_node.rings_direction();
@@ -763,14 +764,14 @@ static ShaderNode *add_node(Scene *scene,
}
else if (b_node.is_a(&RNA_ShaderNodeTexChecker)) {
BL::ShaderNodeTexChecker b_checker_node(b_node);
- CheckerTextureNode *checker = new CheckerTextureNode();
+ CheckerTextureNode *checker = graph->create_node<CheckerTextureNode>();
BL::TexMapping b_texture_mapping(b_checker_node.texture_mapping());
get_tex_mapping(&checker->tex_mapping, b_texture_mapping);
node = checker;
}
else if (b_node.is_a(&RNA_ShaderNodeTexBrick)) {
BL::ShaderNodeTexBrick b_brick_node(b_node);
- BrickTextureNode *brick = new BrickTextureNode();
+ BrickTextureNode *brick = graph->create_node<BrickTextureNode>();
brick->offset = b_brick_node.offset();
brick->offset_frequency = b_brick_node.offset_frequency();
brick->squash = b_brick_node.squash();
@@ -781,7 +782,7 @@ static ShaderNode *add_node(Scene *scene,
}
else if (b_node.is_a(&RNA_ShaderNodeTexNoise)) {
BL::ShaderNodeTexNoise b_noise_node(b_node);
- NoiseTextureNode *noise = new NoiseTextureNode();
+ NoiseTextureNode *noise = graph->create_node<NoiseTextureNode>();
noise->dimensions = b_noise_node.noise_dimensions();
BL::TexMapping b_texture_mapping(b_noise_node.texture_mapping());
get_tex_mapping(&noise->tex_mapping, b_texture_mapping);
@@ -789,7 +790,7 @@ static ShaderNode *add_node(Scene *scene,
}
else if (b_node.is_a(&RNA_ShaderNodeTexMusgrave)) {
BL::ShaderNodeTexMusgrave b_musgrave_node(b_node);
- MusgraveTextureNode *musgrave_node = new MusgraveTextureNode();
+ MusgraveTextureNode *musgrave_node = graph->create_node<MusgraveTextureNode>();
musgrave_node->type = (NodeMusgraveType)b_musgrave_node.musgrave_type();
musgrave_node->dimensions = b_musgrave_node.musgrave_dimensions();
BL::TexMapping b_texture_mapping(b_musgrave_node.texture_mapping());
@@ -798,7 +799,7 @@ static ShaderNode *add_node(Scene *scene,
}
else if (b_node.is_a(&RNA_ShaderNodeTexCoord)) {
BL::ShaderNodeTexCoord b_tex_coord_node(b_node);
- TextureCoordinateNode *tex_coord = new TextureCoordinateNode();
+ TextureCoordinateNode *tex_coord = graph->create_node<TextureCoordinateNode>();
tex_coord->from_dupli = b_tex_coord_node.from_instancer();
if (b_tex_coord_node.object()) {
tex_coord->use_transform = true;
@@ -808,7 +809,7 @@ static ShaderNode *add_node(Scene *scene,
}
else if (b_node.is_a(&RNA_ShaderNodeTexSky)) {
BL::ShaderNodeTexSky b_sky_node(b_node);
- SkyTextureNode *sky = new SkyTextureNode();
+ SkyTextureNode *sky = graph->create_node<SkyTextureNode>();
sky->type = (NodeSkyType)b_sky_node.sky_type();
sky->sun_direction = normalize(get_float3(b_sky_node.sun_direction()));
sky->turbidity = b_sky_node.turbidity();
@@ -828,7 +829,7 @@ static ShaderNode *add_node(Scene *scene,
}
else if (b_node.is_a(&RNA_ShaderNodeTexIES)) {
BL::ShaderNodeTexIES b_ies_node(b_node);
- IESLightNode *ies = new IESLightNode();
+ IESLightNode *ies = graph->create_node<IESLightNode>();
switch (b_ies_node.mode()) {
case BL::ShaderNodeTexIES::mode_EXTERNAL:
ies->filename = blender_absolute_path(b_data, b_ntree, b_ies_node.filepath());
@@ -844,20 +845,20 @@ static ShaderNode *add_node(Scene *scene,
}
else if (b_node.is_a(&RNA_ShaderNodeTexWhiteNoise)) {
BL::ShaderNodeTexWhiteNoise b_tex_white_noise_node(b_node);
- WhiteNoiseTextureNode *white_noise_node = new WhiteNoiseTextureNode();
+ WhiteNoiseTextureNode *white_noise_node = graph->create_node<WhiteNoiseTextureNode>();
white_noise_node->dimensions = b_tex_white_noise_node.noise_dimensions();
node = white_noise_node;
}
else if (b_node.is_a(&RNA_ShaderNodeNormalMap)) {
BL::ShaderNodeNormalMap b_normal_map_node(b_node);
- NormalMapNode *nmap = new NormalMapNode();
+ NormalMapNode *nmap = graph->create_node<NormalMapNode>();
nmap->space = (NodeNormalMapSpace)b_normal_map_node.space();
nmap->attribute = b_normal_map_node.uv_map();
node = nmap;
}
else if (b_node.is_a(&RNA_ShaderNodeTangent)) {
BL::ShaderNodeTangent b_tangent_node(b_node);
- TangentNode *tangent = new TangentNode();
+ TangentNode *tangent = graph->create_node<TangentNode>();
tangent->direction_type = (NodeTangentDirectionType)b_tangent_node.direction_type();
tangent->axis = (NodeTangentAxis)b_tangent_node.axis();
tangent->attribute = b_tangent_node.uv_map();
@@ -865,14 +866,14 @@ static ShaderNode *add_node(Scene *scene,
}
else if (b_node.is_a(&RNA_ShaderNodeUVMap)) {
BL::ShaderNodeUVMap b_uvmap_node(b_node);
- UVMapNode *uvm = new UVMapNode();
+ UVMapNode *uvm = graph->create_node<UVMapNode>();
uvm->attribute = b_uvmap_node.uv_map();
uvm->from_dupli = b_uvmap_node.from_instancer();
node = uvm;
}
else if (b_node.is_a(&RNA_ShaderNodeTexPointDensity)) {
BL::ShaderNodeTexPointDensity b_point_density_node(b_node);
- PointDensityTextureNode *point_density = new PointDensityTextureNode();
+ PointDensityTextureNode *point_density = graph->create_node<PointDensityTextureNode>();
point_density->space = (NodeTexVoxelSpace)b_point_density_node.space();
point_density->interpolation = get_image_interpolation(b_point_density_node);
point_density->handle = scene->image_manager->add_image(
@@ -897,26 +898,26 @@ static ShaderNode *add_node(Scene *scene,
}
else if (b_node.is_a(&RNA_ShaderNodeBevel)) {
BL::ShaderNodeBevel b_bevel_node(b_node);
- BevelNode *bevel = new BevelNode();
+ BevelNode *bevel = graph->create_node<BevelNode>();
bevel->samples = b_bevel_node.samples();
node = bevel;
}
else if (b_node.is_a(&RNA_ShaderNodeDisplacement)) {
BL::ShaderNodeDisplacement b_disp_node(b_node);
- DisplacementNode *disp = new DisplacementNode();
+ DisplacementNode *disp = graph->create_node<DisplacementNode>();
disp->space = (NodeNormalMapSpace)b_disp_node.space();
node = disp;
}
else if (b_node.is_a(&RNA_ShaderNodeVectorDisplacement)) {
BL::ShaderNodeVectorDisplacement b_disp_node(b_node);
- VectorDisplacementNode *disp = new VectorDisplacementNode();
+ VectorDisplacementNode *disp = graph->create_node<VectorDisplacementNode>();
disp->space = (NodeNormalMapSpace)b_disp_node.space();
disp->attribute = "";
node = disp;
}
else if (b_node.is_a(&RNA_ShaderNodeOutputAOV)) {
BL::ShaderNodeOutputAOV b_aov_node(b_node);
- OutputAOVNode *aov = new OutputAOVNode();
+ OutputAOVNode *aov = graph->create_node<OutputAOVNode>();
aov->name = b_aov_node.name();
node = aov;
}
@@ -1038,7 +1039,7 @@ static void add_nodes(Scene *scene,
continue;
}
- ConvertNode *proxy = new ConvertNode(to_socket_type, to_socket_type, true);
+ ConvertNode *proxy = graph->create_node<ConvertNode>(to_socket_type, to_socket_type, true);
input_map[b_link->from_socket().ptr.data] = proxy->inputs[0];
output_map[b_link->to_socket().ptr.data] = proxy->outputs[0];
@@ -1069,7 +1070,7 @@ static void add_nodes(Scene *scene,
continue;
}
- ConvertNode *proxy = new ConvertNode(input_type, input_type, true);
+ ConvertNode *proxy = graph->create_node<ConvertNode>(input_type, input_type, true);
graph->add(proxy);
/* register the proxy node for internal binding */
@@ -1085,7 +1086,7 @@ static void add_nodes(Scene *scene,
continue;
}
- ConvertNode *proxy = new ConvertNode(output_type, output_type, true);
+ ConvertNode *proxy = graph->create_node<ConvertNode>(output_type, output_type, true);
graph->add(proxy);
/* register the proxy node for internal binding */
@@ -1240,7 +1241,7 @@ void BlenderSync::sync_materials(BL::Depsgraph &b_depsgraph, bool update_all)
Shader *shader;
/* test if we need to sync */
- if (shader_map.add_or_update(&shader, b_mat) || update_all) {
+ if (shader_map.add_or_update(scene, &shader, b_mat) || update_all) {
ShaderGraph *graph = new ShaderGraph();
shader->name = b_mat.name().c_str();
@@ -1253,7 +1254,7 @@ void BlenderSync::sync_materials(BL::Depsgraph &b_depsgraph, bool update_all)
add_nodes(scene, b_engine, b_data, b_depsgraph, b_scene, graph, b_ntree);
}
else {
- DiffuseBsdfNode *diffuse = new DiffuseBsdfNode();
+ DiffuseBsdfNode *diffuse = graph->create_node<DiffuseBsdfNode>();
diffuse->color = get_float3(b_mat.diffuse_color());
graph->add(diffuse);
@@ -1336,7 +1337,7 @@ void BlenderSync::sync_world(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d,
shader->volume_step_rate = get_float(cworld, "volume_step_size");
}
else if (new_viewport_parameters.use_scene_world && b_world) {
- BackgroundNode *background = new BackgroundNode();
+ BackgroundNode *background = graph->create_node<BackgroundNode>();
background->color = get_float3(b_world.color());
graph->add(background);
@@ -1352,23 +1353,23 @@ void BlenderSync::sync_world(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d,
world_color = make_float3(0.0f, 0.0f, 0.0f);
}
- BackgroundNode *background = new BackgroundNode();
+ BackgroundNode *background = graph->create_node<BackgroundNode>();
graph->add(background);
- LightPathNode *light_path = new LightPathNode();
+ LightPathNode *light_path = graph->create_node<LightPathNode>();
graph->add(light_path);
- MixNode *mix_scene_with_background = new MixNode();
+ MixNode *mix_scene_with_background = graph->create_node<MixNode>();
mix_scene_with_background->color2 = world_color;
graph->add(mix_scene_with_background);
- EnvironmentTextureNode *texture_environment = new EnvironmentTextureNode();
+ EnvironmentTextureNode *texture_environment = graph->create_node<EnvironmentTextureNode>();
texture_environment->tex_mapping.type = TextureMapping::VECTOR;
texture_environment->tex_mapping.rotation[2] = new_viewport_parameters.studiolight_rotate_z;
texture_environment->filename = new_viewport_parameters.studiolight_path;
graph->add(texture_environment);
- MixNode *mix_intensity = new MixNode();
+ MixNode *mix_intensity = graph->create_node<MixNode>();
mix_intensity->type = NODE_MIX_MUL;
mix_intensity->fac = 1.0f;
mix_intensity->color2 = make_float3(new_viewport_parameters.studiolight_intensity,
@@ -1376,10 +1377,10 @@ void BlenderSync::sync_world(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d,
new_viewport_parameters.studiolight_intensity);
graph->add(mix_intensity);
- TextureCoordinateNode *texture_coordinate = new TextureCoordinateNode();
+ TextureCoordinateNode *texture_coordinate = graph->create_node<TextureCoordinateNode>();
graph->add(texture_coordinate);
- MixNode *mix_background_with_environment = new MixNode();
+ MixNode *mix_background_with_environment = graph->create_node<MixNode>();
mix_background_with_environment->fac = new_viewport_parameters.studiolight_background_alpha;
mix_background_with_environment->color1 = world_color;
graph->add(mix_background_with_environment);
@@ -1466,7 +1467,7 @@ void BlenderSync::sync_lights(BL::Depsgraph &b_depsgraph, bool update_all)
Shader *shader;
/* test if we need to sync */
- if (shader_map.add_or_update(&shader, b_light) || update_all) {
+ if (shader_map.add_or_update(scene, &shader, b_light) || update_all) {
ShaderGraph *graph = new ShaderGraph();
/* create nodes */
@@ -1478,7 +1479,7 @@ void BlenderSync::sync_lights(BL::Depsgraph &b_depsgraph, bool update_all)
add_nodes(scene, b_engine, b_data, b_depsgraph, b_scene, graph, b_ntree);
}
else {
- EmissionNode *emission = new EmissionNode();
+ EmissionNode *emission = graph->create_node<EmissionNode>();
emission->color = make_float3(1.0f, 1.0f, 1.0f);
emission->strength = 1.0f;
graph->add(emission);
diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp
index 511061db08a..0126e5c8874 100644
--- a/intern/cycles/blender/blender_sync.cpp
+++ b/intern/cycles/blender/blender_sync.cpp
@@ -55,11 +55,11 @@ BlenderSync::BlenderSync(BL::RenderEngine &b_engine,
: b_engine(b_engine),
b_data(b_data),
b_scene(b_scene),
- shader_map(&scene->shaders),
- object_map(&scene->objects),
- geometry_map(&scene->geometry),
- light_map(&scene->lights),
- particle_system_map(&scene->particle_systems),
+ shader_map(),
+ object_map(),
+ geometry_map(),
+ light_map(),
+ particle_system_map(),
world_map(NULL),
world_recalc(false),
scene(scene),
@@ -239,7 +239,7 @@ void BlenderSync::sync_data(BL::RenderSettings &b_render,
/* Shader sync done at the end, since object sync uses it.
* false = don't delete unused shaders, not supported. */
- shader_map.post_sync(false);
+ shader_map.post_sync(scene, false);
free_data_after_sync(b_depsgraph);
}
@@ -372,8 +372,10 @@ void BlenderSync::sync_film(BL::SpaceView3D &b_v3d)
Film *film = scene->film;
Film prevfilm = *film;
+ vector<Pass> prevpasses = scene->passes;
+
if (b_v3d) {
- film->display_pass = update_viewport_display_passes(b_v3d, film->passes);
+ film->display_pass = update_viewport_display_passes(b_v3d, scene->passes);
}
film->exposure = get_float(cscene, "film_exposure");
@@ -403,7 +405,11 @@ void BlenderSync::sync_film(BL::SpaceView3D &b_v3d)
if (film->modified(prevfilm)) {
film->tag_update(scene);
- film->tag_passes_update(scene, prevfilm.passes, false);
+ }
+
+ if (!Pass::equals(prevpasses, scene->passes)) {
+ film->tag_passes_update(scene, prevpasses, false);
+ film->tag_update(scene);
}
}
diff --git a/intern/cycles/blender/blender_sync.h b/intern/cycles/blender/blender_sync.h
index a551ec31e04..62fd1ac2351 100644
--- a/intern/cycles/blender/blender_sync.h
+++ b/intern/cycles/blender/blender_sync.h
@@ -148,7 +148,7 @@ class BlenderSync {
bool *use_portal);
/* Volume */
- void sync_volume(BL::Object &b_ob, Mesh *mesh, const vector<Shader *> &used_shaders);
+ void sync_volume(BL::Object &b_ob, Volume *volume, const vector<Shader *> &used_shaders);
/* Mesh */
void sync_mesh(BL::Depsgraph b_depsgraph,
diff --git a/intern/cycles/blender/blender_volume.cpp b/intern/cycles/blender/blender_volume.cpp
index d0e1e4d6131..e039d8a4895 100644
--- a/intern/cycles/blender/blender_volume.cpp
+++ b/intern/cycles/blender/blender_volume.cpp
@@ -17,8 +17,8 @@
#include "render/colorspace.h"
#include "render/image.h"
#include "render/image_vdb.h"
-#include "render/mesh.h"
#include "render/object.h"
+#include "render/volume.h"
#include "blender/blender_sync.h"
#include "blender/blender_util.h"
@@ -181,7 +181,7 @@ class BlenderSmokeLoader : public ImageLoader {
AttributeStandard attribute;
};
-static void sync_smoke_volume(Scene *scene, BL::Object &b_ob, Mesh *mesh, float frame)
+static void sync_smoke_volume(Scene *scene, BL::Object &b_ob, Volume *volume, float frame)
{
BL::FluidDomainSettings b_domain = object_fluid_gas_domain_find(b_ob);
if (!b_domain) {
@@ -198,13 +198,13 @@ static void sync_smoke_volume(Scene *scene, BL::Object &b_ob, Mesh *mesh, float
for (int i = 0; attributes[i] != ATTR_STD_NONE; i++) {
AttributeStandard std = attributes[i];
- if (!mesh->need_attribute(scene, std)) {
+ if (!volume->need_attribute(scene, std)) {
continue;
}
- mesh->volume_clipping = b_domain.clipping();
+ volume->clipping = b_domain.clipping();
- Attribute *attr = mesh->attributes.add(std);
+ Attribute *attr = volume->attributes.add(std);
ImageLoader *loader = new BlenderSmokeLoader(b_ob, std);
ImageParams params;
@@ -228,7 +228,7 @@ class BlenderVolumeLoader : public VDBImageLoader {
if (b_volume_grid.name() == grid_name) {
const bool unload = !b_volume_grid.is_loaded();
- Volume *volume = (Volume *)b_volume.ptr.data;
+ ::Volume *volume = (::Volume *)b_volume.ptr.data;
VolumeGrid *volume_grid = (VolumeGrid *)b_volume_grid.ptr.data;
grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid);
@@ -252,16 +252,19 @@ class BlenderVolumeLoader : public VDBImageLoader {
BL::Volume b_volume;
};
-static void sync_volume_object(BL::BlendData &b_data, BL::Object &b_ob, Scene *scene, Mesh *mesh)
+static void sync_volume_object(BL::BlendData &b_data,
+ BL::Object &b_ob,
+ Scene *scene,
+ Volume *volume)
{
BL::Volume b_volume(b_ob.data());
b_volume.grids.load(b_data.ptr.data);
BL::VolumeRender b_render(b_volume.render());
- mesh->volume_clipping = b_render.clipping();
- mesh->volume_step_size = b_render.step_size();
- mesh->volume_object_space = (b_render.space() == BL::VolumeRender::space_OBJECT);
+ volume->clipping = b_render.clipping();
+ volume->step_size = b_render.step_size();
+ volume->object_space = (b_render.space() == BL::VolumeRender::space_OBJECT);
/* Find grid with matching name. */
BL::Volume::grids_iterator b_grid_iter;
@@ -289,11 +292,11 @@ static void sync_volume_object(BL::BlendData &b_data, BL::Object &b_ob, Scene *s
std = ATTR_STD_VOLUME_VELOCITY;
}
- if ((std != ATTR_STD_NONE && mesh->need_attribute(scene, std)) ||
- mesh->need_attribute(scene, name)) {
+ if ((std != ATTR_STD_NONE && volume->need_attribute(scene, std)) ||
+ volume->need_attribute(scene, name)) {
Attribute *attr = (std != ATTR_STD_NONE) ?
- mesh->attributes.add(std) :
- mesh->attributes.add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_VOXEL);
+ volume->attributes.add(std) :
+ volume->attributes.add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_VOXEL);
ImageLoader *loader = new BlenderVolumeLoader(b_data, b_volume, name.string());
ImageParams params;
@@ -317,28 +320,30 @@ static vector<int> get_voxel_image_slots(Mesh *mesh)
return slots;
}
-void BlenderSync::sync_volume(BL::Object &b_ob, Mesh *mesh, const vector<Shader *> &used_shaders)
+void BlenderSync::sync_volume(BL::Object &b_ob,
+ Volume *volume,
+ const vector<Shader *> &used_shaders)
{
- vector<int> old_voxel_slots = get_voxel_image_slots(mesh);
+ vector<int> old_voxel_slots = get_voxel_image_slots(volume);
- mesh->clear();
- mesh->used_shaders = used_shaders;
+ volume->clear();
+ volume->used_shaders = used_shaders;
if (view_layer.use_volumes) {
if (b_ob.type() == BL::Object::type_VOLUME) {
/* Volume object. Create only attributes, bounding mesh will then
* be automatically generated later. */
- sync_volume_object(b_data, b_ob, scene, mesh);
+ sync_volume_object(b_data, b_ob, scene, volume);
}
else {
/* Smoke domain. */
- sync_smoke_volume(scene, b_ob, mesh, b_scene.frame_current());
+ sync_smoke_volume(scene, b_ob, volume, b_scene.frame_current());
}
}
/* Tag update. */
- bool rebuild = (old_voxel_slots != get_voxel_image_slots(mesh));
- mesh->tag_update(scene, rebuild);
+ bool rebuild = (old_voxel_slots != get_voxel_image_slots(volume));
+ volume->tag_update(scene, rebuild);
}
CCL_NAMESPACE_END
diff --git a/intern/cycles/bvh/bvh_build.cpp b/intern/cycles/bvh/bvh_build.cpp
index 86ab7b00815..360cac59e9b 100644
--- a/intern/cycles/bvh/bvh_build.cpp
+++ b/intern/cycles/bvh/bvh_build.cpp
@@ -270,7 +270,7 @@ void BVHBuild::add_reference_curves(BoundBox &root, BoundBox &center, Hair *hair
void BVHBuild::add_reference_geometry(BoundBox &root, BoundBox &center, Geometry *geom, int i)
{
- if (geom->type == Geometry::MESH) {
+ if (geom->type == Geometry::MESH || geom->type == Geometry::VOLUME) {
Mesh *mesh = static_cast<Mesh *>(geom);
add_reference_triangles(root, center, mesh, i);
}
@@ -299,7 +299,7 @@ static size_t count_curve_segments(Hair *hair)
static size_t count_primitives(Geometry *geom)
{
- if (geom->type == Geometry::MESH) {
+ if (geom->type == Geometry::MESH || geom->type == Geometry::VOLUME) {
Mesh *mesh = static_cast<Mesh *>(geom);
return mesh->num_triangles();
}
diff --git a/intern/cycles/bvh/bvh_embree.cpp b/intern/cycles/bvh/bvh_embree.cpp
index 17e1f86a589..d5c442516c7 100644
--- a/intern/cycles/bvh/bvh_embree.cpp
+++ b/intern/cycles/bvh/bvh_embree.cpp
@@ -126,9 +126,13 @@ static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args)
}
else {
kernel_embree_convert_hit(kg, ray, hit, &current_isect);
- if (ctx->local_object_id != current_isect.object) {
+ int object = (current_isect.object == OBJECT_NONE) ?
+ kernel_tex_fetch(__prim_object, current_isect.prim) :
+ current_isect.object;
+ if (ctx->local_object_id != object) {
/* This tells Embree to continue tracing. */
*args->valid = 0;
+ break;
}
}
@@ -302,7 +306,7 @@ thread_mutex BVHEmbree::rtc_shared_mutex;
static size_t count_primitives(Geometry *geom)
{
- if (geom->type == Geometry::MESH) {
+ if (geom->type == Geometry::MESH || geom->type == Geometry::VOLUME) {
Mesh *mesh = static_cast<Mesh *>(geom);
return mesh->num_triangles();
}
@@ -527,7 +531,7 @@ void BVHEmbree::add_object(Object *ob, int i)
{
Geometry *geom = ob->geometry;
- if (geom->type == Geometry::MESH) {
+ if (geom->type == Geometry::MESH || geom->type == Geometry::VOLUME) {
Mesh *mesh = static_cast<Mesh *>(geom);
if (mesh->num_triangles() > 0) {
add_triangles(ob, mesh, i);
@@ -975,7 +979,7 @@ void BVHEmbree::refit_nodes()
if (!params.top_level || (ob->is_traceable() && !ob->geometry->is_instanced())) {
Geometry *geom = ob->geometry;
- if (geom->type == Geometry::MESH) {
+ if (geom->type == Geometry::MESH || geom->type == Geometry::VOLUME) {
Mesh *mesh = static_cast<Mesh *>(geom);
if (mesh->num_triangles() > 0) {
update_tri_vertex_buffer(rtcGetGeometry(scene, geom_id), mesh);
diff --git a/intern/cycles/bvh/bvh_optix.cpp b/intern/cycles/bvh/bvh_optix.cpp
index ccb7ae08625..0527c0eeda8 100644
--- a/intern/cycles/bvh/bvh_optix.cpp
+++ b/intern/cycles/bvh/bvh_optix.cpp
@@ -95,7 +95,7 @@ void BVHOptiX::pack_blas()
}
}
}
- else if (geom->type == Geometry::MESH) {
+ else if (geom->type == Geometry::MESH || geom->type == Geometry::VOLUME) {
Mesh *const mesh = static_cast<Mesh *const>(geom);
if (mesh->num_triangles() > 0) {
const size_t num_triangles = mesh->num_triangles();
diff --git a/intern/cycles/bvh/bvh_split.cpp b/intern/cycles/bvh/bvh_split.cpp
index 4b21f852d7a..2f1960d664e 100644
--- a/intern/cycles/bvh/bvh_split.cpp
+++ b/intern/cycles/bvh/bvh_split.cpp
@@ -458,7 +458,7 @@ void BVHSpatialSplit::split_object_reference(
{
Geometry *geom = object->geometry;
- if (geom->type == Geometry::MESH) {
+ if (geom->type == Geometry::MESH || geom->type == Geometry::VOLUME) {
Mesh *mesh = static_cast<Mesh *>(geom);
for (int tri_idx = 0; tri_idx < mesh->num_triangles(); ++tri_idx) {
split_triangle_primitive(mesh, &object->tfm, tri_idx, dim, pos, left_bounds, right_bounds);
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/device.h b/intern/cycles/device/device.h
index 115b05e3911..58472f645e0 100644
--- a/intern/cycles/device/device.h
+++ b/intern/cycles/device/device.h
@@ -180,7 +180,6 @@ class DeviceRequestedFeatures {
DeviceRequestedFeatures()
{
/* TODO(sergey): Find more meaningful defaults. */
- experimental = false;
max_nodes_group = 0;
nodes_features = 0;
use_hair = false;
@@ -203,8 +202,7 @@ class DeviceRequestedFeatures {
bool modified(const DeviceRequestedFeatures &requested_features)
{
- return !(experimental == requested_features.experimental &&
- max_nodes_group == requested_features.max_nodes_group &&
+ return !(max_nodes_group == requested_features.max_nodes_group &&
nodes_features == requested_features.nodes_features &&
use_hair == requested_features.use_hair &&
use_hair_thick == requested_features.use_hair_thick &&
diff --git a/intern/cycles/device/device_optix.cpp b/intern/cycles/device/device_optix.cpp
index 1cc45983565..43b1fb30baf 100644
--- a/intern/cycles/device/device_optix.cpp
+++ b/intern/cycles/device/device_optix.cpp
@@ -1389,7 +1389,7 @@ class OptiXDevice : public CUDADevice {
return false;
}
}
- else if (geom->type == Geometry::MESH) {
+ else if (geom->type == Geometry::MESH || geom->type == Geometry::VOLUME) {
// Build BLAS for triangle primitives
Mesh *const mesh = static_cast<Mesh *const>(ob->geometry);
if (mesh->num_triangles() == 0) {
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/graph/node.cpp b/intern/cycles/graph/node.cpp
index c437c6fda1e..37387053d97 100644
--- a/intern/cycles/graph/node.cpp
+++ b/intern/cycles/graph/node.cpp
@@ -26,10 +26,16 @@ CCL_NAMESPACE_BEGIN
/* Node Type */
+NodeOwner::~NodeOwner()
+{
+}
+
Node::Node(const NodeType *type_, ustring name_) : name(name_), type(type_)
{
assert(type);
+ owner = nullptr;
+
/* assign non-empty name, convenient for debugging */
if (name.empty()) {
name = type->name;
@@ -679,4 +685,15 @@ bool Node::is_a(const NodeType *type_)
return false;
}
+const NodeOwner *Node::get_owner() const
+{
+ return owner;
+}
+
+void Node::set_owner(const NodeOwner *owner_)
+{
+ assert(owner_);
+ owner = owner_;
+}
+
CCL_NAMESPACE_END
diff --git a/intern/cycles/graph/node.h b/intern/cycles/graph/node.h
index 4473b8aca28..3c84dbdb4a7 100644
--- a/intern/cycles/graph/node.h
+++ b/intern/cycles/graph/node.h
@@ -31,6 +31,10 @@ struct Transform;
/* Node */
+struct NodeOwner {
+ virtual ~NodeOwner();
+};
+
struct Node {
explicit Node(const NodeType *type, ustring name = ustring());
virtual ~Node() = 0;
@@ -99,6 +103,12 @@ struct Node {
ustring name;
const NodeType *type;
+
+ const NodeOwner *get_owner() const;
+ void set_owner(const NodeOwner *owner_);
+
+ protected:
+ const NodeOwner *owner;
};
CCL_NAMESPACE_END
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/kernel/svm/svm_sky.h b/intern/cycles/kernel/svm/svm_sky.h
index f824184c1d4..b908732f026 100644
--- a/intern/cycles/kernel/svm/svm_sky.h
+++ b/intern/cycles/kernel/svm/svm_sky.h
@@ -137,7 +137,7 @@ ccl_device float3 sky_radiance_nishita(KernelGlobals *kg,
float sun_rotation = nishita_data[7];
float angular_diameter = nishita_data[8];
float sun_intensity = nishita_data[9];
- bool sun_disc = (angular_diameter > 0.0f);
+ bool sun_disc = (angular_diameter >= 0.0f);
float3 xyz;
/* convert dir to spherical coordinates */
float2 direction = direction_to_spherical(dir);
diff --git a/intern/cycles/render/CMakeLists.txt b/intern/cycles/render/CMakeLists.txt
index 6a1335dc5dd..46d9b503c03 100644
--- a/intern/cycles/render/CMakeLists.txt
+++ b/intern/cycles/render/CMakeLists.txt
@@ -34,7 +34,6 @@ set(SRC
mesh.cpp
mesh_displace.cpp
mesh_subdivision.cpp
- mesh_volume.cpp
nodes.cpp
object.cpp
osl.cpp
@@ -48,6 +47,7 @@ set(SRC
svm.cpp
tables.cpp
tile.cpp
+ volume.cpp
)
set(SRC_HEADERS
@@ -86,6 +86,7 @@ set(SRC_HEADERS
svm.h
tables.h
tile.h
+ volume.h
)
set(LIB
diff --git a/intern/cycles/render/attribute.cpp b/intern/cycles/render/attribute.cpp
index 4c26d5e8365..cdef1036647 100644
--- a/intern/cycles/render/attribute.cpp
+++ b/intern/cycles/render/attribute.cpp
@@ -167,7 +167,7 @@ size_t Attribute::element_size(Geometry *geom, AttributePrimitive prim) const
size = 1;
break;
case ATTR_ELEMENT_VERTEX:
- if (geom->type == Geometry::MESH) {
+ if (geom->type == Geometry::MESH || geom->type == Geometry::VOLUME) {
Mesh *mesh = static_cast<Mesh *>(geom);
size = mesh->verts.size() + mesh->num_ngons;
if (prim == ATTR_PRIM_SUBD) {
@@ -185,7 +185,7 @@ size_t Attribute::element_size(Geometry *geom, AttributePrimitive prim) const
}
break;
case ATTR_ELEMENT_FACE:
- if (geom->type == Geometry::MESH) {
+ if (geom->type == Geometry::MESH || geom->type == Geometry::VOLUME) {
Mesh *mesh = static_cast<Mesh *>(geom);
if (prim == ATTR_PRIM_GEOMETRY) {
size = mesh->num_triangles();
@@ -485,6 +485,25 @@ Attribute *AttributeSet::add(AttributeStandard std, ustring name)
case ATTR_STD_GENERATED_TRANSFORM:
attr = add(name, TypeDesc::TypeMatrix, ATTR_ELEMENT_MESH);
break;
+ case ATTR_STD_POINTINESS:
+ attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_VERTEX);
+ break;
+ case ATTR_STD_RANDOM_PER_ISLAND:
+ attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_FACE);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ }
+ else if (geometry->type == Geometry::VOLUME) {
+ switch (std) {
+ case ATTR_STD_VERTEX_NORMAL:
+ attr = add(name, TypeDesc::TypeNormal, ATTR_ELEMENT_VERTEX);
+ break;
+ case ATTR_STD_FACE_NORMAL:
+ attr = add(name, TypeDesc::TypeNormal, ATTR_ELEMENT_FACE);
+ break;
case ATTR_STD_VOLUME_DENSITY:
case ATTR_STD_VOLUME_FLAME:
case ATTR_STD_VOLUME_HEAT:
@@ -497,12 +516,6 @@ Attribute *AttributeSet::add(AttributeStandard std, ustring name)
case ATTR_STD_VOLUME_VELOCITY:
attr = add(name, TypeDesc::TypeVector, ATTR_ELEMENT_VOXEL);
break;
- case ATTR_STD_POINTINESS:
- attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_VERTEX);
- break;
- case ATTR_STD_RANDOM_PER_ISLAND:
- attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_FACE);
- break;
default:
assert(0);
break;
diff --git a/intern/cycles/render/bake.cpp b/intern/cycles/render/bake.cpp
index 6044182a51a..c00451e931f 100644
--- a/intern/cycles/render/bake.cpp
+++ b/intern/cycles/render/bake.cpp
@@ -97,17 +97,17 @@ void BakeManager::set(Scene *scene,
type = type_;
pass_filter = shader_type_to_pass_filter(type_, pass_filter_);
- Pass::add(PASS_BAKE_PRIMITIVE, scene->film->passes);
- Pass::add(PASS_BAKE_DIFFERENTIAL, scene->film->passes);
+ Pass::add(PASS_BAKE_PRIMITIVE, scene->passes);
+ Pass::add(PASS_BAKE_DIFFERENTIAL, scene->passes);
if (type == SHADER_EVAL_UV) {
/* force UV to be available */
- Pass::add(PASS_UV, scene->film->passes);
+ Pass::add(PASS_UV, scene->passes);
}
/* force use_light_pass to be true if we bake more than just colors */
if (pass_filter & ~BAKE_FILTER_COLOR) {
- Pass::add(PASS_LIGHT, scene->film->passes);
+ Pass::add(PASS_LIGHT, scene->passes);
}
/* create device and update scene */
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 d7cbf4a3581..2da28222a7f 100644
--- a/intern/cycles/render/film.cpp
+++ b/intern/cycles/render/film.cpp
@@ -38,6 +38,60 @@ static bool compare_pass_order(const Pass &a, const Pass &b)
return (a.components > b.components);
}
+NODE_DEFINE(Pass)
+{
+ NodeType *type = NodeType::add("pass", create);
+
+ static NodeEnum pass_type_enum;
+ pass_type_enum.insert("combined", PASS_COMBINED);
+ pass_type_enum.insert("depth", PASS_DEPTH);
+ pass_type_enum.insert("normal", PASS_NORMAL);
+ pass_type_enum.insert("uv", PASS_UV);
+ pass_type_enum.insert("object_id", PASS_OBJECT_ID);
+ pass_type_enum.insert("material_id", PASS_MATERIAL_ID);
+ pass_type_enum.insert("motion", PASS_MOTION);
+ pass_type_enum.insert("motion_weight", PASS_MOTION_WEIGHT);
+#ifdef __KERNEL_DEBUG__
+ pass_type_enum.insert("traversed_nodes", PASS_BVH_TRAVERSED_NODES);
+ pass_type_enum.insert("traverse_instances", PASS_BVH_TRAVERSED_INSTANCES);
+ pass_type_enum.insert("bvh_intersections", PASS_BVH_INTERSECTIONS);
+ pass_type_enum.insert("ray_bounces", PASS_RAY_BOUNCES);
+#endif
+ pass_type_enum.insert("render_time", PASS_RENDER_TIME);
+ pass_type_enum.insert("cryptomatte", PASS_CRYPTOMATTE);
+ pass_type_enum.insert("aov_color", PASS_AOV_COLOR);
+ pass_type_enum.insert("aov_value", PASS_AOV_VALUE);
+ pass_type_enum.insert("adaptive_aux_buffer", PASS_ADAPTIVE_AUX_BUFFER);
+ pass_type_enum.insert("sample_count", PASS_SAMPLE_COUNT);
+ pass_type_enum.insert("mist", PASS_MIST);
+ pass_type_enum.insert("emission", PASS_EMISSION);
+ pass_type_enum.insert("background", PASS_BACKGROUND);
+ pass_type_enum.insert("ambient_occlusion", PASS_AO);
+ pass_type_enum.insert("shadow", PASS_SHADOW);
+ pass_type_enum.insert("diffuse_direct", PASS_DIFFUSE_DIRECT);
+ pass_type_enum.insert("diffuse_indirect", PASS_DIFFUSE_INDIRECT);
+ pass_type_enum.insert("diffuse_color", PASS_DIFFUSE_COLOR);
+ pass_type_enum.insert("glossy_direct", PASS_GLOSSY_DIRECT);
+ pass_type_enum.insert("glossy_indirect", PASS_GLOSSY_INDIRECT);
+ pass_type_enum.insert("glossy_color", PASS_GLOSSY_COLOR);
+ pass_type_enum.insert("transmission_direct", PASS_TRANSMISSION_DIRECT);
+ pass_type_enum.insert("transmission_indirect", PASS_TRANSMISSION_INDIRECT);
+ pass_type_enum.insert("transmission_color", PASS_TRANSMISSION_COLOR);
+ pass_type_enum.insert("volume_direct", PASS_VOLUME_DIRECT);
+ pass_type_enum.insert("volume_indirect", PASS_VOLUME_INDIRECT);
+ pass_type_enum.insert("bake_primitive", PASS_BAKE_PRIMITIVE);
+ pass_type_enum.insert("bake_differential", PASS_BAKE_DIFFERENTIAL);
+
+ SOCKET_ENUM(type, "Type", pass_type_enum, PASS_COMBINED);
+ SOCKET_STRING(name, "Name", ustring());
+
+ return type;
+}
+
+Pass::Pass() : Node(node_type)
+{
+}
+
void Pass::add(PassType type, vector<Pass> &passes, const char *name)
{
for (size_t i = 0; i < passes.size(); i++) {
@@ -199,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);
@@ -330,8 +386,6 @@ NODE_DEFINE(Film)
Film::Film() : Node(node_type)
{
- Pass::add(PASS_COMBINED, passes);
-
use_light_visibility = false;
filter_table_offset = TABLE_OFFSET_INVALID;
cryptomatte_passes = CRYPT_NONE;
@@ -344,6 +398,11 @@ Film::~Film()
{
}
+void Film::add_default(Scene *scene)
+{
+ Pass::add(PASS_COMBINED, scene->passes);
+}
+
void Film::device_update(Device *device, DeviceScene *dscene, Scene *scene)
{
if (!need_update)
@@ -371,8 +430,8 @@ void Film::device_update(Device *device, DeviceScene *dscene, Scene *scene)
bool have_cryptomatte = false;
- for (size_t i = 0; i < passes.size(); i++) {
- Pass &pass = passes[i];
+ for (size_t i = 0; i < scene->passes.size(); i++) {
+ Pass &pass = scene->passes[i];
if (pass.type == PASS_NONE) {
continue;
@@ -601,26 +660,26 @@ void Film::device_free(Device * /*device*/, DeviceScene * /*dscene*/, Scene *sce
bool Film::modified(const Film &film)
{
- return !Node::equals(film) || !Pass::equals(passes, film.passes);
+ return !Node::equals(film);
}
void Film::tag_passes_update(Scene *scene, const vector<Pass> &passes_, bool update_passes)
{
- if (Pass::contains(passes, PASS_UV) != Pass::contains(passes_, PASS_UV)) {
+ if (Pass::contains(scene->passes, PASS_UV) != Pass::contains(passes_, PASS_UV)) {
scene->geometry_manager->tag_update(scene);
foreach (Shader *shader, scene->shaders)
shader->need_update_geometry = true;
}
- else if (Pass::contains(passes, PASS_MOTION) != Pass::contains(passes_, PASS_MOTION)) {
+ else if (Pass::contains(scene->passes, PASS_MOTION) != Pass::contains(passes_, PASS_MOTION)) {
scene->geometry_manager->tag_update(scene);
}
- else if (Pass::contains(passes, PASS_AO) != Pass::contains(passes_, PASS_AO)) {
+ else if (Pass::contains(scene->passes, PASS_AO) != Pass::contains(passes_, PASS_AO)) {
scene->integrator->tag_update(scene);
}
if (update_passes) {
- passes = passes_;
+ scene->passes = passes_;
}
}
@@ -629,10 +688,10 @@ void Film::tag_update(Scene * /*scene*/)
need_update = true;
}
-int Film::get_aov_offset(string name, bool &is_color)
+int Film::get_aov_offset(Scene *scene, string name, bool &is_color)
{
int num_color = 0, num_value = 0;
- foreach (const Pass &pass, passes) {
+ foreach (const Pass &pass, scene->passes) {
if (pass.type == PASS_AOV_COLOR) {
num_color++;
}
diff --git a/intern/cycles/render/film.h b/intern/cycles/render/film.h
index aae8fb404b0..961058c008e 100644
--- a/intern/cycles/render/film.h
+++ b/intern/cycles/render/film.h
@@ -38,14 +38,18 @@ typedef enum FilterType {
FILTER_NUM_TYPES,
} FilterType;
-class Pass {
+class Pass : public Node {
public:
+ NODE_DECLARE
+
+ Pass();
+
PassType type;
int components;
bool filter;
bool exposure;
PassType divide_type;
- string name;
+ ustring name;
static void add(PassType type, vector<Pass> &passes, const char *name = NULL);
static bool equals(const vector<Pass> &A, const vector<Pass> &B);
@@ -57,7 +61,6 @@ class Film : public Node {
NODE_DECLARE
float exposure;
- vector<Pass> passes;
bool denoising_data_pass;
bool denoising_clean_pass;
bool denoising_prefiltered_pass;
@@ -88,6 +91,9 @@ class Film : public Node {
Film();
~Film();
+ /* add default passes to scene */
+ static void add_default(Scene *scene);
+
void device_update(Device *device, DeviceScene *dscene, Scene *scene);
void device_free(Device *device, DeviceScene *dscene, Scene *scene);
@@ -95,7 +101,7 @@ class Film : public Node {
void tag_passes_update(Scene *scene, const vector<Pass> &passes_, bool update_passes = true);
void tag_update(Scene *scene);
- int get_aov_offset(string name, bool &is_color);
+ int get_aov_offset(Scene *scene, string name, bool &is_color);
};
CCL_NAMESPACE_END
diff --git a/intern/cycles/render/geometry.cpp b/intern/cycles/render/geometry.cpp
index 145b1fa492c..71540dc9c7b 100644
--- a/intern/cycles/render/geometry.cpp
+++ b/intern/cycles/render/geometry.cpp
@@ -31,6 +31,7 @@
#include "render/scene.h"
#include "render/shader.h"
#include "render/stats.h"
+#include "render/volume.h"
#include "subd/subd_patch_table.h"
#include "subd/subd_split.h"
@@ -796,7 +797,7 @@ void GeometryManager::mesh_calc_offset(Scene *scene)
size_t optix_prim_size = 0;
foreach (Geometry *geom, scene->geometry) {
- if (geom->type == Geometry::MESH) {
+ if (geom->type == Geometry::MESH || geom->type == Geometry::VOLUME) {
Mesh *mesh = static_cast<Mesh *>(geom);
mesh->vert_offset = vert_size;
@@ -854,7 +855,7 @@ void GeometryManager::device_update_mesh(
size_t patch_size = 0;
foreach (Geometry *geom, scene->geometry) {
- if (geom->type == Geometry::MESH) {
+ if (geom->type == Geometry::MESH || geom->type == Geometry::VOLUME) {
Mesh *mesh = static_cast<Mesh *>(geom);
vert_size += mesh->verts.size();
@@ -888,7 +889,7 @@ void GeometryManager::device_update_mesh(
* really use same semantic of arrays.
*/
foreach (Geometry *geom, scene->geometry) {
- if (geom->type == Geometry::MESH) {
+ if (geom->type == Geometry::MESH || geom->type == Geometry::VOLUME) {
Mesh *mesh = static_cast<Mesh *>(geom);
for (size_t i = 0; i < mesh->num_triangles(); ++i) {
tri_prim_index[i + mesh->prim_offset] = 3 * (i + mesh->prim_offset);
@@ -916,7 +917,7 @@ void GeometryManager::device_update_mesh(
float2 *tri_patch_uv = dscene->tri_patch_uv.alloc(vert_size);
foreach (Geometry *geom, scene->geometry) {
- if (geom->type == Geometry::MESH) {
+ if (geom->type == Geometry::MESH || geom->type == Geometry::VOLUME) {
Mesh *mesh = static_cast<Mesh *>(geom);
mesh->pack_shaders(scene, &tri_shader[mesh->prim_offset]);
mesh->pack_normals(&vnormal[mesh->vert_offset]);
@@ -992,7 +993,7 @@ void GeometryManager::device_update_mesh(
if (for_displacement) {
float4 *prim_tri_verts = dscene->prim_tri_verts.alloc(tri_size * 3);
foreach (Geometry *geom, scene->geometry) {
- if (geom->type == Geometry::MESH) {
+ if (geom->type == Geometry::MESH || geom->type == Geometry::VOLUME) {
Mesh *mesh = static_cast<Mesh *>(geom);
for (size_t i = 0; i < mesh->num_triangles(); ++i) {
Mesh::Triangle t = mesh->get_triangle(i);
@@ -1122,18 +1123,16 @@ void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Pro
}
}
- if (need_update && geom->has_volume && geom->type == Geometry::MESH) {
+ if (need_update && geom->type == Geometry::VOLUME) {
/* Create volume meshes if there is voxel data. */
- if (geom->has_voxel_attributes()) {
- if (!volume_images_updated) {
- progress.set_status("Updating Meshes Volume Bounds");
- device_update_volume_images(device, scene, progress);
- volume_images_updated = true;
- }
-
- Mesh *mesh = static_cast<Mesh *>(geom);
- create_volume_mesh(mesh, progress);
+ if (!volume_images_updated) {
+ progress.set_status("Updating Meshes Volume Bounds");
+ device_update_volume_images(device, scene, progress);
+ volume_images_updated = true;
}
+
+ Volume *volume = static_cast<Volume *>(geom);
+ create_volume_mesh(volume, progress);
}
if (geom->type == Geometry::HAIR) {
@@ -1238,7 +1237,7 @@ void GeometryManager::device_update(Device *device,
geom->need_update = true;
}
- if (geom->need_update && geom->type == Geometry::MESH) {
+ if (geom->need_update && (geom->type == Geometry::MESH || geom->type == Geometry::VOLUME)) {
Mesh *mesh = static_cast<Mesh *>(geom);
/* Update normals. */
diff --git a/intern/cycles/render/geometry.h b/intern/cycles/render/geometry.h
index b0284304843..bcadb3a8051 100644
--- a/intern/cycles/render/geometry.h
+++ b/intern/cycles/render/geometry.h
@@ -40,6 +40,7 @@ class RenderStats;
class Scene;
class SceneParams;
class Shader;
+class Volume;
/* Geometry
*
@@ -52,6 +53,7 @@ class Geometry : public Node {
enum Type {
MESH,
HAIR,
+ VOLUME,
};
Type type;
@@ -166,7 +168,7 @@ class GeometryManager {
protected:
bool displace(Device *device, DeviceScene *dscene, Scene *scene, Mesh *mesh, Progress &progress);
- void create_volume_mesh(Mesh *mesh, Progress &progress);
+ void create_volume_mesh(Volume *volume, Progress &progress);
/* Attributes */
void update_osl_attributes(Device *device,
diff --git a/intern/cycles/render/graph.cpp b/intern/cycles/render/graph.cpp
index 1b138455515..485d6167ee3 100644
--- a/intern/cycles/render/graph.cpp
+++ b/intern/cycles/render/graph.cpp
@@ -221,7 +221,7 @@ ShaderGraph::ShaderGraph()
finalized = false;
simplified = false;
num_node_ids = 0;
- add(new OutputNode());
+ add(create_node<OutputNode>());
}
ShaderGraph::~ShaderGraph()
@@ -272,7 +272,7 @@ void ShaderGraph::connect(ShaderOutput *from, ShaderInput *to)
ShaderInput *convert_in;
if (to->type() == SocketType::CLOSURE) {
- EmissionNode *emission = new EmissionNode();
+ EmissionNode *emission = create_node<EmissionNode>();
emission->color = make_float3(1.0f, 1.0f, 1.0f);
emission->strength = 1.0f;
convert = add(emission);
@@ -285,7 +285,7 @@ void ShaderGraph::connect(ShaderOutput *from, ShaderInput *to)
}
}
else {
- convert = add(new ConvertNode(from->type(), to->type(), true));
+ convert = add(create_node<ConvertNode>(from->type(), to->type(), true));
convert_in = convert->inputs[0];
}
@@ -416,7 +416,7 @@ void ShaderGraph::find_dependencies(ShaderNodeSet &dependencies, ShaderInput *in
void ShaderGraph::clear_nodes()
{
foreach (ShaderNode *node, nodes) {
- delete node;
+ delete_node(node);
}
nodes.clear();
}
@@ -428,7 +428,7 @@ void ShaderGraph::copy_nodes(ShaderNodeSet &nodes, ShaderNodeMap &nnodemap)
/* copy nodes */
foreach (ShaderNode *node, nodes) {
- ShaderNode *nnode = node->clone();
+ ShaderNode *nnode = node->clone(this);
nnodemap[node] = nnode;
/* create new inputs and outputs to recreate links and ensure
@@ -523,7 +523,7 @@ void ShaderGraph::remove_proxy_nodes()
if (!removed[node->id])
newnodes.push_back(node);
else
- delete node;
+ delete_node(node);
}
nodes = newnodes;
@@ -585,7 +585,7 @@ void ShaderGraph::constant_fold(Scene *scene)
* that happens to ensure there is still a valid graph for displacement.
*/
if (has_displacement && !output()->input("Displacement")->link) {
- ColorNode *value = (ColorNode *)add(new ColorNode());
+ ColorNode *value = (ColorNode *)add(create_node<ColorNode>());
value->value = output()->displacement;
connect(value->output("Color"), output()->input("Displacement"));
@@ -821,7 +821,7 @@ void ShaderGraph::clean(Scene *scene)
if (visited[node->id])
newnodes.push_back(node);
else
- delete node;
+ delete_node(node);
}
nodes = newnodes;
@@ -848,43 +848,43 @@ void ShaderGraph::default_inputs(bool do_osl)
if (!input->link && (!(input->flags() & SocketType::OSL_INTERNAL) || do_osl)) {
if (input->flags() & SocketType::LINK_TEXTURE_GENERATED) {
if (!texco)
- texco = new TextureCoordinateNode();
+ texco = create_node<TextureCoordinateNode>();
connect(texco->output("Generated"), input);
}
if (input->flags() & SocketType::LINK_TEXTURE_NORMAL) {
if (!texco)
- texco = new TextureCoordinateNode();
+ texco = create_node<TextureCoordinateNode>();
connect(texco->output("Normal"), input);
}
else if (input->flags() & SocketType::LINK_TEXTURE_UV) {
if (!texco)
- texco = new TextureCoordinateNode();
+ texco = create_node<TextureCoordinateNode>();
connect(texco->output("UV"), input);
}
else if (input->flags() & SocketType::LINK_INCOMING) {
if (!geom)
- geom = new GeometryNode();
+ geom = create_node<GeometryNode>();
connect(geom->output("Incoming"), input);
}
else if (input->flags() & SocketType::LINK_NORMAL) {
if (!geom)
- geom = new GeometryNode();
+ geom = create_node<GeometryNode>();
connect(geom->output("Normal"), input);
}
else if (input->flags() & SocketType::LINK_POSITION) {
if (!geom)
- geom = new GeometryNode();
+ geom = create_node<GeometryNode>();
connect(geom->output("Position"), input);
}
else if (input->flags() & SocketType::LINK_TANGENT) {
if (!geom)
- geom = new GeometryNode();
+ geom = create_node<GeometryNode>();
connect(geom->output("Tangent"), input);
}
@@ -999,10 +999,10 @@ void ShaderGraph::bump_from_displacement(bool use_object_space)
* output, so it can finally set the shader normal, note we are only doing
* this for bump from displacement, this will be the only bump allowed to
* overwrite the shader normal */
- ShaderNode *set_normal = add(new SetNormalNode());
+ ShaderNode *set_normal = add(create_node<SetNormalNode>());
/* add bump node and connect copied graphs to it */
- BumpNode *bump = (BumpNode *)add(new BumpNode());
+ BumpNode *bump = (BumpNode *)add(create_node<BumpNode>());
bump->use_object_space = use_object_space;
bump->distance = 1.0f;
@@ -1012,15 +1012,15 @@ void ShaderGraph::bump_from_displacement(bool use_object_space)
ShaderOutput *out_dy = nodes_dy[out->parent]->output(out->name());
/* convert displacement vector to height */
- VectorMathNode *dot_center = (VectorMathNode *)add(new VectorMathNode());
- VectorMathNode *dot_dx = (VectorMathNode *)add(new VectorMathNode());
- VectorMathNode *dot_dy = (VectorMathNode *)add(new VectorMathNode());
+ VectorMathNode *dot_center = (VectorMathNode *)add(create_node<VectorMathNode>());
+ VectorMathNode *dot_dx = (VectorMathNode *)add(create_node<VectorMathNode>());
+ VectorMathNode *dot_dy = (VectorMathNode *)add(create_node<VectorMathNode>());
dot_center->type = NODE_VECTOR_MATH_DOT_PRODUCT;
dot_dx->type = NODE_VECTOR_MATH_DOT_PRODUCT;
dot_dy->type = NODE_VECTOR_MATH_DOT_PRODUCT;
- GeometryNode *geom = (GeometryNode *)add(new GeometryNode());
+ GeometryNode *geom = (GeometryNode *)add(create_node<GeometryNode>());
connect(geom->output("Normal"), dot_center->input("Vector2"));
connect(geom->output("Normal"), dot_dx->input("Vector2"));
connect(geom->output("Normal"), dot_dy->input("Vector2"));
@@ -1064,7 +1064,7 @@ void ShaderGraph::transform_multi_closure(ShaderNode *node, ShaderOutput *weight
if (fin) {
/* mix closure: add node to mix closure weights */
- MixClosureWeightNode *mix_node = new MixClosureWeightNode();
+ MixClosureWeightNode *mix_node = create_node<MixClosureWeightNode>();
add(mix_node);
ShaderInput *fac_in = mix_node->input("Fac");
ShaderInput *weight_in = mix_node->input("Weight");
@@ -1101,7 +1101,7 @@ void ShaderGraph::transform_multi_closure(ShaderNode *node, ShaderOutput *weight
/* already has a weight connected to it? add weights */
float weight_value = node->get_float(weight_in->socket_type);
if (weight_in->link || weight_value != 0.0f) {
- MathNode *math_node = new MathNode();
+ MathNode *math_node = create_node<MathNode>();
add(math_node);
if (weight_in->link)
diff --git a/intern/cycles/render/graph.h b/intern/cycles/render/graph.h
index febd7a76f03..f6ee708b3f8 100644
--- a/intern/cycles/render/graph.h
+++ b/intern/cycles/render/graph.h
@@ -159,7 +159,7 @@ class ShaderNode : public Node {
ShaderInput *input(ustring name);
ShaderOutput *output(ustring name);
- virtual ShaderNode *clone() const = 0;
+ virtual ShaderNode *clone(ShaderGraph *graph) const = 0;
virtual void attributes(Shader *shader, AttributeRequestSet *attributes);
virtual void compile(SVMCompiler &compiler) = 0;
virtual void compile(OSLCompiler &compiler) = 0;
@@ -275,9 +275,9 @@ class ShaderNode : public Node {
#define SHADER_NODE_CLASS(type) \
NODE_DECLARE \
type(); \
- virtual ShaderNode *clone() const \
+ virtual ShaderNode *clone(ShaderGraph *graph) const \
{ \
- return new type(*this); \
+ return graph->create_node<type>(*this); \
} \
virtual void compile(SVMCompiler &compiler); \
virtual void compile(OSLCompiler &compiler);
@@ -289,9 +289,9 @@ class ShaderNode : public Node {
virtual void compile(OSLCompiler &compiler);
#define SHADER_NODE_BASE_CLASS(type) \
- virtual ShaderNode *clone() const \
+ virtual ShaderNode *clone(ShaderGraph *graph) const \
{ \
- return new type(*this); \
+ return graph->create_node<type>(*this); \
} \
virtual void compile(SVMCompiler &compiler); \
virtual void compile(OSLCompiler &compiler);
@@ -312,7 +312,7 @@ typedef map<ShaderNode *, ShaderNode *, ShaderNodeIDComparator> ShaderNodeMap;
* Shader graph of nodes. Also does graph manipulations for default inputs,
* bump mapping from displacement, and possibly other things in the future. */
-class ShaderGraph {
+class ShaderGraph : public NodeOwner {
public:
list<ShaderNode *> nodes;
size_t num_node_ids;
@@ -345,6 +345,24 @@ class ShaderGraph {
void dump_graph(const char *filename);
+ /* This function is used to create a node of a specified type instead of
+ * calling 'new', and sets the graph as the owner of the node.
+ */
+ template<typename T, typename... Args> T *create_node(Args &&... args)
+ {
+ T *node = new T(args...);
+ node->set_owner(this);
+ return node;
+ }
+
+ /* This function is used to delete a node created and owned by the graph.
+ */
+ template<typename T> void delete_node(T *node)
+ {
+ assert(node->get_owner() == this);
+ delete node;
+ }
+
protected:
typedef pair<ShaderNode *const, ShaderNode *> NodePair;
diff --git a/intern/cycles/render/integrator.cpp b/intern/cycles/render/integrator.cpp
index eff416efa2b..6c295b20538 100644
--- a/intern/cycles/render/integrator.cpp
+++ b/intern/cycles/render/integrator.cpp
@@ -152,7 +152,7 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene
kintegrator->seed = hash_uint2(seed, 0);
- kintegrator->use_ambient_occlusion = ((Pass::contains(scene->film->passes, PASS_AO)) ||
+ kintegrator->use_ambient_occlusion = ((Pass::contains(scene->passes, PASS_AO)) ||
dscene->data.background.ao_factor != 0.0f);
kintegrator->sample_clamp_direct = (sample_clamp_direct == 0.0f) ? FLT_MAX :
diff --git a/intern/cycles/render/light.cpp b/intern/cycles/render/light.cpp
index 183c02cb6b9..5f586f98b13 100644
--- a/intern/cycles/render/light.cpp
+++ b/intern/cycles/render/light.cpp
@@ -250,7 +250,7 @@ void LightManager::test_enabled_lights(Scene *scene)
bool LightManager::object_usable_as_light(Object *object)
{
Geometry *geom = object->geometry;
- if (geom->type != Geometry::MESH) {
+ if (geom->type != Geometry::MESH && geom->type != Geometry::VOLUME) {
return false;
}
/* Skip objects with NaNs */
@@ -634,7 +634,7 @@ void LightManager::device_update_background(Device *device,
sun_direction = transform_direction(&sky_transform, sun_direction);
/* Pack sun direction and size. */
- float half_angle = sky->sun_size * 0.5f;
+ float half_angle = sky->get_sun_size() * 0.5f;
kbackground->sun = make_float4(
sun_direction.x, sun_direction.y, sun_direction.z, half_angle);
diff --git a/intern/cycles/render/mesh.cpp b/intern/cycles/render/mesh.cpp
index c262d770331..3015ac5e569 100644
--- a/intern/cycles/render/mesh.cpp
+++ b/intern/cycles/render/mesh.cpp
@@ -135,7 +135,8 @@ NODE_DEFINE(Mesh)
return type;
}
-Mesh::Mesh() : Geometry(node_type, Geometry::MESH), subd_attributes(this, ATTR_PRIM_SUBD)
+Mesh::Mesh(const NodeType *node_type_, Type geom_type_)
+ : Geometry(node_type_, geom_type_), subd_attributes(this, ATTR_PRIM_SUBD)
{
vert_offset = 0;
@@ -145,10 +146,6 @@ Mesh::Mesh() : Geometry(node_type, Geometry::MESH), subd_attributes(this, ATTR_P
num_subd_verts = 0;
- volume_clipping = 0.001f;
- volume_step_size = 0.0f;
- volume_object_space = false;
-
num_ngons = 0;
subdivision_type = SUBDIVISION_NONE;
@@ -157,6 +154,10 @@ Mesh::Mesh() : Geometry(node_type, Geometry::MESH), subd_attributes(this, ATTR_P
patch_table = NULL;
}
+Mesh::Mesh() : Mesh(node_type, Geometry::MESH)
+{
+}
+
Mesh::~Mesh()
{
delete patch_table;
diff --git a/intern/cycles/render/mesh.h b/intern/cycles/render/mesh.h
index d0cf4d557aa..c8286a01e2c 100644
--- a/intern/cycles/render/mesh.h
+++ b/intern/cycles/render/mesh.h
@@ -52,6 +52,9 @@ struct PackedPatchTable;
/* Mesh */
class Mesh : public Geometry {
+ protected:
+ Mesh(const NodeType *node_type_, Type geom_type_);
+
public:
NODE_DECLARE
@@ -133,10 +136,6 @@ class Mesh : public Geometry {
array<int> triangle_patch; /* must be < 0 for non subd triangles */
array<float2> vert_patch_uv;
- float volume_clipping;
- float volume_step_size;
- bool volume_object_space;
-
array<SubdFace> subd_faces;
array<int> subd_face_corners;
int num_ngons;
diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp
index d5f65fb54db..de32ce53ad7 100644
--- a/intern/cycles/render/nodes.cpp
+++ b/intern/cycles/render/nodes.cpp
@@ -262,9 +262,9 @@ ImageTextureNode::ImageTextureNode() : ImageSlotTextureNode(node_type)
tiles.push_back(1001);
}
-ShaderNode *ImageTextureNode::clone() const
+ShaderNode *ImageTextureNode::clone(ShaderGraph *graph) const
{
- ImageTextureNode *node = new ImageTextureNode(*this);
+ ImageTextureNode *node = graph->create_node<ImageTextureNode>(*this);
node->handle = handle;
return node;
}
@@ -525,9 +525,9 @@ EnvironmentTextureNode::EnvironmentTextureNode() : ImageSlotTextureNode(node_typ
animated = false;
}
-ShaderNode *EnvironmentTextureNode::clone() const
+ShaderNode *EnvironmentTextureNode::clone(ShaderGraph *graph) const
{
- EnvironmentTextureNode *node = new EnvironmentTextureNode(*this);
+ EnvironmentTextureNode *node = graph->create_node<EnvironmentTextureNode>(*this);
node->handle = handle;
return node;
}
@@ -776,7 +776,7 @@ static void sky_texture_precompute_nishita(SunSky *sunsky,
sunsky->nishita_data[5] = pixel_top[2];
sunsky->nishita_data[6] = sun_elevation;
sunsky->nishita_data[7] = sun_rotation;
- sunsky->nishita_data[8] = sun_disc ? sun_size : 0.0f;
+ sunsky->nishita_data[8] = sun_disc ? sun_size : -1.0f;
sunsky->nishita_data[9] = sun_intensity;
}
@@ -834,7 +834,7 @@ void SkyTextureNode::compile(SVMCompiler &compiler)
sky_texture_precompute_nishita(&sunsky,
sun_disc,
- sun_size,
+ get_sun_size(),
sun_intensity,
sun_elevation,
sun_rotation,
@@ -930,7 +930,7 @@ void SkyTextureNode::compile(OSLCompiler &compiler)
sky_texture_precompute_nishita(&sunsky,
sun_disc,
- sun_size,
+ get_sun_size(),
sun_intensity,
sun_elevation,
sun_rotation,
@@ -1234,9 +1234,9 @@ IESLightNode::IESLightNode() : TextureNode(node_type)
slot = -1;
}
-ShaderNode *IESLightNode::clone() const
+ShaderNode *IESLightNode::clone(ShaderGraph *graph) const
{
- IESLightNode *node = new IESLightNode(*this);
+ IESLightNode *node = graph->create_node<IESLightNode>(*this);
node->light_manager = NULL;
node->slot = -1;
@@ -1782,12 +1782,12 @@ PointDensityTextureNode::~PointDensityTextureNode()
{
}
-ShaderNode *PointDensityTextureNode::clone() const
+ShaderNode *PointDensityTextureNode::clone(ShaderGraph *graph) const
{
/* Increase image user count for new node. We need to ensure to not call
* add_image again, to work around access of freed data on the Blender
* side. A better solution should be found to avoid this. */
- PointDensityTextureNode *node = new PointDensityTextureNode(*this);
+ PointDensityTextureNode *node = graph->create_node<PointDensityTextureNode>(*this);
node->handle = handle; /* TODO: not needed? */
return node;
}
@@ -2783,8 +2783,8 @@ void PrincipledBsdfNode::expand(ShaderGraph *graph)
ShaderInput *emission_in = input("Emission");
if (emission_in->link || emission != make_float3(0.0f, 0.0f, 0.0f)) {
/* Create add closure and emission. */
- AddClosureNode *add = new AddClosureNode();
- EmissionNode *emission_node = new EmissionNode();
+ AddClosureNode *add = graph->create_node<AddClosureNode>();
+ EmissionNode *emission_node = graph->create_node<EmissionNode>();
ShaderOutput *new_out = add->output("Closure");
graph->add(add);
@@ -2802,8 +2802,8 @@ void PrincipledBsdfNode::expand(ShaderGraph *graph)
ShaderInput *alpha_in = input("Alpha");
if (alpha_in->link || alpha != 1.0f) {
/* Create mix and transparent BSDF for alpha transparency. */
- MixClosureNode *mix = new MixClosureNode();
- TransparentBsdfNode *transparent = new TransparentBsdfNode();
+ MixClosureNode *mix = graph->create_node<MixClosureNode>();
+ TransparentBsdfNode *transparent = graph->create_node<TransparentBsdfNode>();
graph->add(mix);
graph->add(transparent);
@@ -4475,7 +4475,7 @@ void VolumeInfoNode::expand(ShaderGraph *graph)
{
ShaderOutput *color_out = output("Color");
if (!color_out->links.empty()) {
- AttributeNode *attr = new AttributeNode();
+ AttributeNode *attr = graph->create_node<AttributeNode>();
attr->attribute = "color";
graph->add(attr);
graph->relink(color_out, attr->output("Color"));
@@ -4483,7 +4483,7 @@ void VolumeInfoNode::expand(ShaderGraph *graph)
ShaderOutput *density_out = output("Density");
if (!density_out->links.empty()) {
- AttributeNode *attr = new AttributeNode();
+ AttributeNode *attr = graph->create_node<AttributeNode>();
attr->attribute = "density";
graph->add(attr);
graph->relink(density_out, attr->output("Fac"));
@@ -4491,7 +4491,7 @@ void VolumeInfoNode::expand(ShaderGraph *graph)
ShaderOutput *flame_out = output("Flame");
if (!flame_out->links.empty()) {
- AttributeNode *attr = new AttributeNode();
+ AttributeNode *attr = graph->create_node<AttributeNode>();
attr->attribute = "flame";
graph->add(attr);
graph->relink(flame_out, attr->output("Fac"));
@@ -4499,7 +4499,7 @@ void VolumeInfoNode::expand(ShaderGraph *graph)
ShaderOutput *temperature_out = output("Temperature");
if (!temperature_out->links.empty()) {
- AttributeNode *attr = new AttributeNode();
+ AttributeNode *attr = graph->create_node<AttributeNode>();
attr->attribute = "temperature";
graph->add(attr);
graph->relink(temperature_out, attr->output("Fac"));
@@ -5768,7 +5768,7 @@ void MapRangeNode::expand(ShaderGraph *graph)
if (clamp) {
ShaderOutput *result_out = output("Result");
if (!result_out->links.empty()) {
- ClampNode *clamp_node = new ClampNode();
+ ClampNode *clamp_node = graph->create_node<ClampNode>();
clamp_node->type = NODE_CLAMP_RANGE;
graph->add(clamp_node);
graph->relink(result_out, clamp_node->output("Result"));
@@ -5910,9 +5910,9 @@ OutputAOVNode::OutputAOVNode() : ShaderNode(node_type)
void OutputAOVNode::simplify_settings(Scene *scene)
{
- slot = scene->film->get_aov_offset(name.string(), is_color);
+ slot = scene->film->get_aov_offset(scene, name.string(), is_color);
if (slot == -1) {
- slot = scene->film->get_aov_offset(name.string(), is_color);
+ slot = scene->film->get_aov_offset(scene, name.string(), is_color);
}
if (slot == -1 || is_color) {
@@ -6009,7 +6009,7 @@ void MathNode::expand(ShaderGraph *graph)
if (use_clamp) {
ShaderOutput *result_out = output("Value");
if (!result_out->links.empty()) {
- ClampNode *clamp_node = new ClampNode();
+ ClampNode *clamp_node = graph->create_node<ClampNode>();
clamp_node->type = NODE_CLAMP_MINMAX;
clamp_node->min = 0.0f;
clamp_node->max = 1.0f;
@@ -6337,7 +6337,7 @@ void BumpNode::constant_fold(const ConstantFolder &folder)
if (height_in->link == NULL) {
if (normal_in->link == NULL) {
- GeometryNode *geom = new GeometryNode();
+ GeometryNode *geom = folder.graph->create_node<GeometryNode>();
folder.graph->add(geom);
folder.bypass(geom->output("Normal"));
}
@@ -6620,12 +6620,12 @@ OSLNode::~OSLNode()
delete type;
}
-ShaderNode *OSLNode::clone() const
+ShaderNode *OSLNode::clone(ShaderGraph *graph) const
{
- return OSLNode::create(this->inputs.size(), this);
+ return OSLNode::create(graph, this->inputs.size(), this);
}
-OSLNode *OSLNode::create(size_t num_inputs, const OSLNode *from)
+OSLNode *OSLNode::create(ShaderGraph *graph, size_t num_inputs, const OSLNode *from)
{
/* allocate space for the node itself and parameters, aligned to 16 bytes
* assuming that's the most parameter types need */
@@ -6636,7 +6636,9 @@ OSLNode *OSLNode::create(size_t num_inputs, const OSLNode *from)
memset(node_memory, 0, node_size + inputs_size);
if (!from) {
- return new (node_memory) OSLNode();
+ OSLNode *node = new (node_memory) OSLNode();
+ node->set_owner(graph);
+ return node;
}
else {
/* copy input default values and node type for cloning */
@@ -6644,6 +6646,7 @@ OSLNode *OSLNode::create(size_t num_inputs, const OSLNode *from)
OSLNode *node = new (node_memory) OSLNode(*from);
node->type = new NodeType(*(from->type));
+ node->set_owner(from->owner);
return node;
}
}
diff --git a/intern/cycles/render/nodes.h b/intern/cycles/render/nodes.h
index 326f1d14168..59e00231c2c 100644
--- a/intern/cycles/render/nodes.h
+++ b/intern/cycles/render/nodes.h
@@ -92,7 +92,7 @@ class ImageSlotTextureNode : public TextureNode {
class ImageTextureNode : public ImageSlotTextureNode {
public:
SHADER_NODE_NO_CLONE_CLASS(ImageTextureNode)
- ShaderNode *clone() const;
+ ShaderNode *clone(ShaderGraph *graph) const;
void attributes(Shader *shader, AttributeRequestSet *attributes);
bool has_attribute_dependency()
{
@@ -126,7 +126,7 @@ class ImageTextureNode : public ImageSlotTextureNode {
class EnvironmentTextureNode : public ImageSlotTextureNode {
public:
SHADER_NODE_NO_CLONE_CLASS(EnvironmentTextureNode)
- ShaderNode *clone() const;
+ ShaderNode *clone(ShaderGraph *graph) const;
void attributes(Shader *shader, AttributeRequestSet *attributes);
bool has_attribute_dependency()
{
@@ -179,6 +179,12 @@ class SkyTextureNode : public TextureNode {
float ozone_density;
float3 vector;
ImageHandle handle;
+
+ float get_sun_size()
+ {
+ /* Clamping for numerical precision. */
+ return fmaxf(sun_size, 0.0005f);
+ }
};
class OutputNode : public ShaderNode {
@@ -358,7 +364,7 @@ class PointDensityTextureNode : public ShaderNode {
}
~PointDensityTextureNode();
- ShaderNode *clone() const;
+ ShaderNode *clone(ShaderGraph *graph) const;
void attributes(Shader *shader, AttributeRequestSet *attributes);
bool has_attribute_dependency()
{
@@ -394,7 +400,7 @@ class IESLightNode : public TextureNode {
SHADER_NODE_NO_CLONE_CLASS(IESLightNode)
~IESLightNode();
- ShaderNode *clone() const;
+ ShaderNode *clone(ShaderGraph *graph) const;
virtual int get_group()
{
return NODE_GROUP_LEVEL_2;
@@ -1495,10 +1501,10 @@ class SetNormalNode : public ShaderNode {
class OSLNode : public ShaderNode {
public:
- static OSLNode *create(size_t num_inputs, const OSLNode *from = NULL);
+ static OSLNode *create(ShaderGraph *graph, size_t num_inputs, const OSLNode *from = NULL);
~OSLNode();
- ShaderNode *clone() const;
+ ShaderNode *clone(ShaderGraph *graph) const;
char *input_default_value();
void add_input(ustring name, SocketType::Type type);
diff --git a/intern/cycles/render/object.cpp b/intern/cycles/render/object.cpp
index f200e409b9e..9396ae49288 100644
--- a/intern/cycles/render/object.cpp
+++ b/intern/cycles/render/object.cpp
@@ -24,6 +24,7 @@
#include "render/mesh.h"
#include "render/particles.h"
#include "render/scene.h"
+#include "render/volume.h"
#include "util/util_foreach.h"
#include "util/util_logging.h"
@@ -270,7 +271,7 @@ uint Object::visibility_for_tracing() const
float Object::compute_volume_step_size() const
{
- if (geometry->type != Geometry::MESH) {
+ if (geometry->type != Geometry::MESH && geometry->type != Geometry::VOLUME) {
return FLT_MAX;
}
@@ -299,37 +300,41 @@ float Object::compute_volume_step_size() const
/* Compute step size from voxel grids. */
float step_size = FLT_MAX;
- foreach (Attribute &attr, mesh->attributes.attributes) {
- if (attr.element == ATTR_ELEMENT_VOXEL) {
- ImageHandle &handle = attr.data_voxel();
- const ImageMetaData &metadata = handle.metadata();
- if (metadata.width == 0 || metadata.height == 0 || metadata.depth == 0) {
- continue;
- }
+ if (geometry->type == Geometry::VOLUME) {
+ Volume *volume = static_cast<Volume *>(geometry);
- /* User specified step size. */
- float voxel_step_size = mesh->volume_step_size;
+ foreach (Attribute &attr, volume->attributes.attributes) {
+ if (attr.element == ATTR_ELEMENT_VOXEL) {
+ ImageHandle &handle = attr.data_voxel();
+ const ImageMetaData &metadata = handle.metadata();
+ if (metadata.width == 0 || metadata.height == 0 || metadata.depth == 0) {
+ continue;
+ }
- if (voxel_step_size == 0.0f) {
- /* Auto detect step size. */
- float3 size = make_float3(
- 1.0f / metadata.width, 1.0f / metadata.height, 1.0f / metadata.depth);
+ /* User specified step size. */
+ float voxel_step_size = volume->step_size;
- /* Step size is transformed from voxel to world space. */
- Transform voxel_tfm = tfm;
- if (metadata.use_transform_3d) {
- voxel_tfm = tfm * transform_inverse(metadata.transform_3d);
+ if (voxel_step_size == 0.0f) {
+ /* Auto detect step size. */
+ float3 size = make_float3(
+ 1.0f / metadata.width, 1.0f / metadata.height, 1.0f / metadata.depth);
+
+ /* Step size is transformed from voxel to world space. */
+ Transform voxel_tfm = tfm;
+ if (metadata.use_transform_3d) {
+ voxel_tfm = tfm * transform_inverse(metadata.transform_3d);
+ }
+ voxel_step_size = min3(fabs(transform_direction(&voxel_tfm, size)));
+ }
+ else if (volume->object_space) {
+ /* User specified step size in object space. */
+ float3 size = make_float3(voxel_step_size, voxel_step_size, voxel_step_size);
+ voxel_step_size = min3(fabs(transform_direction(&tfm, size)));
}
- voxel_step_size = min3(fabs(transform_direction(&voxel_tfm, size)));
- }
- else if (mesh->volume_object_space) {
- /* User specified step size in object space. */
- float3 size = make_float3(voxel_step_size, voxel_step_size, voxel_step_size);
- voxel_step_size = min3(fabs(transform_direction(&tfm, size)));
- }
- if (voxel_step_size > 0.0f) {
- step_size = fminf(voxel_step_size, step_size);
+ if (voxel_step_size > 0.0f) {
+ step_size = fminf(voxel_step_size, step_size);
+ }
}
}
}
@@ -365,14 +370,14 @@ static float object_surface_area(UpdateObjectTransformState *state,
const Transform &tfm,
Geometry *geom)
{
- if (geom->type != Geometry::MESH) {
+ if (geom->type != Geometry::MESH && geom->type != Geometry::VOLUME) {
return 0.0f;
}
Mesh *mesh = static_cast<Mesh *>(geom);
- if (mesh->has_volume) {
+ if (mesh->has_volume || geom->type == Geometry::VOLUME) {
/* Volume density automatically adjust to object scale. */
- if (mesh->volume_object_space) {
+ if (geom->type == Geometry::VOLUME && static_cast<Volume *>(geom)->object_space) {
const float3 unit = normalize(make_float3(1.0f, 1.0f, 1.0f));
return 1.0f / len(transform_direction(&tfm, unit));
}
@@ -527,7 +532,9 @@ void ObjectManager::device_update_object_transform(UpdateObjectTransformState *s
kobject.dupli_uv[1] = ob->dupli_uv[1];
int totalsteps = geom->motion_steps;
kobject.numsteps = (totalsteps - 1) / 2;
- kobject.numverts = (geom->type == Geometry::MESH) ? static_cast<Mesh *>(geom)->verts.size() : 0;
+ kobject.numverts = (geom->type == Geometry::MESH || geom->type == Geometry::VOLUME) ?
+ static_cast<Mesh *>(geom)->verts.size() :
+ 0;
kobject.patch_map_offset = 0;
kobject.attribute_map_offset = 0;
uint32_t hash_name = util_murmur_hash3(ob->name.c_str(), ob->name.length(), 0);
@@ -819,7 +826,7 @@ void ObjectManager::apply_static_transforms(DeviceScene *dscene, Scene *scene, P
bool apply = (geometry_users[geom] == 1) && !geom->has_surface_bssrdf &&
!geom->has_true_displacement();
- if (geom->type == Geometry::MESH) {
+ if (geom->type == Geometry::MESH || geom->type == Geometry::VOLUME) {
Mesh *mesh = static_cast<Mesh *>(geom);
apply = apply && mesh->subdivision_type == Mesh::SUBDIVISION_NONE;
}
diff --git a/intern/cycles/render/osl.cpp b/intern/cycles/render/osl.cpp
index 5c62ae73e47..1c54dc2d531 100644
--- a/intern/cycles/render/osl.cpp
+++ b/intern/cycles/render/osl.cpp
@@ -439,7 +439,8 @@ const char *OSLShaderManager::shader_load_bytecode(const string &hash, const str
/* This is a static function to avoid RTTI link errors with only this
* file being compiled without RTTI to match OSL and LLVM libraries. */
-OSLNode *OSLShaderManager::osl_node(ShaderManager *manager,
+OSLNode *OSLShaderManager::osl_node(ShaderGraph *graph,
+ ShaderManager *manager,
const std::string &filepath,
const std::string &bytecode_hash,
const std::string &bytecode)
@@ -482,7 +483,7 @@ OSLNode *OSLShaderManager::osl_node(ShaderManager *manager,
}
/* create node */
- OSLNode *node = OSLNode::create(num_inputs);
+ OSLNode *node = OSLNode::create(graph, num_inputs);
/* add new sockets from parameters */
set<void *> used_sockets;
diff --git a/intern/cycles/render/osl.h b/intern/cycles/render/osl.h
index 4dd9f6630f2..ea2d0ca492c 100644
--- a/intern/cycles/render/osl.h
+++ b/intern/cycles/render/osl.h
@@ -93,7 +93,8 @@ class OSLShaderManager : public ShaderManager {
OSLShaderInfo *shader_loaded_info(const string &hash);
/* create OSL node using OSLQuery */
- static OSLNode *osl_node(ShaderManager *manager,
+ static OSLNode *osl_node(ShaderGraph *graph,
+ ShaderManager *manager,
const std::string &filepath,
const std::string &bytecode_hash = "",
const std::string &bytecode = "");
diff --git a/intern/cycles/render/particles.cpp b/intern/cycles/render/particles.cpp
index ec9276eff86..494f5929df2 100644
--- a/intern/cycles/render/particles.cpp
+++ b/intern/cycles/render/particles.cpp
@@ -29,7 +29,13 @@ CCL_NAMESPACE_BEGIN
/* Particle System */
-ParticleSystem::ParticleSystem()
+NODE_DEFINE(ParticleSystem)
+{
+ NodeType *type = NodeType::add("particle_system", create);
+ return type;
+}
+
+ParticleSystem::ParticleSystem() : Node(node_type)
{
}
diff --git a/intern/cycles/render/particles.h b/intern/cycles/render/particles.h
index 65663c31c37..0b0408184fe 100644
--- a/intern/cycles/render/particles.h
+++ b/intern/cycles/render/particles.h
@@ -20,6 +20,8 @@
#include "util/util_array.h"
#include "util/util_types.h"
+#include "graph/node.h"
+
CCL_NAMESPACE_BEGIN
class Device;
@@ -40,8 +42,10 @@ struct Particle {
float3 angular_velocity;
};
-class ParticleSystem {
+class ParticleSystem : public Node {
public:
+ NODE_DECLARE
+
ParticleSystem();
~ParticleSystem();
diff --git a/intern/cycles/render/scene.cpp b/intern/cycles/render/scene.cpp
index 9016a8d325f..8f863b5d15f 100644
--- a/intern/cycles/render/scene.cpp
+++ b/intern/cycles/render/scene.cpp
@@ -29,9 +29,11 @@
#include "render/osl.h"
#include "render/particles.h"
#include "render/scene.h"
+#include "render/session.h"
#include "render/shader.h"
#include "render/svm.h"
#include "render/tables.h"
+#include "render/volume.h"
#include "util/util_foreach.h"
#include "util/util_guarded_allocator.h"
@@ -97,18 +99,24 @@ Scene::Scene(const SceneParams &params_, Device *device)
{
memset((void *)&dscene.data, 0, sizeof(dscene.data));
- camera = new Camera();
- dicing_camera = new Camera();
+ camera = create_node<Camera>();
+ dicing_camera = create_node<Camera>();
lookup_tables = new LookupTables();
- film = new Film();
- background = new Background();
+ film = create_node<Film>();
+ background = create_node<Background>();
light_manager = new LightManager();
geometry_manager = new GeometryManager();
object_manager = new ObjectManager();
- integrator = new Integrator();
+ integrator = create_node<Integrator>();
image_manager = new ImageManager(device->info);
particle_system_manager = new ParticleSystemManager();
bake_manager = new BakeManager();
+ kernels_loaded = false;
+
+ /* TODO(sergey): Check if it's indeed optimal value for the split kernel. */
+ max_closure_global = 1;
+
+ film->add_default(this);
/* OSL only works on the CPU */
if (device->info.has_osl)
@@ -317,7 +325,7 @@ Scene::MotionType Scene::need_motion()
{
if (integrator->motion_blur)
return MOTION_BLUR;
- else if (Pass::contains(film->passes, PASS_MOTION))
+ else if (Pass::contains(passes, PASS_MOTION))
return MOTION_PASS;
else
return MOTION_NONE;
@@ -334,7 +342,7 @@ float Scene::motion_shutter_time()
bool Scene::need_global_attribute(AttributeStandard std)
{
if (std == ATTR_STD_UV)
- return Pass::contains(film->passes, PASS_UV);
+ return Pass::contains(passes, PASS_UV);
else if (std == ATTR_STD_MOTION_VERTEX_POSITION)
return need_motion() != MOTION_NONE;
else if (std == ATTR_STD_MOTION_VERTEX_NORMAL)
@@ -396,4 +404,284 @@ void Scene::collect_statistics(RenderStats *stats)
image_manager->collect_statistics(stats);
}
+DeviceRequestedFeatures Scene::get_requested_device_features()
+{
+ DeviceRequestedFeatures requested_features;
+
+ shader_manager->get_requested_features(this, &requested_features);
+
+ /* This features are not being tweaked as often as shaders,
+ * so could be done selective magic for the viewport as well.
+ */
+ bool use_motion = need_motion() == Scene::MotionType::MOTION_BLUR;
+ requested_features.use_hair = false;
+ requested_features.use_hair_thick = (params.hair_shape == CURVE_THICK);
+ requested_features.use_object_motion = false;
+ requested_features.use_camera_motion = use_motion && camera->use_motion();
+ foreach (Object *object, objects) {
+ Geometry *geom = object->geometry;
+ if (use_motion) {
+ requested_features.use_object_motion |= object->use_motion() | geom->use_motion_blur;
+ requested_features.use_camera_motion |= geom->use_motion_blur;
+ }
+ if (object->is_shadow_catcher) {
+ requested_features.use_shadow_tricks = true;
+ }
+ if (geom->type == Geometry::MESH) {
+ Mesh *mesh = static_cast<Mesh *>(geom);
+#ifdef WITH_OPENSUBDIV
+ if (mesh->subdivision_type != Mesh::SUBDIVISION_NONE) {
+ requested_features.use_patch_evaluation = true;
+ }
+#endif
+ requested_features.use_true_displacement |= mesh->has_true_displacement();
+ }
+ else if (geom->type == Geometry::HAIR) {
+ requested_features.use_hair = true;
+ }
+ }
+
+ requested_features.use_background_light = light_manager->has_background_light(this);
+
+ requested_features.use_baking = bake_manager->get_baking();
+ requested_features.use_integrator_branched = (integrator->method == Integrator::BRANCHED_PATH);
+ if (film->denoising_data_pass) {
+ requested_features.use_denoising = true;
+ requested_features.use_shadow_tricks = true;
+ }
+
+ return requested_features;
+}
+
+bool Scene::update(Progress &progress, bool &kernel_switch_needed)
+{
+ /* update scene */
+ if (need_update()) {
+ /* Updated used shader tag so we know which features are need for the kernel. */
+ shader_manager->update_shaders_used(this);
+
+ /* Update max_closures. */
+ KernelIntegrator *kintegrator = &dscene.data.integrator;
+ if (params.background) {
+ kintegrator->max_closures = get_max_closure_count();
+ }
+ else {
+ /* Currently viewport render is faster with higher max_closures, needs investigating. */
+ kintegrator->max_closures = MAX_CLOSURE;
+ }
+
+ /* Load render kernels, before device update where we upload data to the GPU. */
+ bool new_kernels_needed = load_kernels(progress, false);
+
+ progress.set_status("Updating Scene");
+ MEM_GUARDED_CALL(&progress, device_update, device, progress);
+
+ DeviceKernelStatus kernel_switch_status = device->get_active_kernel_switch_state();
+ kernel_switch_needed = kernel_switch_status == DEVICE_KERNEL_FEATURE_KERNEL_AVAILABLE ||
+ kernel_switch_status == DEVICE_KERNEL_FEATURE_KERNEL_INVALID;
+ if (kernel_switch_status == DEVICE_KERNEL_WAITING_FOR_FEATURE_KERNEL) {
+ progress.set_kernel_status("Compiling render kernels");
+ }
+ if (new_kernels_needed || kernel_switch_needed) {
+ progress.set_kernel_status("Compiling render kernels");
+ device->wait_for_availability(loaded_kernel_features);
+ progress.set_kernel_status("");
+ }
+
+ return true;
+ }
+ return false;
+}
+
+bool Scene::load_kernels(Progress &progress, bool lock_scene)
+{
+ thread_scoped_lock scene_lock;
+ if (lock_scene) {
+ scene_lock = thread_scoped_lock(mutex);
+ }
+
+ DeviceRequestedFeatures requested_features = get_requested_device_features();
+
+ if (!kernels_loaded || loaded_kernel_features.modified(requested_features)) {
+ progress.set_status("Loading render kernels (may take a few minutes the first time)");
+
+ scoped_timer timer;
+
+ VLOG(2) << "Requested features:\n" << requested_features;
+ if (!device->load_kernels(requested_features)) {
+ string message = device->error_message();
+ if (message.empty())
+ message = "Failed loading render kernel, see console for errors";
+
+ progress.set_error(message);
+ progress.set_status(message);
+ progress.set_update();
+ return false;
+ }
+
+ progress.add_skip_time(timer, false);
+ VLOG(1) << "Total time spent loading kernels: " << time_dt() - timer.get_start();
+
+ kernels_loaded = true;
+ loaded_kernel_features = requested_features;
+ return true;
+ }
+ return false;
+}
+
+int Scene::get_max_closure_count()
+{
+ if (shader_manager->use_osl()) {
+ /* OSL always needs the maximum as we can't predict the
+ * number of closures a shader might generate. */
+ return MAX_CLOSURE;
+ }
+
+ int max_closures = 0;
+ for (int i = 0; i < shaders.size(); i++) {
+ Shader *shader = shaders[i];
+ if (shader->used) {
+ int num_closures = shader->graph->get_num_closures();
+ max_closures = max(max_closures, num_closures);
+ }
+ }
+ max_closure_global = max(max_closure_global, max_closures);
+
+ if (max_closure_global > MAX_CLOSURE) {
+ /* This is usually harmless as more complex shader tend to get many
+ * closures discarded due to mixing or low weights. We need to limit
+ * to MAX_CLOSURE as this is hardcoded in CPU/mega kernels, and it
+ * avoids excessive memory usage for split kernels. */
+ VLOG(2) << "Maximum number of closures exceeded: " << max_closure_global << " > "
+ << MAX_CLOSURE;
+
+ max_closure_global = MAX_CLOSURE;
+ }
+
+ return max_closure_global;
+}
+
+template<> Light *Scene::create_node<Light>()
+{
+ Light *node = new Light();
+ node->set_owner(this);
+ lights.push_back(node);
+ light_manager->tag_update(this);
+ return node;
+}
+
+template<> Mesh *Scene::create_node<Mesh>()
+{
+ Mesh *node = new Mesh();
+ node->set_owner(this);
+ geometry.push_back(node);
+ geometry_manager->tag_update(this);
+ return node;
+}
+
+template<> Hair *Scene::create_node<Hair>()
+{
+ Hair *node = new Hair();
+ node->set_owner(this);
+ geometry.push_back(node);
+ geometry_manager->tag_update(this);
+ return node;
+}
+
+template<> Volume *Scene::create_node<Volume>()
+{
+ Volume *node = new Volume();
+ node->set_owner(this);
+ geometry.push_back(node);
+ geometry_manager->tag_update(this);
+ return node;
+}
+
+template<> Object *Scene::create_node<Object>()
+{
+ Object *node = new Object();
+ node->set_owner(this);
+ objects.push_back(node);
+ object_manager->tag_update(this);
+ return node;
+}
+
+template<> ParticleSystem *Scene::create_node<ParticleSystem>()
+{
+ ParticleSystem *node = new ParticleSystem();
+ node->set_owner(this);
+ particle_systems.push_back(node);
+ particle_system_manager->tag_update(this);
+ return node;
+}
+
+template<> Shader *Scene::create_node<Shader>()
+{
+ Shader *node = new Shader();
+ node->set_owner(this);
+ shaders.push_back(node);
+ shader_manager->need_update = true;
+ return node;
+}
+
+template<typename T> void delete_node_from_array(vector<T> &nodes, T node)
+{
+ for (size_t i = 0; i < nodes.size(); ++i) {
+ if (nodes[i] == node) {
+ std::swap(nodes[i], nodes[nodes.size() - 1]);
+ break;
+ }
+ }
+
+ nodes.resize(nodes.size() - 1);
+ delete node;
+}
+
+template<> void Scene::delete_node_impl(Light *node)
+{
+ delete_node_from_array(lights, node);
+ light_manager->tag_update(this);
+}
+
+template<> void Scene::delete_node_impl(Mesh *node)
+{
+ delete_node_from_array(geometry, static_cast<Geometry *>(node));
+ geometry_manager->tag_update(this);
+}
+
+template<> void Scene::delete_node_impl(Hair *node)
+{
+ delete_node_from_array(geometry, static_cast<Geometry *>(node));
+ geometry_manager->tag_update(this);
+}
+
+template<> void Scene::delete_node_impl(Volume *node)
+{
+ delete_node_from_array(geometry, static_cast<Geometry *>(node));
+ geometry_manager->tag_update(this);
+}
+
+template<> void Scene::delete_node_impl(Geometry *node)
+{
+ delete_node_from_array(geometry, node);
+ geometry_manager->tag_update(this);
+}
+
+template<> void Scene::delete_node_impl(Object *node)
+{
+ delete_node_from_array(objects, node);
+ object_manager->tag_update(this);
+}
+
+template<> void Scene::delete_node_impl(ParticleSystem *node)
+{
+ delete_node_from_array(particle_systems, node);
+ particle_system_manager->tag_update(this);
+}
+
+template<> void Scene::delete_node_impl(Shader * /*node*/)
+{
+ /* don't delete unused shaders, not supported */
+}
+
CCL_NAMESPACE_END
diff --git a/intern/cycles/render/scene.h b/intern/cycles/render/scene.h
index 67616262c03..0e32a70952b 100644
--- a/intern/cycles/render/scene.h
+++ b/intern/cycles/render/scene.h
@@ -19,9 +19,11 @@
#include "bvh/bvh_params.h"
+#include "render/film.h"
#include "render/image.h"
#include "render/shader.h"
+#include "device/device.h"
#include "device/device_memory.h"
#include "util/util_param.h"
@@ -57,6 +59,7 @@ class Progress;
class BakeManager;
class BakeData;
class RenderStats;
+class Volume;
/* Scene Device Data */
@@ -210,7 +213,7 @@ class SceneParams {
/* Scene */
-class Scene {
+class Scene : public NodeOwner {
public:
/* Optional name. Is used for logging and reporting. */
string name;
@@ -229,6 +232,7 @@ class Scene {
vector<Shader *> shaders;
vector<Light *> lights;
vector<ParticleSystem *> particle_systems;
+ vector<Pass> passes;
/* data managers */
ImageManager *image_manager;
@@ -276,6 +280,42 @@ class Scene {
void collect_statistics(RenderStats *stats);
+ bool update(Progress &progress, bool &kernel_switch_needed);
+
+ /* This function is used to create a node of a specified type instead of
+ * calling 'new', and sets the scene as the owner of the node.
+ * The function has overloads that will also add the created node to the right
+ * node array (e.g. Scene::geometry for Geometry nodes) and tag the appropriate
+ * manager for an update.
+ */
+ template<typename T, typename... Args> T *create_node(Args &&... args)
+ {
+ T *node = new T(args...);
+ node->set_owner(this);
+ return node;
+ }
+
+ /* This function is used to delete a node from the scene instead of calling 'delete'
+ * and manually removing the node from the data array. It also tags the
+ * appropriate manager for an update, if any, and checks that the scene is indeed
+ * the owner of the node. Calling this function on a node not owned by the scene
+ * will likely cause a crash which we want in order to detect such cases.
+ */
+ template<typename T> void delete_node(T *node)
+ {
+ assert(node->get_owner() == this);
+ delete_node_impl(node);
+ }
+
+ /* Same as above, but specify the actual owner.
+ */
+ template<typename T> void delete_node(T *node, const NodeOwner *owner)
+ {
+ assert(node->get_owner() == owner);
+ delete_node_impl(node);
+ (void)owner;
+ }
+
protected:
/* Check if some heavy data worth logging was updated.
* Mainly used to suppress extra annoying logging.
@@ -283,8 +323,58 @@ class Scene {
bool need_data_update();
void free_memory(bool final);
+
+ bool kernels_loaded;
+ DeviceRequestedFeatures loaded_kernel_features;
+
+ bool load_kernels(Progress &progress, bool lock_scene = true);
+
+ /* ** Split kernel routines ** */
+
+ DeviceRequestedFeatures get_requested_device_features();
+
+ /* Maximumnumber of closure during session lifetime. */
+ int max_closure_global;
+
+ /* Get maximum number of closures to be used in kernel. */
+ int get_max_closure_count();
+
+ template<typename T> void delete_node_impl(T *node)
+ {
+ delete node;
+ }
};
+template<> Light *Scene::create_node<Light>();
+
+template<> Mesh *Scene::create_node<Mesh>();
+
+template<> Object *Scene::create_node<Object>();
+
+template<> Hair *Scene::create_node<Hair>();
+
+template<> Volume *Scene::create_node<Volume>();
+
+template<> ParticleSystem *Scene::create_node<ParticleSystem>();
+
+template<> Shader *Scene::create_node<Shader>();
+
+template<> void Scene::delete_node_impl(Light *node);
+
+template<> void Scene::delete_node_impl(Mesh *node);
+
+template<> void Scene::delete_node_impl(Volume *node);
+
+template<> void Scene::delete_node_impl(Hair *node);
+
+template<> void Scene::delete_node_impl(Geometry *node);
+
+template<> void Scene::delete_node_impl(Object *node);
+
+template<> void Scene::delete_node_impl(ParticleSystem *node);
+
+template<> void Scene::delete_node_impl(Shader *node);
+
CCL_NAMESPACE_END
#endif /* __SCENE_H__ */
diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp
index 70c4214c684..c22e29043d3 100644
--- a/intern/cycles/render/session.cpp
+++ b/intern/cycles/render/session.cpp
@@ -90,10 +90,6 @@ Session::Session(const SessionParams &params_)
gpu_draw_ready = false;
gpu_need_display_buffer_update = false;
pause = false;
- kernels_loaded = false;
-
- /* TODO(sergey): Check if it's indeed optimal value for the split kernel. */
- max_closure_global = 1;
}
Session::~Session()
@@ -767,95 +763,6 @@ void Session::run_cpu()
update_progressive_refine(true);
}
-DeviceRequestedFeatures Session::get_requested_device_features()
-{
- /* TODO(sergey): Consider moving this to the Scene level. */
- DeviceRequestedFeatures requested_features;
- requested_features.experimental = params.experimental;
-
- scene->shader_manager->get_requested_features(scene, &requested_features);
-
- /* This features are not being tweaked as often as shaders,
- * so could be done selective magic for the viewport as well.
- */
- bool use_motion = scene->need_motion() == Scene::MotionType::MOTION_BLUR;
- requested_features.use_hair = false;
- requested_features.use_hair_thick = (scene->params.hair_shape == CURVE_THICK);
- requested_features.use_object_motion = false;
- requested_features.use_camera_motion = use_motion && scene->camera->use_motion();
- foreach (Object *object, scene->objects) {
- Geometry *geom = object->geometry;
- if (use_motion) {
- requested_features.use_object_motion |= object->use_motion() | geom->use_motion_blur;
- requested_features.use_camera_motion |= geom->use_motion_blur;
- }
- if (object->is_shadow_catcher) {
- requested_features.use_shadow_tricks = true;
- }
- if (geom->type == Geometry::MESH) {
- Mesh *mesh = static_cast<Mesh *>(geom);
-#ifdef WITH_OPENSUBDIV
- if (mesh->subdivision_type != Mesh::SUBDIVISION_NONE) {
- requested_features.use_patch_evaluation = true;
- }
-#endif
- requested_features.use_true_displacement |= mesh->has_true_displacement();
- }
- else if (geom->type == Geometry::HAIR) {
- requested_features.use_hair = true;
- }
- }
-
- requested_features.use_background_light = scene->light_manager->has_background_light(scene);
-
- BakeManager *bake_manager = scene->bake_manager;
- requested_features.use_baking = bake_manager->get_baking();
- requested_features.use_integrator_branched = (scene->integrator->method ==
- Integrator::BRANCHED_PATH);
- if (params.denoising.use || params.denoising.store_passes) {
- requested_features.use_denoising = true;
- requested_features.use_shadow_tricks = true;
- }
-
- return requested_features;
-}
-
-bool Session::load_kernels(bool lock_scene)
-{
- thread_scoped_lock scene_lock;
- if (lock_scene) {
- scene_lock = thread_scoped_lock(scene->mutex);
- }
-
- DeviceRequestedFeatures requested_features = get_requested_device_features();
-
- if (!kernels_loaded || loaded_kernel_features.modified(requested_features)) {
- progress.set_status("Loading render kernels (may take a few minutes the first time)");
-
- scoped_timer timer;
-
- VLOG(2) << "Requested features:\n" << requested_features;
- if (!device->load_kernels(requested_features)) {
- string message = device->error_message();
- if (message.empty())
- message = "Failed loading render kernel, see console for errors";
-
- progress.set_error(message);
- progress.set_status(message);
- progress.set_update();
- return false;
- }
-
- progress.add_skip_time(timer, false);
- VLOG(1) << "Total time spent loading kernels: " << time_dt() - timer.get_start();
-
- kernels_loaded = true;
- loaded_kernel_features = requested_features;
- return true;
- }
- return false;
-}
-
void Session::run()
{
if (params.use_profiling && (params.device.type == DEVICE_CPU)) {
@@ -1032,39 +939,8 @@ bool Session::update_scene()
}
}
- /* update scene */
- if (scene->need_update()) {
- /* Updated used shader tag so we know which features are need for the kernel. */
- scene->shader_manager->update_shaders_used(scene);
-
- /* Update max_closures. */
- KernelIntegrator *kintegrator = &scene->dscene.data.integrator;
- if (params.background) {
- kintegrator->max_closures = get_max_closure_count();
- }
- else {
- /* Currently viewport render is faster with higher max_closures, needs investigating. */
- kintegrator->max_closures = MAX_CLOSURE;
- }
-
- /* Load render kernels, before device update where we upload data to the GPU. */
- bool new_kernels_needed = load_kernels(false);
-
- progress.set_status("Updating Scene");
- MEM_GUARDED_CALL(&progress, scene->device_update, device, progress);
-
- DeviceKernelStatus kernel_switch_status = device->get_active_kernel_switch_state();
- bool kernel_switch_needed = kernel_switch_status == DEVICE_KERNEL_FEATURE_KERNEL_AVAILABLE ||
- kernel_switch_status == DEVICE_KERNEL_FEATURE_KERNEL_INVALID;
- if (kernel_switch_status == DEVICE_KERNEL_WAITING_FOR_FEATURE_KERNEL) {
- progress.set_kernel_status("Compiling render kernels");
- }
- if (new_kernels_needed || kernel_switch_needed) {
- progress.set_kernel_status("Compiling render kernels");
- device->wait_for_availability(loaded_kernel_features);
- progress.set_kernel_status("");
- }
-
+ bool kernel_switch_needed = false;
+ if (scene->update(progress, kernel_switch_needed)) {
if (kernel_switch_needed) {
reset(tile_manager.params, params.samples);
}
@@ -1334,36 +1210,4 @@ void Session::collect_statistics(RenderStats *render_stats)
}
}
-int Session::get_max_closure_count()
-{
- if (scene->shader_manager->use_osl()) {
- /* OSL always needs the maximum as we can't predict the
- * number of closures a shader might generate. */
- return MAX_CLOSURE;
- }
-
- int max_closures = 0;
- for (int i = 0; i < scene->shaders.size(); i++) {
- Shader *shader = scene->shaders[i];
- if (shader->used) {
- int num_closures = shader->graph->get_num_closures();
- max_closures = max(max_closures, num_closures);
- }
- }
- max_closure_global = max(max_closure_global, max_closures);
-
- if (max_closure_global > MAX_CLOSURE) {
- /* This is usually harmless as more complex shader tend to get many
- * closures discarded due to mixing or low weights. We need to limit
- * to MAX_CLOSURE as this is hardcoded in CPU/mega kernels, and it
- * avoids excessive memory usage for split kernels. */
- VLOG(2) << "Maximum number of closures exceeded: " << max_closure_global << " > "
- << MAX_CLOSURE;
-
- max_closure_global = MAX_CLOSURE;
- }
-
- return max_closure_global;
-}
-
CCL_NAMESPACE_END
diff --git a/intern/cycles/render/session.h b/intern/cycles/render/session.h
index e3ac054ead3..a22bf7731ae 100644
--- a/intern/cycles/render/session.h
+++ b/intern/cycles/render/session.h
@@ -157,7 +157,6 @@ class Session {
void set_denoising_start_sample(int sample);
bool update_scene();
- bool load_kernels(bool lock_scene = true);
void device_free();
@@ -219,25 +218,12 @@ class Session {
thread_mutex display_mutex;
thread_condition_variable denoising_cond;
- bool kernels_loaded;
- DeviceRequestedFeatures loaded_kernel_features;
-
double reset_time;
double last_update_time;
double last_display_time;
/* progressive refine */
bool update_progressive_refine(bool cancel);
-
- DeviceRequestedFeatures get_requested_device_features();
-
- /* ** Split kernel routines ** */
-
- /* Maximumnumber of closure during session lifetime. */
- int max_closure_global;
-
- /* Get maximum number of closures to be used in kernel. */
- int get_max_closure_count();
};
CCL_NAMESPACE_END
diff --git a/intern/cycles/render/shader.cpp b/intern/cycles/render/shader.cpp
index 1120d909e98..135d0dc962b 100644
--- a/intern/cycles/render/shader.cpp
+++ b/intern/cycles/render/shader.cpp
@@ -624,16 +624,15 @@ void ShaderManager::add_default(Scene *scene)
{
ShaderGraph *graph = new ShaderGraph();
- DiffuseBsdfNode *diffuse = new DiffuseBsdfNode();
+ DiffuseBsdfNode *diffuse = graph->create_node<DiffuseBsdfNode>();
diffuse->color = make_float3(0.8f, 0.8f, 0.8f);
graph->add(diffuse);
graph->connect(diffuse->output("BSDF"), graph->output()->input("Surface"));
- Shader *shader = new Shader();
+ Shader *shader = scene->create_node<Shader>();
shader->name = "default_surface";
shader->set_graph(graph);
- scene->shaders.push_back(shader);
scene->default_surface = shader;
shader->tag_update(scene);
}
@@ -642,15 +641,14 @@ void ShaderManager::add_default(Scene *scene)
{
ShaderGraph *graph = new ShaderGraph();
- PrincipledVolumeNode *principled = new PrincipledVolumeNode();
+ PrincipledVolumeNode *principled = graph->create_node<PrincipledVolumeNode>();
graph->add(principled);
graph->connect(principled->output("Volume"), graph->output()->input("Volume"));
- Shader *shader = new Shader();
+ Shader *shader = scene->create_node<Shader>();
shader->name = "default_volume";
shader->set_graph(graph);
- scene->shaders.push_back(shader);
scene->default_volume = shader;
shader->tag_update(scene);
}
@@ -659,17 +657,16 @@ void ShaderManager::add_default(Scene *scene)
{
ShaderGraph *graph = new ShaderGraph();
- EmissionNode *emission = new EmissionNode();
+ EmissionNode *emission = graph->create_node<EmissionNode>();
emission->color = make_float3(0.8f, 0.8f, 0.8f);
emission->strength = 0.0f;
graph->add(emission);
graph->connect(emission->output("Emission"), graph->output()->input("Surface"));
- Shader *shader = new Shader();
+ Shader *shader = scene->create_node<Shader>();
shader->name = "default_light";
shader->set_graph(graph);
- scene->shaders.push_back(shader);
scene->default_light = shader;
shader->tag_update(scene);
}
@@ -678,10 +675,9 @@ void ShaderManager::add_default(Scene *scene)
{
ShaderGraph *graph = new ShaderGraph();
- Shader *shader = new Shader();
+ Shader *shader = scene->create_node<Shader>();
shader->name = "default_background";
shader->set_graph(graph);
- scene->shaders.push_back(shader);
scene->default_background = shader;
shader->tag_update(scene);
}
@@ -690,10 +686,9 @@ void ShaderManager::add_default(Scene *scene)
{
ShaderGraph *graph = new ShaderGraph();
- Shader *shader = new Shader();
+ Shader *shader = scene->create_node<Shader>();
shader->name = "default_empty";
shader->set_graph(graph);
- scene->shaders.push_back(shader);
scene->default_empty = shader;
shader->tag_update(scene);
}
diff --git a/intern/cycles/render/mesh_volume.cpp b/intern/cycles/render/volume.cpp
index 70189ea4812..89777d8669b 100644
--- a/intern/cycles/render/mesh_volume.cpp
+++ b/intern/cycles/render/volume.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2011-2016 Blender Foundation
+ * Copyright 2020 Blender Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,9 +14,9 @@
* limitations under the License.
*/
+#include "render/volume.h"
#include "render/attribute.h"
#include "render/image_vdb.h"
-#include "render/mesh.h"
#include "render/scene.h"
#ifdef WITH_OPENVDB
@@ -34,6 +34,33 @@
CCL_NAMESPACE_BEGIN
+NODE_DEFINE(Volume)
+{
+ NodeType *type = NodeType::add("volume", create, NodeType::NONE, Geometry::node_base_type);
+
+ SOCKET_INT_ARRAY(triangles, "Triangles", array<int>());
+ SOCKET_POINT_ARRAY(verts, "Vertices", array<float3>());
+ SOCKET_INT_ARRAY(shader, "Shader", array<int>());
+
+ SOCKET_FLOAT(clipping, "Clipping", 0.001f);
+ SOCKET_FLOAT(step_size, "Step Size", 0.0f);
+ SOCKET_BOOLEAN(object_space, "Object Space", false);
+
+ return type;
+}
+
+Volume::Volume() : Mesh(node_type, Geometry::VOLUME)
+{
+ clipping = 0.001f;
+ step_size = 0.0f;
+ object_space = false;
+}
+
+void Volume::clear()
+{
+ Mesh::clear(true);
+}
+
struct QuadData {
int v0, v1, v2, v3;
@@ -159,7 +186,7 @@ class VolumeMeshBuilder {
bool empty_grid() const;
#ifdef WITH_OPENVDB
- template <typename GridType>
+ template<typename GridType>
void merge_grid(openvdb::GridBase::ConstPtr grid, bool do_clipping, float volume_clipping)
{
typename GridType::ConstPtr typed_grid = openvdb::gridConstPtrCast<GridType>(grid);
@@ -189,7 +216,9 @@ VolumeMeshBuilder::VolumeMeshBuilder()
}
#ifdef WITH_OPENVDB
-void VolumeMeshBuilder::add_grid(openvdb::GridBase::ConstPtr grid, bool do_clipping, float volume_clipping)
+void VolumeMeshBuilder::add_grid(openvdb::GridBase::ConstPtr grid,
+ bool do_clipping,
+ float volume_clipping)
{
/* set the transform of our grid from the first one */
if (first_grid) {
@@ -415,22 +444,38 @@ static openvdb::GridBase::ConstPtr openvdb_grid_from_device_texture(device_textu
typename GridType::Ptr sparse = GridType::create(ValueType(0.0f));
openvdb::tools::copyFromDense(dense, *sparse, ValueType(volume_clipping));
- /* copyFromDense will remove any leaf node that contains constant data and replace it with a tile,
- * however, we need to preserve the leaves in order to generate the mesh, so revoxelize the leaves
- * that were pruned. This should not affect areas that were skipped due to the volume_clipping parameter. */
+ /* #copyFromDense will remove any leaf node that contains constant data and replace it with a
+ * tile, however, we need to preserve the leaves in order to generate the mesh, so re-voxelize
+ * the leaves that were pruned. This should not affect areas that were skipped due to the
+ * volume_clipping parameter. */
sparse->tree().voxelizeActiveTiles();
/* Compute index to world matrix. */
- float3 voxel_size = make_float3(1.0f / image_memory->data_width, 1.0f / image_memory->data_height, 1.0f / image_memory->data_depth);
+ float3 voxel_size = make_float3(1.0f / image_memory->data_width,
+ 1.0f / image_memory->data_height,
+ 1.0f / image_memory->data_depth);
transform_3d = transform_inverse(transform_3d);
- openvdb::Mat4R index_to_world_mat((double)(voxel_size.x * transform_3d[0][0]), 0.0, 0.0, 0.0,
- 0.0, (double)(voxel_size.y * transform_3d[1][1]), 0.0, 0.0,
- 0.0, 0.0, (double)(voxel_size.z * transform_3d[2][2]), 0.0,
- (double)transform_3d[0][3], (double)transform_3d[1][3], (double)transform_3d[2][3], 1.0);
-
- openvdb::math::Transform::Ptr index_to_world_tfm = openvdb::math::Transform::createLinearTransform(index_to_world_mat);
+ openvdb::Mat4R index_to_world_mat((double)(voxel_size.x * transform_3d[0][0]),
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ (double)(voxel_size.y * transform_3d[1][1]),
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ (double)(voxel_size.z * transform_3d[2][2]),
+ 0.0,
+ (double)transform_3d[0][3],
+ (double)transform_3d[1][3],
+ (double)transform_3d[2][3],
+ 1.0);
+
+ openvdb::math::Transform::Ptr index_to_world_tfm =
+ openvdb::math::Transform::createLinearTransform(index_to_world_mat);
sparse->setTransform(index_to_world_tfm);
@@ -440,15 +485,15 @@ static openvdb::GridBase::ConstPtr openvdb_grid_from_device_texture(device_textu
/* ************************************************************************** */
-void GeometryManager::create_volume_mesh(Mesh *mesh, Progress &progress)
+void GeometryManager::create_volume_mesh(Volume *volume, Progress &progress)
{
- string msg = string_printf("Computing Volume Mesh %s", mesh->name.c_str());
+ string msg = string_printf("Computing Volume Mesh %s", volume->name.c_str());
progress.set_status("Updating Mesh", msg);
VolumeMeshBuilder builder;
#ifdef WITH_OPENVDB
- foreach (Attribute &attr, mesh->attributes.attributes) {
+ foreach (Attribute &attr, volume->attributes.attributes) {
if (attr.element != ATTR_ELEMENT_VOXEL) {
continue;
}
@@ -472,24 +517,21 @@ void GeometryManager::create_volume_mesh(Mesh *mesh, Progress &progress)
device_texture *image_memory = handle.image_memory();
if (image_memory->data_elements == 1) {
- grid = openvdb_grid_from_device_texture<openvdb::FloatGrid>(image_memory,
- mesh->volume_clipping,
- handle.metadata().transform_3d);
+ grid = openvdb_grid_from_device_texture<openvdb::FloatGrid>(
+ image_memory, volume->clipping, handle.metadata().transform_3d);
}
else if (image_memory->data_elements == 3) {
- grid = openvdb_grid_from_device_texture<openvdb::Vec3fGrid>(image_memory,
- mesh->volume_clipping,
- handle.metadata().transform_3d);
+ grid = openvdb_grid_from_device_texture<openvdb::Vec3fGrid>(
+ image_memory, volume->clipping, handle.metadata().transform_3d);
}
else if (image_memory->data_elements == 4) {
- grid = openvdb_grid_from_device_texture<openvdb::Vec4fGrid>(image_memory,
- mesh->volume_clipping,
- handle.metadata().transform_3d);
+ grid = openvdb_grid_from_device_texture<openvdb::Vec4fGrid>(
+ image_memory, volume->clipping, handle.metadata().transform_3d);
}
}
if (grid) {
- builder.add_grid(grid, do_clipping, mesh->volume_clipping);
+ builder.add_grid(grid, do_clipping, volume->clipping);
}
}
#endif
@@ -502,7 +544,7 @@ void GeometryManager::create_volume_mesh(Mesh *mesh, Progress &progress)
Shader *volume_shader = NULL;
int pad_size = 0;
- foreach (Shader *shader, mesh->used_shaders) {
+ foreach (Shader *shader, volume->used_shaders) {
if (!shader->has_volume) {
continue;
}
@@ -529,7 +571,8 @@ void GeometryManager::create_volume_mesh(Mesh *mesh, Progress &progress)
* volumes or meshes. The proper solution would be to improve intersection in
* the kernel to support robust handling of multiple overlapping faces or use
* an all-hit intersection similar to shadows. */
- const float face_overlap_avoidance = 0.1f * hash_uint_to_float(hash_string(mesh->name.c_str()));
+ const float face_overlap_avoidance = 0.1f *
+ hash_uint_to_float(hash_string(volume->name.c_str()));
/* Create mesh. */
vector<float3> vertices;
@@ -537,19 +580,20 @@ void GeometryManager::create_volume_mesh(Mesh *mesh, Progress &progress)
vector<float3> face_normals;
builder.create_mesh(vertices, indices, face_normals, face_overlap_avoidance);
- mesh->clear(true);
- mesh->reserve_mesh(vertices.size(), indices.size() / 3);
- mesh->used_shaders.push_back(volume_shader);
+ volume->clear();
+ volume->reserve_mesh(vertices.size(), indices.size() / 3);
+ volume->used_shaders.push_back(volume_shader);
+ volume->need_update_rebuild = true;
for (size_t i = 0; i < vertices.size(); ++i) {
- mesh->add_vertex(vertices[i]);
+ volume->add_vertex(vertices[i]);
}
for (size_t i = 0; i < indices.size(); i += 3) {
- mesh->add_triangle(indices[i], indices[i + 1], indices[i + 2], 0, false);
+ volume->add_triangle(indices[i], indices[i + 1], indices[i + 2], 0, false);
}
- Attribute *attr_fN = mesh->attributes.add(ATTR_STD_FACE_NORMAL);
+ Attribute *attr_fN = volume->attributes.add(ATTR_STD_FACE_NORMAL);
float3 *fN = attr_fN->data_float3();
for (size_t i = 0; i < face_normals.size(); ++i) {
diff --git a/intern/cycles/render/volume.h b/intern/cycles/render/volume.h
new file mode 100644
index 00000000000..05157eb948f
--- /dev/null
+++ b/intern/cycles/render/volume.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2020 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "graph/node.h"
+
+#include "render/mesh.h"
+
+CCL_NAMESPACE_BEGIN
+
+class Volume : public Mesh {
+ public:
+ NODE_DECLARE
+
+ Volume();
+
+ float clipping;
+ float step_size;
+ bool object_space;
+
+ virtual void clear() override;
+};
+
+CCL_NAMESPACE_END
diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h
index f23742a9166..2046b55c97f 100644
--- a/intern/ghost/GHOST_C-api.h
+++ b/intern/ghost/GHOST_C-api.h
@@ -202,7 +202,8 @@ extern GHOST_WindowHandle GHOST_CreateDialogWindow(GHOST_SystemHandle systemhand
* \param platform_support_callback An optional callback to check platform support
* \return A handle to the new context ( == NULL if creation failed).
*/
-extern GHOST_ContextHandle GHOST_CreateOpenGLContext(GHOST_SystemHandle systemhandle);
+extern GHOST_ContextHandle GHOST_CreateOpenGLContext(GHOST_SystemHandle systemhandle,
+ GHOST_GLSettings glSettings);
/**
* Dispose of a context.
diff --git a/intern/ghost/GHOST_ISystem.h b/intern/ghost/GHOST_ISystem.h
index 04e9d5e4e14..63018a9cee8 100644
--- a/intern/ghost/GHOST_ISystem.h
+++ b/intern/ghost/GHOST_ISystem.h
@@ -265,7 +265,7 @@ class GHOST_ISystem {
* Never explicitly delete the context, use disposeContext() instead.
* \return The new context (or 0 if creation failed).
*/
- virtual GHOST_IContext *createOffscreenContext() = 0;
+ virtual GHOST_IContext *createOffscreenContext(GHOST_GLSettings glSettings) = 0;
/**
* Dispose of a context.
diff --git a/intern/ghost/intern/GHOST_C-api.cpp b/intern/ghost/intern/GHOST_C-api.cpp
index 843684b6d2e..e4bb908fec8 100644
--- a/intern/ghost/intern/GHOST_C-api.cpp
+++ b/intern/ghost/intern/GHOST_C-api.cpp
@@ -135,11 +135,12 @@ void GHOST_GetAllDisplayDimensions(GHOST_SystemHandle systemhandle,
system->getAllDisplayDimensions(*width, *height);
}
-GHOST_ContextHandle GHOST_CreateOpenGLContext(GHOST_SystemHandle systemhandle)
+GHOST_ContextHandle GHOST_CreateOpenGLContext(GHOST_SystemHandle systemhandle,
+ GHOST_GLSettings glSettings)
{
GHOST_ISystem *system = (GHOST_ISystem *)systemhandle;
- return (GHOST_ContextHandle)system->createOffscreenContext();
+ return (GHOST_ContextHandle)system->createOffscreenContext(glSettings);
}
GHOST_TSuccess GHOST_DisposeOpenGLContext(GHOST_SystemHandle systemhandle,
diff --git a/intern/ghost/intern/GHOST_IXrGraphicsBinding.h b/intern/ghost/intern/GHOST_IXrGraphicsBinding.h
index de8bf9f1a5a..24b42c08e8a 100644
--- a/intern/ghost/intern/GHOST_IXrGraphicsBinding.h
+++ b/intern/ghost/intern/GHOST_IXrGraphicsBinding.h
@@ -21,15 +21,13 @@
#pragma once
#include <memory>
+#include <optional>
#include <string>
#include <vector>
#include "GHOST_Xr_openxr_includes.h"
class GHOST_IXrGraphicsBinding {
- friend std::unique_ptr<GHOST_IXrGraphicsBinding> GHOST_XrGraphicsBindingCreateFromType(
- GHOST_TXrGraphicsBinding type);
-
public:
union {
#if defined(WITH_GHOST_X11)
@@ -49,18 +47,17 @@ class GHOST_IXrGraphicsBinding {
* \param r_requirement_info Return argument to retrieve an informal string on the requirements
* to be met. Useful for error/debug messages.
*/
- virtual bool checkVersionRequirements(class GHOST_Context *ghost_ctx,
+ virtual bool checkVersionRequirements(class GHOST_Context &ghost_ctx,
XrInstance instance,
XrSystemId system_id,
std::string *r_requirement_info) const = 0;
- virtual void initFromGhostContext(class GHOST_Context *ghost_ctx) = 0;
- virtual bool chooseSwapchainFormat(const std::vector<int64_t> &runtime_formats,
- int64_t &r_result,
- bool &r_is_rgb_format) const = 0;
+ virtual void initFromGhostContext(class GHOST_Context &ghost_ctx) = 0;
+ virtual std::optional<int64_t> chooseSwapchainFormat(const std::vector<int64_t> &runtime_formats,
+ bool &r_is_rgb_format) const = 0;
virtual std::vector<XrSwapchainImageBaseHeader *> createSwapchainImages(
uint32_t image_count) = 0;
- virtual void submitToSwapchainImage(XrSwapchainImageBaseHeader *swapchain_image,
- const GHOST_XrDrawViewInfo *draw_info) = 0;
+ virtual void submitToSwapchainImage(XrSwapchainImageBaseHeader &swapchain_image,
+ const GHOST_XrDrawViewInfo &draw_info) = 0;
virtual bool needsUpsideDownDrawing(GHOST_Context &ghost_ctx) const = 0;
protected:
@@ -69,4 +66,4 @@ class GHOST_IXrGraphicsBinding {
};
std::unique_ptr<GHOST_IXrGraphicsBinding> GHOST_XrGraphicsBindingCreateFromType(
- GHOST_TXrGraphicsBinding type, GHOST_Context *ghost_ctx);
+ GHOST_TXrGraphicsBinding type, GHOST_Context &ghost_ctx);
diff --git a/intern/ghost/intern/GHOST_System.h b/intern/ghost/intern/GHOST_System.h
index e29a9ba0c29..d5b23d76016 100644
--- a/intern/ghost/intern/GHOST_System.h
+++ b/intern/ghost/intern/GHOST_System.h
@@ -115,7 +115,7 @@ class GHOST_System : public GHOST_ISystem {
* Never explicitly delete the context, use disposeContext() instead.
* \return The new context (or 0 if creation failed).
*/
- virtual GHOST_IContext *createOffscreenContext() = 0;
+ virtual GHOST_IContext *createOffscreenContext(GHOST_GLSettings glSettings) = 0;
/**
* Returns whether a window is valid.
diff --git a/intern/ghost/intern/GHOST_SystemCocoa.h b/intern/ghost/intern/GHOST_SystemCocoa.h
index 8e36cebb88a..b89edf8835d 100644
--- a/intern/ghost/intern/GHOST_SystemCocoa.h
+++ b/intern/ghost/intern/GHOST_SystemCocoa.h
@@ -116,7 +116,7 @@ class GHOST_SystemCocoa : public GHOST_System {
* Never explicitly delete the context, use disposeContext() instead.
* \return The new context (or 0 if creation failed).
*/
- GHOST_IContext *createOffscreenContext();
+ GHOST_IContext *createOffscreenContext(GHOST_GLSettings glSettings);
/**
* Dispose of a context.
diff --git a/intern/ghost/intern/GHOST_SystemCocoa.mm b/intern/ghost/intern/GHOST_SystemCocoa.mm
index 5592078e20e..467f59defea 100644
--- a/intern/ghost/intern/GHOST_SystemCocoa.mm
+++ b/intern/ghost/intern/GHOST_SystemCocoa.mm
@@ -765,7 +765,7 @@ GHOST_IWindow *GHOST_SystemCocoa::createWindow(const char *title,
* Never explicitly delete the context, use #disposeContext() instead.
* \return The new context (or 0 if creation failed).
*/
-GHOST_IContext *GHOST_SystemCocoa::createOffscreenContext()
+GHOST_IContext *GHOST_SystemCocoa::createOffscreenContext(GHOST_GLSettings glSettings)
{
GHOST_Context *context = new GHOST_ContextCGL(false, NULL, NULL, NULL);
if (context->initializeDrawingContext())
diff --git a/intern/ghost/intern/GHOST_SystemNULL.h b/intern/ghost/intern/GHOST_SystemNULL.h
index 5becf110b15..faeffffed9e 100644
--- a/intern/ghost/intern/GHOST_SystemNULL.h
+++ b/intern/ghost/intern/GHOST_SystemNULL.h
@@ -81,7 +81,7 @@ class GHOST_SystemNULL : public GHOST_System {
void getAllDisplayDimensions(GHOST_TUns32 &width, GHOST_TUns32 &height) const
{ /* nop */
}
- GHOST_IContext *createOffscreenContext()
+ GHOST_IContext *createOffscreenContext(GHOST_GLSettings glSettings)
{
return NULL;
}
diff --git a/intern/ghost/intern/GHOST_SystemSDL.cpp b/intern/ghost/intern/GHOST_SystemSDL.cpp
index b32ec4306e8..1769c432d96 100644
--- a/intern/ghost/intern/GHOST_SystemSDL.cpp
+++ b/intern/ghost/intern/GHOST_SystemSDL.cpp
@@ -139,7 +139,7 @@ GHOST_TUns8 GHOST_SystemSDL::getNumDisplays() const
return SDL_GetNumVideoDisplays();
}
-GHOST_IContext *GHOST_SystemSDL::createOffscreenContext()
+GHOST_IContext *GHOST_SystemSDL::createOffscreenContext(GHOST_GLSettings glSettings)
{
GHOST_Context *context = new GHOST_ContextSDL(0,
NULL,
diff --git a/intern/ghost/intern/GHOST_SystemSDL.h b/intern/ghost/intern/GHOST_SystemSDL.h
index 4b2c52f8282..57e8d17861d 100644
--- a/intern/ghost/intern/GHOST_SystemSDL.h
+++ b/intern/ghost/intern/GHOST_SystemSDL.h
@@ -72,7 +72,7 @@ class GHOST_SystemSDL : public GHOST_System {
void getMainDisplayDimensions(GHOST_TUns32 &width, GHOST_TUns32 &height) const;
- GHOST_IContext *createOffscreenContext();
+ GHOST_IContext *createOffscreenContext(GHOST_GLSettings glSettings);
GHOST_TSuccess disposeContext(GHOST_IContext *context);
diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp
index 31110694ea6..81d9824ed26 100644
--- a/intern/ghost/intern/GHOST_SystemWayland.cpp
+++ b/intern/ghost/intern/GHOST_SystemWayland.cpp
@@ -1481,7 +1481,7 @@ void GHOST_SystemWayland::getAllDisplayDimensions(GHOST_TUns32 &width, GHOST_TUn
getMainDisplayDimensions(width, height);
}
-GHOST_IContext *GHOST_SystemWayland::createOffscreenContext()
+GHOST_IContext *GHOST_SystemWayland::createOffscreenContext(GHOST_GLSettings glSettings)
{
/* Create new off-screen window. */
wl_surface *os_surface = wl_compositor_create_surface(compositor());
diff --git a/intern/ghost/intern/GHOST_SystemWayland.h b/intern/ghost/intern/GHOST_SystemWayland.h
index 30ee7679287..10b9ef6bd62 100644
--- a/intern/ghost/intern/GHOST_SystemWayland.h
+++ b/intern/ghost/intern/GHOST_SystemWayland.h
@@ -62,7 +62,7 @@ class GHOST_SystemWayland : public GHOST_System {
void getAllDisplayDimensions(GHOST_TUns32 &width, GHOST_TUns32 &height) const override;
- GHOST_IContext *createOffscreenContext() override;
+ GHOST_IContext *createOffscreenContext(GHOST_GLSettings glSettings) override;
GHOST_TSuccess disposeContext(GHOST_IContext *context) override;
diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp
index f59b106afd6..533e51db355 100644
--- a/intern/ghost/intern/GHOST_SystemWin32.cpp
+++ b/intern/ghost/intern/GHOST_SystemWin32.cpp
@@ -328,9 +328,9 @@ GHOST_IWindow *GHOST_SystemWin32::createWindow(const char *title,
* Never explicitly delete the window, use #disposeContext() instead.
* \return The new context (or 0 if creation failed).
*/
-GHOST_IContext *GHOST_SystemWin32::createOffscreenContext()
+GHOST_IContext *GHOST_SystemWin32::createOffscreenContext(GHOST_GLSettings glSettings)
{
- bool debug_context = false; /* TODO: inform as a parameter */
+ const bool debug_context = (glSettings.flags & GHOST_glDebugContext) != 0;
GHOST_Context *context;
diff --git a/intern/ghost/intern/GHOST_SystemWin32.h b/intern/ghost/intern/GHOST_SystemWin32.h
index 24925b9c403..86916b3aeeb 100644
--- a/intern/ghost/intern/GHOST_SystemWin32.h
+++ b/intern/ghost/intern/GHOST_SystemWin32.h
@@ -142,7 +142,7 @@ class GHOST_SystemWin32 : public GHOST_System {
* Never explicitly delete the window, use disposeContext() instead.
* \return The new context (or 0 if creation failed).
*/
- GHOST_IContext *createOffscreenContext();
+ GHOST_IContext *createOffscreenContext(GHOST_GLSettings glSettings);
/**
* Dispose of a context.
diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp
index c0676618101..2a07e02706b 100644
--- a/intern/ghost/intern/GHOST_SystemX11.cpp
+++ b/intern/ghost/intern/GHOST_SystemX11.cpp
@@ -394,7 +394,7 @@ GHOST_IWindow *GHOST_SystemX11::createWindow(const char *title,
* Never explicitly delete the context, use disposeContext() instead.
* \return The new context (or 0 if creation failed).
*/
-GHOST_IContext *GHOST_SystemX11::createOffscreenContext()
+GHOST_IContext *GHOST_SystemX11::createOffscreenContext(GHOST_GLSettings glSettings)
{
// During development:
// try 4.x compatibility profile
@@ -406,6 +406,8 @@ GHOST_IContext *GHOST_SystemX11::createOffscreenContext()
// try 3.3 core profile
// no fallbacks
+ const bool debug_context = (glSettings.flags & GHOST_glDebugContext) != 0;
+
#if defined(WITH_GL_PROFILE_CORE)
{
const char *version_major = (char *)glewGetString(GLEW_VERSION_MAJOR);
@@ -446,7 +448,7 @@ GHOST_IContext *GHOST_SystemX11::createOffscreenContext()
4,
minor,
GHOST_OPENGL_EGL_CONTEXT_FLAGS |
- (false ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0),
+ (debug_context ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0),
GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY,
EGL_OPENGL_API);
#else
@@ -458,7 +460,7 @@ GHOST_IContext *GHOST_SystemX11::createOffscreenContext()
4,
minor,
GHOST_OPENGL_GLX_CONTEXT_FLAGS |
- (false ? GLX_CONTEXT_DEBUG_BIT_ARB : 0),
+ (debug_context ? GLX_CONTEXT_DEBUG_BIT_ARB : 0),
GHOST_OPENGL_GLX_RESET_NOTIFICATION_STRATEGY);
#endif
@@ -476,7 +478,7 @@ GHOST_IContext *GHOST_SystemX11::createOffscreenContext()
3,
3,
GHOST_OPENGL_EGL_CONTEXT_FLAGS |
- (false ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0),
+ (debug_context ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0),
GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY,
EGL_OPENGL_API);
#else
@@ -488,7 +490,7 @@ GHOST_IContext *GHOST_SystemX11::createOffscreenContext()
3,
3,
GHOST_OPENGL_GLX_CONTEXT_FLAGS |
- (false ? GLX_CONTEXT_DEBUG_BIT_ARB : 0),
+ (debug_context ? GLX_CONTEXT_DEBUG_BIT_ARB : 0),
GHOST_OPENGL_GLX_RESET_NOTIFICATION_STRATEGY);
#endif
diff --git a/intern/ghost/intern/GHOST_SystemX11.h b/intern/ghost/intern/GHOST_SystemX11.h
index ad58138d416..a852675acfb 100644
--- a/intern/ghost/intern/GHOST_SystemX11.h
+++ b/intern/ghost/intern/GHOST_SystemX11.h
@@ -153,7 +153,7 @@ class GHOST_SystemX11 : public GHOST_System {
* Never explicitly delete the context, use disposeContext() instead.
* \return The new context (or 0 if creation failed).
*/
- GHOST_IContext *createOffscreenContext();
+ GHOST_IContext *createOffscreenContext(GHOST_GLSettings glSettings);
/**
* Dispose of a context.
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/ghost/intern/GHOST_Xr.cpp b/intern/ghost/intern/GHOST_Xr.cpp
index dc63aac217c..2c94aaab430 100644
--- a/intern/ghost/intern/GHOST_Xr.cpp
+++ b/intern/ghost/intern/GHOST_Xr.cpp
@@ -31,7 +31,7 @@
GHOST_XrContextHandle GHOST_XrContextCreate(const GHOST_XrContextCreateInfo *create_info)
{
- GHOST_XrContext *xr_context = new GHOST_XrContext(create_info);
+ auto xr_context = std::make_unique<GHOST_XrContext>(create_info);
/* TODO GHOST_XrContext's should probably be owned by the GHOST_System, which will handle context
* creation and destruction. Try-catch logic can be moved to C-API then. */
@@ -40,12 +40,11 @@ GHOST_XrContextHandle GHOST_XrContextCreate(const GHOST_XrContextCreateInfo *cre
}
catch (GHOST_XrException &e) {
xr_context->dispatchErrorMessage(&e);
- delete xr_context;
-
return nullptr;
}
- return (GHOST_XrContextHandle)xr_context;
+ /* Give ownership to the caller. */
+ return (GHOST_XrContextHandle)xr_context.release();
}
void GHOST_XrContextDestroy(GHOST_XrContextHandle xr_contexthandle)
diff --git a/intern/ghost/intern/GHOST_XrContext.cpp b/intern/ghost/intern/GHOST_XrContext.cpp
index 16687c34679..583bda9731a 100644
--- a/intern/ghost/intern/GHOST_XrContext.cpp
+++ b/intern/ghost/intern/GHOST_XrContext.cpp
@@ -23,6 +23,7 @@
#include <cassert>
#include <sstream>
#include <string>
+#include <string_view>
#include "GHOST_Types.h"
#include "GHOST_XrException.h"
@@ -58,7 +59,7 @@ void *GHOST_XrContext::s_error_handler_customdata = nullptr;
* \{ */
GHOST_XrContext::GHOST_XrContext(const GHOST_XrContextCreateInfo *create_info)
- : m_oxr(new OpenXRInstanceData()),
+ : m_oxr(std::make_unique<OpenXRInstanceData>()),
m_debug(create_info->context_flag & GHOST_kXrContextDebug),
m_debug_time(create_info->context_flag & GHOST_kXrContextDebugTime)
{
@@ -88,18 +89,26 @@ void GHOST_XrContext::initialize(const GHOST_XrContextCreateInfo *create_info)
printAvailableAPILayersAndExtensionsInfo();
}
- m_gpu_binding_type = determineGraphicsBindingTypeToEnable(create_info);
+ /* Multiple graphics binding extensions can be enabled, but only one will actually be used
+ * (determined later on). */
+ const std::vector<GHOST_TXrGraphicsBinding> graphics_binding_types =
+ determineGraphicsBindingTypesToEnable(create_info);
assert(m_oxr->instance == XR_NULL_HANDLE);
- createOpenXRInstance();
+ createOpenXRInstance(graphics_binding_types);
storeInstanceProperties();
+
+ /* Multiple bindings may be enabled. Now that we know the runtime in use, settle for one. */
+ m_gpu_binding_type = determineGraphicsBindingTypeToUse(graphics_binding_types);
+
printInstanceInfo();
if (isDebugMode()) {
initDebugMessenger();
}
}
-void GHOST_XrContext::createOpenXRInstance()
+void GHOST_XrContext::createOpenXRInstance(
+ const std::vector<GHOST_TXrGraphicsBinding> &graphics_binding_types)
{
XrInstanceCreateInfo create_info = {XR_TYPE_INSTANCE_CREATE_INFO};
@@ -108,7 +117,7 @@ void GHOST_XrContext::createOpenXRInstance()
create_info.applicationInfo.apiVersion = XR_CURRENT_API_VERSION;
getAPILayersToEnable(m_enabled_layers);
- getExtensionsToEnable(m_enabled_extensions);
+ getExtensionsToEnable(graphics_binding_types, m_enabled_extensions);
create_info.enabledApiLayerCount = m_enabled_layers.size();
create_info.enabledApiLayerNames = m_enabled_layers.data();
create_info.enabledExtensionCount = m_enabled_extensions.size();
@@ -327,7 +336,7 @@ void GHOST_XrContext::initApiLayers()
}
}
-static bool openxr_layer_is_available(const std::vector<XrApiLayerProperties> layers_info,
+static bool openxr_layer_is_available(const std::vector<XrApiLayerProperties> &layers_info,
const std::string &layer_name)
{
for (const XrApiLayerProperties &layer_info : layers_info) {
@@ -339,8 +348,9 @@ static bool openxr_layer_is_available(const std::vector<XrApiLayerProperties> la
return false;
}
-static bool openxr_extension_is_available(const std::vector<XrExtensionProperties> extensions_info,
- const std::string &extension_name)
+static bool openxr_extension_is_available(
+ const std::vector<XrExtensionProperties> &extensions_info,
+ const std::string_view &extension_name)
{
for (const XrExtensionProperties &ext_info : extensions_info) {
if (ext_info.extensionName == extension_name) {
@@ -393,32 +403,30 @@ static const char *openxr_ext_name_from_wm_gpu_binding(GHOST_TXrGraphicsBinding
/**
* Gather an array of names for the extensions to enable.
*/
-void GHOST_XrContext::getExtensionsToEnable(std::vector<const char *> &r_ext_names)
+void GHOST_XrContext::getExtensionsToEnable(
+ const std::vector<GHOST_TXrGraphicsBinding> &graphics_binding_types,
+ std::vector<const char *> &r_ext_names)
{
- assert(m_gpu_binding_type != GHOST_kXrGraphicsUnknown);
-
- const char *gpu_binding = openxr_ext_name_from_wm_gpu_binding(m_gpu_binding_type);
- static std::vector<std::string> try_ext;
-
- try_ext.clear();
+ std::vector<std::string_view> try_ext;
/* Try enabling debug extension. */
-#ifndef WIN32
if (isDebugMode()) {
try_ext.push_back(XR_EXT_DEBUG_UTILS_EXTENSION_NAME);
}
-#endif
- r_ext_names.reserve(try_ext.size() + 1); /* + 1 for graphics binding extension. */
+ r_ext_names.reserve(try_ext.size() + graphics_binding_types.size());
- /* Add graphics binding extension. */
- assert(gpu_binding);
- assert(openxr_extension_is_available(m_oxr->extensions, gpu_binding));
- r_ext_names.push_back(gpu_binding);
+ /* Add graphics binding extensions (may be multiple ones, we'll settle for one to use later, once
+ * we have more info about the runtime). */
+ for (GHOST_TXrGraphicsBinding type : graphics_binding_types) {
+ const char *gpu_binding = openxr_ext_name_from_wm_gpu_binding(type);
+ assert(openxr_extension_is_available(m_oxr->extensions, gpu_binding));
+ r_ext_names.push_back(gpu_binding);
+ }
- for (const std::string &ext : try_ext) {
+ for (const std::string_view &ext : try_ext) {
if (openxr_extension_is_available(m_oxr->extensions, ext)) {
- r_ext_names.push_back(ext.c_str());
+ r_ext_names.push_back(ext.data());
}
}
}
@@ -427,9 +435,10 @@ void GHOST_XrContext::getExtensionsToEnable(std::vector<const char *> &r_ext_nam
* Decide which graphics binding extension to use based on
* #GHOST_XrContextCreateInfo.gpu_binding_candidates and available extensions.
*/
-GHOST_TXrGraphicsBinding GHOST_XrContext::determineGraphicsBindingTypeToEnable(
+std::vector<GHOST_TXrGraphicsBinding> GHOST_XrContext::determineGraphicsBindingTypesToEnable(
const GHOST_XrContextCreateInfo *create_info)
{
+ std::vector<GHOST_TXrGraphicsBinding> result;
assert(create_info->gpu_binding_candidates != NULL);
assert(create_info->gpu_binding_candidates_count > 0);
@@ -438,11 +447,35 @@ GHOST_TXrGraphicsBinding GHOST_XrContext::determineGraphicsBindingTypeToEnable(
const char *ext_name = openxr_ext_name_from_wm_gpu_binding(
create_info->gpu_binding_candidates[i]);
if (openxr_extension_is_available(m_oxr->extensions, ext_name)) {
- return create_info->gpu_binding_candidates[i];
+ result.push_back(create_info->gpu_binding_candidates[i]);
}
}
- return GHOST_kXrGraphicsUnknown;
+ if (result.empty()) {
+ throw GHOST_XrException("No supported graphics binding found.");
+ }
+
+ return result;
+}
+
+GHOST_TXrGraphicsBinding GHOST_XrContext::determineGraphicsBindingTypeToUse(
+ const std::vector<GHOST_TXrGraphicsBinding> &enabled_types)
+{
+ /* Return the first working type. */
+ for (GHOST_TXrGraphicsBinding type : enabled_types) {
+#ifdef WIN32
+ /* The SteamVR OpenGL backend fails currently. Disable it and allow falling back to the DirectX
+ * one. */
+ if ((m_runtime_id == OPENXR_RUNTIME_STEAMVR) && (type == GHOST_kXrGraphicsOpenGL)) {
+ continue;
+ }
+#endif
+
+ assert(type != GHOST_kXrGraphicsUnknown);
+ return type;
+ }
+
+ throw GHOST_XrException("Failed to determine a graphics binding to use.");
}
/** \} */ /* OpenXR API-Layers and Extensions */
@@ -459,7 +492,7 @@ void GHOST_XrContext::startSession(const GHOST_XrSessionBeginInfo *begin_info)
m_custom_funcs.session_exit_customdata = begin_info->exit_customdata;
if (m_session == nullptr) {
- m_session = std::unique_ptr<GHOST_XrSession>(new GHOST_XrSession(this));
+ m_session = std::make_unique<GHOST_XrSession>(*this);
}
m_session->start(begin_info);
}
@@ -489,7 +522,7 @@ void GHOST_XrContext::drawSessionViews(void *draw_customdata)
/**
* Delegates event to session, allowing context to destruct the session if needed.
*/
-void GHOST_XrContext::handleSessionStateChange(const XrEventDataSessionStateChanged *lifecycle)
+void GHOST_XrContext::handleSessionStateChange(const XrEventDataSessionStateChanged &lifecycle)
{
if (m_session &&
m_session->handleStateChangeEvent(lifecycle) == GHOST_XrSession::SESSION_DESTROY) {
diff --git a/intern/ghost/intern/GHOST_XrContext.h b/intern/ghost/intern/GHOST_XrContext.h
index d2edb40c080..59c7786ed7a 100644
--- a/intern/ghost/intern/GHOST_XrContext.h
+++ b/intern/ghost/intern/GHOST_XrContext.h
@@ -80,7 +80,7 @@ class GHOST_XrContext : public GHOST_IXrContext {
void setDrawViewFunc(GHOST_XrDrawViewFn draw_view_fn) override;
bool needsUpsideDownDrawing() const override;
- void handleSessionStateChange(const XrEventDataSessionStateChanged *lifecycle);
+ void handleSessionStateChange(const XrEventDataSessionStateChanged &lifecycle);
GHOST_TXrOpenXRRuntimeID getOpenXRRuntimeID() const;
const GHOST_XrCustomFuncs &getCustomFuncs() const;
@@ -114,7 +114,7 @@ class GHOST_XrContext : public GHOST_IXrContext {
bool m_debug = false;
bool m_debug_time = false;
- void createOpenXRInstance();
+ void createOpenXRInstance(const std::vector<GHOST_TXrGraphicsBinding> &graphics_binding_types);
void storeInstanceProperties();
void initDebugMessenger();
@@ -126,7 +126,10 @@ class GHOST_XrContext : public GHOST_IXrContext {
void initExtensions();
void initExtensionsEx(std::vector<XrExtensionProperties> &extensions, const char *layer_name);
void getAPILayersToEnable(std::vector<const char *> &r_ext_names);
- void getExtensionsToEnable(std::vector<const char *> &r_ext_names);
- GHOST_TXrGraphicsBinding determineGraphicsBindingTypeToEnable(
+ void getExtensionsToEnable(const std::vector<GHOST_TXrGraphicsBinding> &graphics_binding_types,
+ std::vector<const char *> &r_ext_names);
+ std::vector<GHOST_TXrGraphicsBinding> determineGraphicsBindingTypesToEnable(
const GHOST_XrContextCreateInfo *create_info);
+ GHOST_TXrGraphicsBinding determineGraphicsBindingTypeToUse(
+ const std::vector<GHOST_TXrGraphicsBinding> &enabled_types);
};
diff --git a/intern/ghost/intern/GHOST_XrEvent.cpp b/intern/ghost/intern/GHOST_XrEvent.cpp
index 30005055f9b..992f89fadeb 100644
--- a/intern/ghost/intern/GHOST_XrEvent.cpp
+++ b/intern/ghost/intern/GHOST_XrEvent.cpp
@@ -35,25 +35,25 @@ static bool GHOST_XrEventPollNext(XrInstance instance, XrEventDataBuffer &r_even
GHOST_TSuccess GHOST_XrEventsHandle(GHOST_XrContextHandle xr_contexthandle)
{
- GHOST_XrContext *xr_context = (GHOST_XrContext *)xr_contexthandle;
- XrEventDataBuffer event_buffer; /* Structure big enough to hold all possible events. */
-
- if (xr_context == NULL) {
+ if (xr_contexthandle == nullptr) {
return GHOST_kFailure;
}
- while (GHOST_XrEventPollNext(xr_context->getInstance(), event_buffer)) {
+ GHOST_XrContext &xr_context = *(GHOST_XrContext *)xr_contexthandle;
+ XrEventDataBuffer event_buffer; /* Structure big enough to hold all possible events. */
+
+ while (GHOST_XrEventPollNext(xr_context.getInstance(), event_buffer)) {
XrEventDataBaseHeader *event = (XrEventDataBaseHeader *)&event_buffer;
switch (event->type) {
case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED:
- xr_context->handleSessionStateChange((XrEventDataSessionStateChanged *)event);
+ xr_context.handleSessionStateChange((XrEventDataSessionStateChanged &)*event);
return GHOST_kSuccess;
case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING:
GHOST_XrContextDestroy(xr_contexthandle);
return GHOST_kSuccess;
default:
- if (xr_context->isDebugMode()) {
+ if (xr_context.isDebugMode()) {
printf("Unhandled event: %i\n", event->type);
}
return GHOST_kFailure;
diff --git a/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp b/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp
index 7d7405a974d..39b7f0776e1 100644
--- a/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp
+++ b/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp
@@ -34,12 +34,11 @@
#include "GHOST_IXrGraphicsBinding.h"
-static bool choose_swapchain_format_from_candidates(std::vector<int64_t> gpu_binding_formats,
- std::vector<int64_t> runtime_formats,
- int64_t &r_result)
+static std::optional<int64_t> choose_swapchain_format_from_candidates(
+ const std::vector<int64_t> &gpu_binding_formats, const std::vector<int64_t> &runtime_formats)
{
if (gpu_binding_formats.empty()) {
- return false;
+ return std::nullopt;
}
auto res = std::find_first_of(gpu_binding_formats.begin(),
@@ -47,11 +46,10 @@ static bool choose_swapchain_format_from_candidates(std::vector<int64_t> gpu_bin
runtime_formats.begin(),
runtime_formats.end());
if (res == gpu_binding_formats.end()) {
- return false;
+ return std::nullopt;
}
- r_result = *res;
- return true;
+ return *res;
}
class GHOST_XrGraphicsBindingOpenGL : public GHOST_IXrGraphicsBinding {
@@ -63,21 +61,21 @@ class GHOST_XrGraphicsBindingOpenGL : public GHOST_IXrGraphicsBinding {
}
}
- bool checkVersionRequirements(GHOST_Context *ghost_ctx,
+ bool checkVersionRequirements(GHOST_Context &ghost_ctx,
XrInstance instance,
XrSystemId system_id,
std::string *r_requirement_info) const override
{
#if defined(WITH_GHOST_X11)
- GHOST_ContextGLX *ctx_gl = static_cast<GHOST_ContextGLX *>(ghost_ctx);
+ GHOST_ContextGLX &ctx_gl = static_cast<GHOST_ContextGLX &>(ghost_ctx);
#else
- GHOST_ContextWGL *ctx_gl = static_cast<GHOST_ContextWGL *>(ghost_ctx);
+ GHOST_ContextWGL &ctx_gl = static_cast<GHOST_ContextWGL &>(ghost_ctx);
#endif
static PFN_xrGetOpenGLGraphicsRequirementsKHR s_xrGetOpenGLGraphicsRequirementsKHR_fn =
nullptr;
XrGraphicsRequirementsOpenGLKHR gpu_requirements = {XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR};
const XrVersion gl_version = XR_MAKE_VERSION(
- ctx_gl->m_contextMajorVersion, ctx_gl->m_contextMinorVersion, 0);
+ ctx_gl.m_contextMajorVersion, ctx_gl.m_contextMinorVersion, 0);
if (!s_xrGetOpenGLGraphicsRequirementsKHR_fn &&
XR_FAILED(xrGetInstanceProcAddr(
@@ -105,47 +103,45 @@ class GHOST_XrGraphicsBindingOpenGL : public GHOST_IXrGraphicsBinding {
(gl_version <= gpu_requirements.maxApiVersionSupported);
}
- void initFromGhostContext(GHOST_Context *ghost_ctx) override
+ void initFromGhostContext(GHOST_Context &ghost_ctx) override
{
#if defined(WITH_GHOST_X11)
- GHOST_ContextGLX *ctx_glx = static_cast<GHOST_ContextGLX *>(ghost_ctx);
- XVisualInfo *visual_info = glXGetVisualFromFBConfig(ctx_glx->m_display, ctx_glx->m_fbconfig);
+ GHOST_ContextGLX &ctx_glx = static_cast<GHOST_ContextGLX &>(ghost_ctx);
+ XVisualInfo *visual_info = glXGetVisualFromFBConfig(ctx_glx.m_display, ctx_glx.m_fbconfig);
oxr_binding.glx.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_XLIB_KHR;
- oxr_binding.glx.xDisplay = ctx_glx->m_display;
- oxr_binding.glx.glxFBConfig = ctx_glx->m_fbconfig;
- oxr_binding.glx.glxDrawable = ctx_glx->m_window;
- oxr_binding.glx.glxContext = ctx_glx->m_context;
+ oxr_binding.glx.xDisplay = ctx_glx.m_display;
+ oxr_binding.glx.glxFBConfig = ctx_glx.m_fbconfig;
+ oxr_binding.glx.glxDrawable = ctx_glx.m_window;
+ oxr_binding.glx.glxContext = ctx_glx.m_context;
oxr_binding.glx.visualid = visual_info->visualid;
XFree(visual_info);
#elif defined(WIN32)
- GHOST_ContextWGL *ctx_wgl = static_cast<GHOST_ContextWGL *>(ghost_ctx);
+ GHOST_ContextWGL &ctx_wgl = static_cast<GHOST_ContextWGL &>(ghost_ctx);
oxr_binding.wgl.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR;
- oxr_binding.wgl.hDC = ctx_wgl->m_hDC;
- oxr_binding.wgl.hGLRC = ctx_wgl->m_hGLRC;
+ oxr_binding.wgl.hDC = ctx_wgl.m_hDC;
+ oxr_binding.wgl.hGLRC = ctx_wgl.m_hGLRC;
#endif
/* Generate a framebuffer to use for blitting into the texture. */
glGenFramebuffers(1, &m_fbo);
}
- bool chooseSwapchainFormat(const std::vector<int64_t> &runtime_formats,
- int64_t &r_result,
- bool &r_is_srgb_format) const override
+ std::optional<int64_t> chooseSwapchainFormat(const std::vector<int64_t> &runtime_formats,
+ bool &r_is_srgb_format) const override
{
std::vector<int64_t> gpu_binding_formats = {
GL_RGBA8,
GL_SRGB8_ALPHA8,
};
- if (choose_swapchain_format_from_candidates(gpu_binding_formats, runtime_formats, r_result)) {
- r_is_srgb_format = (r_result == GL_SRGB8_ALPHA8);
- return true;
- }
+ std::optional result = choose_swapchain_format_from_candidates(gpu_binding_formats,
+ runtime_formats);
+ r_is_srgb_format = result ? (*result == GL_SRGB8_ALPHA8) : false;
- return false;
+ return result;
}
std::vector<XrSwapchainImageBaseHeader *> createSwapchainImages(uint32_t image_count) override
@@ -166,25 +162,25 @@ class GHOST_XrGraphicsBindingOpenGL : public GHOST_IXrGraphicsBinding {
return base_images;
}
- void submitToSwapchainImage(XrSwapchainImageBaseHeader *swapchain_image,
- const GHOST_XrDrawViewInfo *draw_info) override
+ void submitToSwapchainImage(XrSwapchainImageBaseHeader &swapchain_image,
+ const GHOST_XrDrawViewInfo &draw_info) override
{
- XrSwapchainImageOpenGLKHR *ogl_swapchain_image = reinterpret_cast<XrSwapchainImageOpenGLKHR *>(
+ XrSwapchainImageOpenGLKHR &ogl_swapchain_image = reinterpret_cast<XrSwapchainImageOpenGLKHR &>(
swapchain_image);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbo);
glFramebufferTexture2D(
- GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ogl_swapchain_image->image, 0);
-
- glBlitFramebuffer(draw_info->ofsx,
- draw_info->ofsy,
- draw_info->ofsx + draw_info->width,
- draw_info->ofsy + draw_info->height,
- draw_info->ofsx,
- draw_info->ofsy,
- draw_info->ofsx + draw_info->width,
- draw_info->ofsy + draw_info->height,
+ GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ogl_swapchain_image.image, 0);
+
+ glBlitFramebuffer(draw_info.ofsx,
+ draw_info.ofsy,
+ draw_info.ofsx + draw_info.width,
+ draw_info.ofsy + draw_info.height,
+ draw_info.ofsx,
+ draw_info.ofsy,
+ draw_info.ofsx + draw_info.width,
+ draw_info.ofsy + draw_info.height,
GL_COLOR_BUFFER_BIT,
GL_LINEAR);
@@ -204,8 +200,8 @@ class GHOST_XrGraphicsBindingOpenGL : public GHOST_IXrGraphicsBinding {
#ifdef WIN32
class GHOST_XrGraphicsBindingD3D : public GHOST_IXrGraphicsBinding {
public:
- GHOST_XrGraphicsBindingD3D(GHOST_Context *ghost_ctx)
- : GHOST_IXrGraphicsBinding(), m_ghost_wgl_ctx(*static_cast<GHOST_ContextWGL *>(ghost_ctx))
+ GHOST_XrGraphicsBindingD3D(GHOST_Context &ghost_ctx)
+ : GHOST_IXrGraphicsBinding(), m_ghost_wgl_ctx(static_cast<GHOST_ContextWGL &>(ghost_ctx))
{
m_ghost_d3d_ctx = GHOST_SystemWin32::createOffscreenContextD3D();
}
@@ -220,7 +216,7 @@ class GHOST_XrGraphicsBindingD3D : public GHOST_IXrGraphicsBinding {
}
bool checkVersionRequirements(
- GHOST_Context * /*ghost_ctx*/, /* Remember: This is the OpenGL context! */
+ GHOST_Context & /*ghost_ctx*/, /* Remember: This is the OpenGL context! */
XrInstance instance,
XrSystemId system_id,
std::string *r_requirement_info) const override
@@ -250,27 +246,25 @@ class GHOST_XrGraphicsBindingD3D : public GHOST_IXrGraphicsBinding {
}
void initFromGhostContext(
- GHOST_Context * /*ghost_ctx*/ /* Remember: This is the OpenGL context! */
+ GHOST_Context & /*ghost_ctx*/ /* Remember: This is the OpenGL context! */
) override
{
oxr_binding.d3d11.type = XR_TYPE_GRAPHICS_BINDING_D3D11_KHR;
oxr_binding.d3d11.device = m_ghost_d3d_ctx->m_device;
}
- bool chooseSwapchainFormat(const std::vector<int64_t> &runtime_formats,
- int64_t &r_result,
- bool &r_is_srgb_format) const override
+ std::optional<int64_t> chooseSwapchainFormat(const std::vector<int64_t> &runtime_formats,
+ bool &r_is_srgb_format) const override
{
std::vector<int64_t> gpu_binding_formats = {
DXGI_FORMAT_R8G8B8A8_UNORM,
DXGI_FORMAT_R8G8B8A8_UNORM_SRGB,
};
- if (choose_swapchain_format_from_candidates(gpu_binding_formats, runtime_formats, r_result)) {
- r_is_srgb_format = (r_result == DXGI_FORMAT_R8G8B8A8_UNORM_SRGB);
- return true;
- }
- return false;
+ std::optional result = choose_swapchain_format_from_candidates(gpu_binding_formats,
+ runtime_formats);
+ r_is_srgb_format = result ? (*result == DXGI_FORMAT_R8G8B8A8_UNORM_SRGB) : false;
+ return result;
}
std::vector<XrSwapchainImageBaseHeader *> createSwapchainImages(uint32_t image_count) override
@@ -291,10 +285,10 @@ class GHOST_XrGraphicsBindingD3D : public GHOST_IXrGraphicsBinding {
return base_images;
}
- void submitToSwapchainImage(XrSwapchainImageBaseHeader *swapchain_image,
- const GHOST_XrDrawViewInfo *draw_info) override
+ void submitToSwapchainImage(XrSwapchainImageBaseHeader &swapchain_image,
+ const GHOST_XrDrawViewInfo &draw_info) override
{
- XrSwapchainImageD3D11KHR *d3d_swapchain_image = reinterpret_cast<XrSwapchainImageD3D11KHR *>(
+ XrSwapchainImageD3D11KHR &d3d_swapchain_image = reinterpret_cast<XrSwapchainImageD3D11KHR &>(
swapchain_image);
# if 0
@@ -308,22 +302,22 @@ class GHOST_XrGraphicsBindingD3D : public GHOST_IXrGraphicsBinding {
CD3D11_RENDER_TARGET_VIEW_DESC rtv_desc(D3D11_RTV_DIMENSION_TEXTURE2D,
DXGI_FORMAT_R8G8B8A8_UNORM);
- m_ghost_ctx->m_device->CreateRenderTargetView(d3d_swapchain_image->texture, &rtv_desc, &rtv);
+ m_ghost_ctx->m_device->CreateRenderTargetView(d3d_swapchain_image.texture, &rtv_desc, &rtv);
if (!m_shared_resource) {
m_shared_resource = m_ghost_ctx->createSharedOpenGLResource(
- draw_info->width, draw_info->height, rtv);
+ draw_info.width, draw_info.height, rtv);
}
- m_ghost_ctx->blitFromOpenGLContext(m_shared_resource, draw_info->width, draw_info->height);
+ m_ghost_ctx->blitFromOpenGLContext(m_shared_resource, draw_info.width, draw_info.height);
# else
if (!m_shared_resource) {
- m_shared_resource = m_ghost_d3d_ctx->createSharedOpenGLResource(draw_info->width,
- draw_info->height);
+ m_shared_resource = m_ghost_d3d_ctx->createSharedOpenGLResource(draw_info.width,
+ draw_info.height);
}
- m_ghost_d3d_ctx->blitFromOpenGLContext(m_shared_resource, draw_info->width, draw_info->height);
+ m_ghost_d3d_ctx->blitFromOpenGLContext(m_shared_resource, draw_info.width, draw_info.height);
m_ghost_d3d_ctx->m_device_ctx->OMSetRenderTargets(0, nullptr, nullptr);
m_ghost_d3d_ctx->m_device_ctx->CopyResource(
- d3d_swapchain_image->texture, m_ghost_d3d_ctx->getSharedTexture2D(m_shared_resource));
+ d3d_swapchain_image.texture, m_ghost_d3d_ctx->getSharedTexture2D(m_shared_resource));
# endif
}
@@ -345,14 +339,14 @@ class GHOST_XrGraphicsBindingD3D : public GHOST_IXrGraphicsBinding {
#endif // WIN32
std::unique_ptr<GHOST_IXrGraphicsBinding> GHOST_XrGraphicsBindingCreateFromType(
- GHOST_TXrGraphicsBinding type, GHOST_Context *context)
+ GHOST_TXrGraphicsBinding type, GHOST_Context &context)
{
switch (type) {
case GHOST_kXrGraphicsOpenGL:
- return std::unique_ptr<GHOST_XrGraphicsBindingOpenGL>(new GHOST_XrGraphicsBindingOpenGL());
+ return std::make_unique<GHOST_XrGraphicsBindingOpenGL>();
#ifdef WIN32
case GHOST_kXrGraphicsD3D11:
- return std::unique_ptr<GHOST_XrGraphicsBindingD3D>(new GHOST_XrGraphicsBindingD3D(context));
+ return std::make_unique<GHOST_XrGraphicsBindingD3D>(context);
#endif
default:
return nullptr;
diff --git a/intern/ghost/intern/GHOST_XrSession.cpp b/intern/ghost/intern/GHOST_XrSession.cpp
index 5a747b1e787..e43991853cb 100644
--- a/intern/ghost/intern/GHOST_XrSession.cpp
+++ b/intern/ghost/intern/GHOST_XrSession.cpp
@@ -62,8 +62,8 @@ struct GHOST_XrDrawInfo {
*
* \{ */
-GHOST_XrSession::GHOST_XrSession(GHOST_XrContext *xr_context)
- : m_context(xr_context), m_oxr(new OpenXRSessionData())
+GHOST_XrSession::GHOST_XrSession(GHOST_XrContext &xr_context)
+ : m_context(&xr_context), m_oxr(std::make_unique<OpenXRSessionData>())
{
}
@@ -113,7 +113,7 @@ void GHOST_XrSession::initSystem()
*
* \{ */
-static void create_reference_spaces(OpenXRSessionData *oxr, const GHOST_XrPose *base_pose)
+static void create_reference_spaces(OpenXRSessionData &oxr, const GHOST_XrPose &base_pose)
{
XrReferenceSpaceCreateInfo create_info = {XR_TYPE_REFERENCE_SPACE_CREATE_INFO};
create_info.poseInReferenceSpace.orientation.w = 1.0f;
@@ -142,11 +142,11 @@ static void create_reference_spaces(OpenXRSessionData *oxr, const GHOST_XrPose *
(void)base_pose;
#endif
- CHECK_XR(xrCreateReferenceSpace(oxr->session, &create_info, &oxr->reference_space),
+ CHECK_XR(xrCreateReferenceSpace(oxr.session, &create_info, &oxr.reference_space),
"Failed to create reference space.");
create_info.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_VIEW;
- CHECK_XR(xrCreateReferenceSpace(oxr->session, &create_info, &oxr->view_space),
+ CHECK_XR(xrCreateReferenceSpace(oxr.session, &create_info, &oxr.view_space),
"Failed to create view reference space.");
}
@@ -173,15 +173,15 @@ void GHOST_XrSession::start(const GHOST_XrSessionBeginInfo *begin_info)
std::string requirement_str;
m_gpu_binding = GHOST_XrGraphicsBindingCreateFromType(m_context->getGraphicsBindingType(),
- m_gpu_ctx);
+ *m_gpu_ctx);
if (!m_gpu_binding->checkVersionRequirements(
- m_gpu_ctx, m_context->getInstance(), m_oxr->system_id, &requirement_str)) {
+ *m_gpu_ctx, m_context->getInstance(), m_oxr->system_id, &requirement_str)) {
std::ostringstream strstream;
strstream << "Available graphics context version does not meet the following requirements: "
<< requirement_str;
throw GHOST_XrException(strstream.str().c_str());
}
- m_gpu_binding->initFromGhostContext(m_gpu_ctx);
+ m_gpu_binding->initFromGhostContext(*m_gpu_ctx);
XrSessionCreateInfo create_info = {};
create_info.type = XR_TYPE_SESSION_CREATE_INFO;
@@ -195,7 +195,7 @@ void GHOST_XrSession::start(const GHOST_XrSessionBeginInfo *begin_info)
"detailed error information to the command line.");
prepareDrawing();
- create_reference_spaces(m_oxr.get(), &begin_info->base_pose);
+ create_reference_spaces(*m_oxr, begin_info->base_pose);
}
void GHOST_XrSession::requestEnd()
@@ -217,14 +217,14 @@ void GHOST_XrSession::endSession()
}
GHOST_XrSession::LifeExpectancy GHOST_XrSession::handleStateChangeEvent(
- const XrEventDataSessionStateChanged *lifecycle)
+ const XrEventDataSessionStateChanged &lifecycle)
{
- m_oxr->session_state = lifecycle->state;
+ m_oxr->session_state = lifecycle.state;
/* Runtime may send events for apparently destroyed session. Our handle should be NULL then. */
- assert((m_oxr->session == XR_NULL_HANDLE) || (m_oxr->session == lifecycle->session));
+ assert((m_oxr->session == XR_NULL_HANDLE) || (m_oxr->session == lifecycle.session));
- switch (lifecycle->state) {
+ switch (lifecycle.state) {
case XR_SESSION_STATE_READY: {
beginSession();
break;
@@ -272,7 +272,7 @@ void GHOST_XrSession::prepareDrawing()
m_oxr->views.resize(view_count, {XR_TYPE_VIEW});
- m_draw_info = std::unique_ptr<GHOST_XrDrawInfo>(new GHOST_XrDrawInfo());
+ m_draw_info = std::make_unique<GHOST_XrDrawInfo>();
}
void GHOST_XrSession::beginFrameDrawing()
@@ -295,43 +295,43 @@ void GHOST_XrSession::beginFrameDrawing()
}
}
-static void print_debug_timings(GHOST_XrDrawInfo *draw_info)
+static void print_debug_timings(GHOST_XrDrawInfo &draw_info)
{
/** Render time of last 8 frames (in ms) to calculate an average. */
std::chrono::duration<double, std::milli> duration = std::chrono::high_resolution_clock::now() -
- draw_info->frame_begin_time;
+ draw_info.frame_begin_time;
const double duration_ms = duration.count();
const int avg_frame_count = 8;
double avg_ms_tot = 0.0;
- if (draw_info->last_frame_times.size() >= avg_frame_count) {
- draw_info->last_frame_times.pop_front();
- assert(draw_info->last_frame_times.size() == avg_frame_count - 1);
+ if (draw_info.last_frame_times.size() >= avg_frame_count) {
+ draw_info.last_frame_times.pop_front();
+ assert(draw_info.last_frame_times.size() == avg_frame_count - 1);
}
- draw_info->last_frame_times.push_back(duration_ms);
- for (double ms_iter : draw_info->last_frame_times) {
+ draw_info.last_frame_times.push_back(duration_ms);
+ for (double ms_iter : draw_info.last_frame_times) {
avg_ms_tot += ms_iter;
}
printf("VR frame render time: %.0fms - %.2f FPS (%.2f FPS 8 frames average)\n",
duration_ms,
1000.0 / duration_ms,
- 1000.0 / (avg_ms_tot / draw_info->last_frame_times.size()));
+ 1000.0 / (avg_ms_tot / draw_info.last_frame_times.size()));
}
-void GHOST_XrSession::endFrameDrawing(std::vector<XrCompositionLayerBaseHeader *> *layers)
+void GHOST_XrSession::endFrameDrawing(std::vector<XrCompositionLayerBaseHeader *> &layers)
{
XrFrameEndInfo end_info = {XR_TYPE_FRAME_END_INFO};
end_info.displayTime = m_draw_info->frame_state.predictedDisplayTime;
end_info.environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE;
- end_info.layerCount = layers->size();
- end_info.layers = layers->data();
+ end_info.layerCount = layers.size();
+ end_info.layers = layers.data();
CHECK_XR(xrEndFrame(m_oxr->session, &end_info), "Failed to submit rendered frame.");
if (m_context->isDebugTimeMode()) {
- print_debug_timings(m_draw_info.get());
+ print_debug_timings(*m_draw_info);
}
}
@@ -349,7 +349,7 @@ void GHOST_XrSession::draw(void *draw_customdata)
layers.push_back(reinterpret_cast<XrCompositionLayerBaseHeader *>(&proj_layer));
}
- endFrameDrawing(&layers);
+ endFrameDrawing(layers);
}
static void copy_openxr_pose_to_ghost_pose(const XrPosef &oxr_pose, GHOST_XrPose &r_ghost_pose)
@@ -399,7 +399,7 @@ void GHOST_XrSession::drawView(GHOST_XrSwapchain &swapchain,
/* Draw! */
m_context->getCustomFuncs().draw_view_fn(&draw_view_info, draw_customdata);
- m_gpu_binding->submitToSwapchainImage(swapchain_image, &draw_view_info);
+ m_gpu_binding->submitToSwapchainImage(*swapchain_image, draw_view_info);
swapchain.releaseImage();
}
diff --git a/intern/ghost/intern/GHOST_XrSession.h b/intern/ghost/intern/GHOST_XrSession.h
index 74555c0c170..79a586411e9 100644
--- a/intern/ghost/intern/GHOST_XrSession.h
+++ b/intern/ghost/intern/GHOST_XrSession.h
@@ -37,13 +37,13 @@ class GHOST_XrSession {
SESSION_DESTROY,
};
- GHOST_XrSession(GHOST_XrContext *xr_context);
+ GHOST_XrSession(GHOST_XrContext &xr_context);
~GHOST_XrSession();
void start(const GHOST_XrSessionBeginInfo *begin_info);
void requestEnd();
- LifeExpectancy handleStateChangeEvent(const XrEventDataSessionStateChanged *lifecycle);
+ LifeExpectancy handleStateChangeEvent(const XrEventDataSessionStateChanged &lifecycle);
bool isRunning() const;
bool needsUpsideDownDrawing() const;
@@ -81,5 +81,5 @@ class GHOST_XrSession {
XrView &view,
void *draw_customdata);
void beginFrameDrawing();
- void endFrameDrawing(std::vector<XrCompositionLayerBaseHeader *> *layers);
+ void endFrameDrawing(std::vector<XrCompositionLayerBaseHeader *> &layers);
};
diff --git a/intern/ghost/intern/GHOST_XrSwapchain.cpp b/intern/ghost/intern/GHOST_XrSwapchain.cpp
index f7808c20112..9973d99cc37 100644
--- a/intern/ghost/intern/GHOST_XrSwapchain.cpp
+++ b/intern/ghost/intern/GHOST_XrSwapchain.cpp
@@ -54,11 +54,10 @@ static OpenXRSwapchainData::ImageVec swapchain_images_create(XrSwapchain swapcha
GHOST_XrSwapchain::GHOST_XrSwapchain(GHOST_IXrGraphicsBinding &gpu_binding,
const XrSession &session,
const XrViewConfigurationView &view_config)
- : m_oxr(new OpenXRSwapchainData())
+ : m_oxr(std::make_unique<OpenXRSwapchainData>())
{
XrSwapchainCreateInfo create_info = {XR_TYPE_SWAPCHAIN_CREATE_INFO};
uint32_t format_count = 0;
- int64_t chosen_format;
CHECK_XR(xrEnumerateSwapchainFormats(session, 0, &format_count, nullptr),
"Failed to get count of swapchain image formats.");
@@ -68,14 +67,16 @@ GHOST_XrSwapchain::GHOST_XrSwapchain(GHOST_IXrGraphicsBinding &gpu_binding,
"Failed to get swapchain image formats.");
assert(swapchain_formats.size() == format_count);
- if (!gpu_binding.chooseSwapchainFormat(swapchain_formats, chosen_format, m_is_srgb_buffer)) {
+ std::optional chosen_format = gpu_binding.chooseSwapchainFormat(swapchain_formats,
+ m_is_srgb_buffer);
+ if (!chosen_format) {
throw GHOST_XrException(
"Error: No format matching OpenXR runtime supported swapchain formats found.");
}
create_info.usageFlags = XR_SWAPCHAIN_USAGE_SAMPLED_BIT |
XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT;
- create_info.format = chosen_format;
+ create_info.format = *chosen_format;
create_info.sampleCount = view_config.recommendedSwapchainSampleCount;
create_info.width = view_config.recommendedImageRectWidth;
create_info.height = view_config.recommendedImageRectHeight;
@@ -95,7 +96,8 @@ GHOST_XrSwapchain::GHOST_XrSwapchain(GHOST_IXrGraphicsBinding &gpu_binding,
GHOST_XrSwapchain::GHOST_XrSwapchain(GHOST_XrSwapchain &&other)
: m_oxr(std::move(other.m_oxr)),
m_image_width(other.m_image_width),
- m_image_height(other.m_image_height)
+ m_image_height(other.m_image_height),
+ m_is_srgb_buffer(other.m_is_srgb_buffer)
{
/* Prevent xrDestroySwapchain call for the moved out item. */
other.m_oxr = nullptr;
diff --git a/intern/glew-mx/intern/gl-deprecated.h b/intern/glew-mx/intern/gl-deprecated.h
index b6524cd5197..d101591fea3 100644
--- a/intern/glew-mx/intern/gl-deprecated.h
+++ b/intern/glew-mx/intern/gl-deprecated.h
@@ -824,10 +824,12 @@
#define GL_SOURCE0_ALPHA DO_NOT_USE_GL_SOURCE0_ALPHA
#undef GL_SOURCE0_RGB
#define GL_SOURCE0_RGB DO_NOT_USE_GL_SOURCE0_RGB
-#undef GL_SOURCE1_ALPHA
-#define GL_SOURCE1_ALPHA DO_NOT_USE_GL_SOURCE1_ALPHA
-#undef GL_SOURCE1_RGB
-#define GL_SOURCE1_RGB DO_NOT_USE_GL_SOURCE1_RGB
+#if 0 /* Those are reused as new valid enum! GL_SRC1_COLOR etc... */
+# undef GL_SOURCE1_ALPHA
+# define GL_SOURCE1_ALPHA DO_NOT_USE_GL_SOURCE1_ALPHA
+# undef GL_SOURCE1_RGB
+# define GL_SOURCE1_RGB DO_NOT_USE_GL_SOURCE1_RGB
+#endif
#undef GL_SOURCE2_ALPHA
#define GL_SOURCE2_ALPHA DO_NOT_USE_GL_SOURCE2_ALPHA
#undef GL_SOURCE2_RGB
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_main.cpp b/intern/mantaflow/intern/MANTA_main.cpp
index 5997e819ccf..8d92d616e15 100644
--- a/intern/mantaflow/intern/MANTA_main.cpp
+++ b/intern/mantaflow/intern/MANTA_main.cpp
@@ -1086,15 +1086,19 @@ string MANTA::parseScript(const string &setup_string, FluidModifierData *fmd)
}
/* Dirty hack: Needed to format paths from python code that is run via PyRun_SimpleString */
-static string escapeSlashes(string const &s)
+static string escapePath(string const &s)
{
string result = "";
- for (string::const_iterator i = s.begin(), end = s.end(); i != end; ++i) {
- unsigned char c = *i;
- if (c == '\\')
+ for (char c : s) {
+ if (c == '\\') {
result += "\\\\";
- else
+ }
+ else if (c == '\'') {
+ result += "\\\'";
+ }
+ else {
result += c;
+ }
}
return result;
}
@@ -1155,13 +1159,13 @@ bool MANTA::writeData(FluidModifierData *fmd, int framenr)
if (mUsingSmoke) {
ss.str("");
- ss << "smoke_save_data_" << mCurrentID << "('" << escapeSlashes(directory) << "', " << framenr
+ ss << "smoke_save_data_" << mCurrentID << "('" << escapePath(directory) << "', " << framenr
<< ", '" << volume_format << "', " << resumable_cache << ")";
pythonCommands.push_back(ss.str());
}
if (mUsingLiquid) {
ss.str("");
- ss << "liquid_save_data_" << mCurrentID << "('" << escapeSlashes(directory) << "', " << framenr
+ ss << "liquid_save_data_" << mCurrentID << "('" << escapePath(directory) << "', " << framenr
<< ", '" << volume_format << "', " << resumable_cache << ")";
pythonCommands.push_back(ss.str());
}
@@ -1183,7 +1187,7 @@ bool MANTA::writeNoise(FluidModifierData *fmd, int framenr)
if (mUsingSmoke && mUsingNoise) {
ss.str("");
- ss << "smoke_save_noise_" << mCurrentID << "('" << escapeSlashes(directory) << "', " << framenr
+ ss << "smoke_save_noise_" << mCurrentID << "('" << escapePath(directory) << "', " << framenr
<< ", '" << volume_format << "', " << resumable_cache << ")";
pythonCommands.push_back(ss.str());
}
@@ -1256,7 +1260,7 @@ bool MANTA::readData(FluidModifierData *fmd, int framenr, bool resumable)
if (mUsingSmoke) {
ss.str("");
- ss << "smoke_load_data_" << mCurrentID << "('" << escapeSlashes(directory) << "', " << framenr
+ ss << "smoke_load_data_" << mCurrentID << "('" << escapePath(directory) << "', " << framenr
<< ", '" << volume_format << "', " << resumable_cache << ")";
pythonCommands.push_back(ss.str());
result &= runPythonString(pythonCommands);
@@ -1264,7 +1268,7 @@ bool MANTA::readData(FluidModifierData *fmd, int framenr, bool resumable)
}
if (mUsingLiquid) {
ss.str("");
- ss << "liquid_load_data_" << mCurrentID << "('" << escapeSlashes(directory) << "', " << framenr
+ ss << "liquid_load_data_" << mCurrentID << "('" << escapePath(directory) << "', " << framenr
<< ", '" << volume_format << "', " << resumable_cache << ")";
pythonCommands.push_back(ss.str());
result &= runPythonString(pythonCommands);
@@ -1298,7 +1302,7 @@ bool MANTA::readNoise(FluidModifierData *fmd, int framenr, bool resumable)
return false;
ss.str("");
- ss << "smoke_load_noise_" << mCurrentID << "('" << escapeSlashes(directory) << "', " << framenr
+ ss << "smoke_load_noise_" << mCurrentID << "('" << escapePath(directory) << "', " << framenr
<< ", '" << volume_format << "', " << resumable_cache << ")";
pythonCommands.push_back(ss.str());
@@ -1326,14 +1330,14 @@ bool MANTA::readMesh(FluidModifierData *fmd, int framenr)
return false;
ss.str("");
- ss << "liquid_load_mesh_" << mCurrentID << "('" << escapeSlashes(directory) << "', " << framenr
+ ss << "liquid_load_mesh_" << mCurrentID << "('" << escapePath(directory) << "', " << framenr
<< ", '" << mesh_format << "')";
pythonCommands.push_back(ss.str());
if (mUsingMVel) {
ss.str("");
- ss << "liquid_load_meshvel_" << mCurrentID << "('" << escapeSlashes(directory) << "', "
- << framenr << ", '" << volume_format << "')";
+ ss << "liquid_load_meshvel_" << mCurrentID << "('" << escapePath(directory) << "', " << framenr
+ << ", '" << volume_format << "')";
pythonCommands.push_back(ss.str());
}
@@ -1367,8 +1371,8 @@ bool MANTA::readParticles(FluidModifierData *fmd, int framenr, bool resumable)
return false;
ss.str("");
- ss << "liquid_load_particles_" << mCurrentID << "('" << escapeSlashes(directory) << "', "
- << framenr << ", '" << volume_format << "', " << resumable_cache << ")";
+ ss << "liquid_load_particles_" << mCurrentID << "('" << escapePath(directory) << "', " << framenr
+ << ", '" << volume_format << "', " << resumable_cache << ")";
pythonCommands.push_back(ss.str());
return (mParticlesFromFile = runPythonString(pythonCommands));
@@ -1399,13 +1403,13 @@ bool MANTA::readGuiding(FluidModifierData *fmd, int framenr, bool sourceDomain)
if (sourceDomain) {
ss.str("");
- ss << "fluid_load_vel_" << mCurrentID << "('" << escapeSlashes(directory) << "', " << framenr
+ ss << "fluid_load_vel_" << mCurrentID << "('" << escapePath(directory) << "', " << framenr
<< ", '" << volume_format << "')";
}
else {
ss.str("");
- ss << "fluid_load_guiding_" << mCurrentID << "('" << escapeSlashes(directory) << "', "
- << framenr << ", '" << volume_format << "')";
+ ss << "fluid_load_guiding_" << mCurrentID << "('" << escapePath(directory) << "', " << framenr
+ << ", '" << volume_format << "')";
}
pythonCommands.push_back(ss.str());
@@ -1439,7 +1443,7 @@ bool MANTA::bakeData(FluidModifierData *fmd, int framenr)
BLI_path_make_safe(cacheDirGuiding);
ss.str("");
- ss << "bake_fluid_data_" << mCurrentID << "('" << escapeSlashes(cacheDirData) << "', " << framenr
+ ss << "bake_fluid_data_" << mCurrentID << "('" << escapePath(cacheDirData) << "', " << framenr
<< ", '" << volume_format << "')";
pythonCommands.push_back(ss.str());
@@ -1465,7 +1469,7 @@ bool MANTA::bakeNoise(FluidModifierData *fmd, int framenr)
BLI_path_make_safe(cacheDirNoise);
ss.str("");
- ss << "bake_noise_" << mCurrentID << "('" << escapeSlashes(cacheDirNoise) << "', " << framenr
+ ss << "bake_noise_" << mCurrentID << "('" << escapePath(cacheDirNoise) << "', " << framenr
<< ", '" << volume_format << "')";
pythonCommands.push_back(ss.str());
@@ -1492,8 +1496,8 @@ bool MANTA::bakeMesh(FluidModifierData *fmd, int framenr)
BLI_path_make_safe(cacheDirMesh);
ss.str("");
- ss << "bake_mesh_" << mCurrentID << "('" << escapeSlashes(cacheDirMesh) << "', " << framenr
- << ", '" << volume_format << "', '" << mesh_format << "')";
+ ss << "bake_mesh_" << mCurrentID << "('" << escapePath(cacheDirMesh) << "', " << framenr << ", '"
+ << volume_format << "', '" << mesh_format << "')";
pythonCommands.push_back(ss.str());
return runPythonString(pythonCommands);
@@ -1522,7 +1526,7 @@ bool MANTA::bakeParticles(FluidModifierData *fmd, int framenr)
BLI_path_make_safe(cacheDirParticles);
ss.str("");
- ss << "bake_particles_" << mCurrentID << "('" << escapeSlashes(cacheDirParticles) << "', "
+ ss << "bake_particles_" << mCurrentID << "('" << escapePath(cacheDirParticles) << "', "
<< framenr << ", '" << volume_format << "', " << resumable_cache << ")";
pythonCommands.push_back(ss.str());
@@ -1552,7 +1556,7 @@ bool MANTA::bakeGuiding(FluidModifierData *fmd, int framenr)
BLI_path_make_safe(cacheDirGuiding);
ss.str("");
- ss << "bake_guiding_" << mCurrentID << "('" << escapeSlashes(cacheDirGuiding) << "', " << framenr
+ ss << "bake_guiding_" << mCurrentID << "('" << escapePath(cacheDirGuiding) << "', " << framenr
<< ", '" << volume_format << "', " << resumable_cache << ")";
pythonCommands.push_back(ss.str());
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/opencolorio/ocio_impl_glsl.cc b/intern/opencolorio/ocio_impl_glsl.cc
index f766f57732a..ae134cc0725 100644
--- a/intern/opencolorio/ocio_impl_glsl.cc
+++ b/intern/opencolorio/ocio_impl_glsl.cc
@@ -47,6 +47,7 @@
#include "GPU_immediate.h"
#include "GPU_shader.h"
+#include "GPU_uniform_buffer.h"
using namespace OCIO_NAMESPACE;
@@ -119,8 +120,8 @@ struct OCIO_GLSLLut3d {
struct OCIO_GLSLCurveMappping {
/** Cache IDs */
size_t cacheId;
- /** OpenGL Uniform Buffer handle. 0 if not allocated. */
- GLuint buffer;
+ /** GPU Uniform Buffer handle. 0 if not allocated. */
+ GPUUniformBuf *buffer;
/** OpenGL Texture handles. 0 if not allocated. */
GLuint texture;
/** Error checking. */
@@ -370,10 +371,7 @@ static void ensureGLSLCurveMapping(OCIO_GLSLCurveMappping **curvemap_ptr,
allocateCurveMappingTexture(curvemap, curve_mapping_settings);
/* Uniform buffer object. */
- glGenBuffers(1, &curvemap->buffer);
- glBindBuffer(GL_UNIFORM_BUFFER, curvemap->buffer);
- glBufferData(GL_UNIFORM_BUFFER, sizeof(OCIO_GLSLCurveMappingParameters), 0, GL_DYNAMIC_DRAW);
- glBindBuffer(GL_UNIFORM_BUFFER, 0);
+ curvemap->buffer = GPU_uniformbuf_create(sizeof(OCIO_GLSLCurveMappingParameters));
curvemap->valid = (curvemap->texture != 0);
curvemap->cacheId = 0;
@@ -384,7 +382,7 @@ static void ensureGLSLCurveMapping(OCIO_GLSLCurveMappping **curvemap_ptr,
static void freeGLSLCurveMapping(OCIO_GLSLCurveMappping *curvemap)
{
glDeleteTextures(1, &curvemap->texture);
- glDeleteBuffers(1, &curvemap->buffer);
+ GPU_uniformbuf_free(curvemap->buffer);
OBJECT_GUARDED_DELETE(curvemap, OCIO_GLSLCurveMappping);
}
@@ -438,9 +436,7 @@ static void updateGLSLCurveMapping(OCIO_GLSLCurveMappping *curvemap,
data.curve_mapping_lut_size = curve_mapping_settings->lut_size;
data.curve_mapping_use_extend_extrapolate = curve_mapping_settings->use_extend_extrapolate;
- glBindBuffer(GL_UNIFORM_BUFFER, curvemap->buffer);
- glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(OCIO_GLSLCurveMappingParameters), &data);
- glBindBuffer(GL_UNIFORM_BUFFER, 0);
+ GPU_uniformbuf_update(curvemap->buffer, &data);
curvemap->cacheId = cacheId;
}
@@ -597,7 +593,7 @@ bool OCIOImpl::setupGLSLDraw(OCIO_GLSLDrawState **state_r,
glActiveTexture(GL_TEXTURE0);
/* Bind UBO. */
- glBindBufferBase(GL_UNIFORM_BUFFER, shader->ubo_bind, shader_curvemap->buffer);
+ GPU_uniformbuf_bind(shader_curvemap->buffer, shader->ubo_bind);
/* TODO(fclem) remove remains of IMM. */
immBindShader(shader->shader);
diff --git a/intern/rigidbody/rb_bullet_api.cpp b/intern/rigidbody/rb_bullet_api.cpp
index db8c062990c..b5814055cf8 100644
--- a/intern/rigidbody/rb_bullet_api.cpp
+++ b/intern/rigidbody/rb_bullet_api.cpp
@@ -81,7 +81,7 @@ struct rbRigidBody {
};
struct rbVert {
- float x, y, z;
+ btScalar x, y, z;
};
struct rbTri {
int v0, v1, v2;
@@ -356,8 +356,8 @@ void RB_body_delete(rbRigidBody *object)
/* motion state */
btMotionState *ms = body->getMotionState();
- if (ms)
- delete ms;
+
+ delete ms;
/* collision shape is done elsewhere... */
@@ -399,8 +399,9 @@ float RB_body_get_mass(rbRigidBody *object)
*/
float value = (float)body->getInvMass();
- if (value)
+ if (value) {
value = 1.0f / value;
+ }
return value;
}
@@ -551,10 +552,12 @@ void RB_body_set_angular_factor(rbRigidBody *object, float x, float y, float z)
void RB_body_set_kinematic_state(rbRigidBody *object, int kinematic)
{
btRigidBody *body = object->body;
- if (kinematic)
+ if (kinematic) {
body->setCollisionFlags(body->getCollisionFlags() | btCollisionObject::CF_KINEMATIC_OBJECT);
- else
+ }
+ else {
body->setCollisionFlags(body->getCollisionFlags() & ~btCollisionObject::CF_KINEMATIC_OBJECT);
+ }
}
/* ............ */
@@ -562,10 +565,12 @@ void RB_body_set_kinematic_state(rbRigidBody *object, int kinematic)
void RB_body_set_activation_state(rbRigidBody *object, int use_deactivation)
{
btRigidBody *body = object->body;
- if (use_deactivation)
+ if (use_deactivation) {
body->forceActivationState(ACTIVE_TAG);
- else
+ }
+ else {
body->setActivationState(DISABLE_DEACTIVATION);
+ }
}
void RB_body_activate(rbRigidBody *object)
{
@@ -621,8 +626,9 @@ void RB_body_set_scale(rbRigidBody *object, const float scale[3])
cshape->setLocalScaling(btVector3(scale[0], scale[1], scale[2]));
/* GIimpact shapes have to be updated to take scaling into account */
- if (cshape->getShapeType() == GIMPACT_SHAPE_PROXYTYPE)
+ if (cshape->getShapeType() == GIMPACT_SHAPE_PROXYTYPE) {
((btGImpactMeshShape *)cshape)->updateBound();
+ }
}
}
@@ -778,7 +784,7 @@ void RB_trimesh_finish(rbMeshData *mesh)
(int *)mesh->triangles,
sizeof(rbTri),
mesh->num_vertices,
- (float *)mesh->vertices,
+ (btScalar *)mesh->vertices,
sizeof(rbVert));
}
@@ -805,8 +811,9 @@ void RB_shape_trimesh_update(rbCollisionShape *shape,
float min[3],
float max[3])
{
- if (shape->mesh == NULL || num_verts != shape->mesh->num_vertices)
+ if (shape->mesh == NULL || num_verts != shape->mesh->num_vertices) {
return;
+ }
for (int i = 0; i < num_verts; i++) {
float *vert = (float *)(((char *)vertices + i * vert_stride));
@@ -882,11 +889,12 @@ void RB_shape_delete(rbCollisionShape *shape)
if (shape->cshape->getShapeType() == SCALED_TRIANGLE_MESH_SHAPE_PROXYTYPE) {
btBvhTriangleMeshShape *child_shape =
((btScaledBvhTriangleMeshShape *)shape->cshape)->getChildShape();
- if (child_shape)
- delete child_shape;
+
+ delete child_shape;
}
- if (shape->mesh)
+ if (shape->mesh) {
RB_trimesh_data_delete(shape->mesh);
+ }
delete shape->cshape;
/* Delete compound child shapes if there are any */
diff --git a/release/datafiles/icons/brush.gpencil_draw.tint.dat b/release/datafiles/icons/brush.gpencil_draw.tint.dat
index 016e4304498..32d8a5f04e4 100644
--- a/release/datafiles/icons/brush.gpencil_draw.tint.dat
+++ b/release/datafiles/icons/brush.gpencil_draw.tint.dat
Binary files differ
diff --git a/release/datafiles/icons/brush.paint_texture.draw.dat b/release/datafiles/icons/brush.paint_texture.draw.dat
index cfa5f1a6042..678a9ea26e5 100644
--- a/release/datafiles/icons/brush.paint_texture.draw.dat
+++ b/release/datafiles/icons/brush.paint_texture.draw.dat
Binary files differ
diff --git a/release/datafiles/icons/brush.paint_texture.soften.dat b/release/datafiles/icons/brush.paint_texture.soften.dat
index 8c547809792..2128de71dff 100644
--- a/release/datafiles/icons/brush.paint_texture.soften.dat
+++ b/release/datafiles/icons/brush.paint_texture.soften.dat
Binary files differ
diff --git a/release/datafiles/icons/brush.paint_vertex.blur.dat b/release/datafiles/icons/brush.paint_vertex.blur.dat
index 8c547809792..2128de71dff 100644
--- a/release/datafiles/icons/brush.paint_vertex.blur.dat
+++ b/release/datafiles/icons/brush.paint_vertex.blur.dat
Binary files differ
diff --git a/release/datafiles/icons/brush.paint_vertex.draw.dat b/release/datafiles/icons/brush.paint_vertex.draw.dat
index c1a8796ea02..74e00d243d6 100644
--- a/release/datafiles/icons/brush.paint_vertex.draw.dat
+++ b/release/datafiles/icons/brush.paint_vertex.draw.dat
Binary files differ
diff --git a/release/datafiles/icons/brush.paint_vertex.replace.dat b/release/datafiles/icons/brush.paint_vertex.replace.dat
index a37fdf2e1b1..676436548a7 100644
--- a/release/datafiles/icons/brush.paint_vertex.replace.dat
+++ b/release/datafiles/icons/brush.paint_vertex.replace.dat
Binary files differ
diff --git a/release/datafiles/icons/brush.paint_weight.blur.dat b/release/datafiles/icons/brush.paint_weight.blur.dat
index 8c547809792..2128de71dff 100644
--- a/release/datafiles/icons/brush.paint_weight.blur.dat
+++ b/release/datafiles/icons/brush.paint_weight.blur.dat
Binary files differ
diff --git a/release/datafiles/icons/brush.paint_weight.draw.dat b/release/datafiles/icons/brush.paint_weight.draw.dat
index cdb4ccf5efb..a2641927371 100644
--- a/release/datafiles/icons/brush.paint_weight.draw.dat
+++ b/release/datafiles/icons/brush.paint_weight.draw.dat
Binary files differ
diff --git a/release/datafiles/icons/brush.particle.add.dat b/release/datafiles/icons/brush.particle.add.dat
index 3c4f65cf6e2..b66f4da5b71 100644
--- a/release/datafiles/icons/brush.particle.add.dat
+++ b/release/datafiles/icons/brush.particle.add.dat
Binary files differ
diff --git a/release/datafiles/icons/brush.particle.comb.dat b/release/datafiles/icons/brush.particle.comb.dat
index 8b656db622b..d6dd75a35d7 100644
--- a/release/datafiles/icons/brush.particle.comb.dat
+++ b/release/datafiles/icons/brush.particle.comb.dat
Binary files differ
diff --git a/release/datafiles/icons/brush.particle.cut.dat b/release/datafiles/icons/brush.particle.cut.dat
index 97dc3e6099b..e7ef86e2fbc 100644
--- a/release/datafiles/icons/brush.particle.cut.dat
+++ b/release/datafiles/icons/brush.particle.cut.dat
Binary files differ
diff --git a/release/datafiles/icons/brush.particle.length.dat b/release/datafiles/icons/brush.particle.length.dat
index 59e74fd9912..e3aa3f2f9df 100644
--- a/release/datafiles/icons/brush.particle.length.dat
+++ b/release/datafiles/icons/brush.particle.length.dat
Binary files differ
diff --git a/release/datafiles/icons/brush.particle.puff.dat b/release/datafiles/icons/brush.particle.puff.dat
index 9dd194bfd93..db2bab46bfe 100644
--- a/release/datafiles/icons/brush.particle.puff.dat
+++ b/release/datafiles/icons/brush.particle.puff.dat
Binary files differ
diff --git a/release/datafiles/icons/brush.particle.smooth.dat b/release/datafiles/icons/brush.particle.smooth.dat
index 54b80a25841..7deaa4ed082 100644
--- a/release/datafiles/icons/brush.particle.smooth.dat
+++ b/release/datafiles/icons/brush.particle.smooth.dat
Binary files differ
diff --git a/release/datafiles/icons/brush.sculpt.cloth.dat b/release/datafiles/icons/brush.sculpt.cloth.dat
index 5e8fad60035..7e936167381 100644
--- a/release/datafiles/icons/brush.sculpt.cloth.dat
+++ b/release/datafiles/icons/brush.sculpt.cloth.dat
Binary files differ
diff --git a/release/datafiles/icons/brush.sculpt.draw.dat b/release/datafiles/icons/brush.sculpt.draw.dat
index 36ec5575bdd..014ce10e8cc 100644
--- a/release/datafiles/icons/brush.sculpt.draw.dat
+++ b/release/datafiles/icons/brush.sculpt.draw.dat
Binary files differ
diff --git a/release/datafiles/icons/brush.sculpt.draw_sharp.dat b/release/datafiles/icons/brush.sculpt.draw_sharp.dat
index ad42f4bf870..9bea1b02894 100644
--- a/release/datafiles/icons/brush.sculpt.draw_sharp.dat
+++ b/release/datafiles/icons/brush.sculpt.draw_sharp.dat
Binary files differ
diff --git a/release/datafiles/icons/brush.sculpt.elastic_deform.dat b/release/datafiles/icons/brush.sculpt.elastic_deform.dat
index 6d0ea25c1fe..0b12d717d3a 100644
--- a/release/datafiles/icons/brush.sculpt.elastic_deform.dat
+++ b/release/datafiles/icons/brush.sculpt.elastic_deform.dat
Binary files differ
diff --git a/release/datafiles/icons/brush.sculpt.layer.dat b/release/datafiles/icons/brush.sculpt.layer.dat
index 184f1bc9c13..1031d95332a 100644
--- a/release/datafiles/icons/brush.sculpt.layer.dat
+++ b/release/datafiles/icons/brush.sculpt.layer.dat
Binary files differ
diff --git a/release/datafiles/icons/brush.sculpt.nudge.dat b/release/datafiles/icons/brush.sculpt.nudge.dat
index 309a01a5645..e10157e9cd0 100644
--- a/release/datafiles/icons/brush.sculpt.nudge.dat
+++ b/release/datafiles/icons/brush.sculpt.nudge.dat
Binary files differ
diff --git a/release/datafiles/icons/brush.sculpt.rotate.dat b/release/datafiles/icons/brush.sculpt.rotate.dat
index a0bb63d14db..8d1723a8c71 100644
--- a/release/datafiles/icons/brush.sculpt.rotate.dat
+++ b/release/datafiles/icons/brush.sculpt.rotate.dat
Binary files differ
diff --git a/release/datafiles/icons/brush.sculpt.snake_hook.dat b/release/datafiles/icons/brush.sculpt.snake_hook.dat
index 64256d93702..20300c1d97c 100644
--- a/release/datafiles/icons/brush.sculpt.snake_hook.dat
+++ b/release/datafiles/icons/brush.sculpt.snake_hook.dat
Binary files differ
diff --git a/release/datafiles/icons/brush.sculpt.thumb.dat b/release/datafiles/icons/brush.sculpt.thumb.dat
index a2634afced9..9da33eccd98 100644
--- a/release/datafiles/icons/brush.sculpt.thumb.dat
+++ b/release/datafiles/icons/brush.sculpt.thumb.dat
Binary files differ
diff --git a/release/datafiles/icons/ops.curve.draw.dat b/release/datafiles/icons/ops.curve.draw.dat
index 8ae8ef5b1c8..cf2c8e31bcb 100644
--- a/release/datafiles/icons/ops.curve.draw.dat
+++ b/release/datafiles/icons/ops.curve.draw.dat
Binary files differ
diff --git a/release/datafiles/icons/ops.gpencil.draw.dat b/release/datafiles/icons/ops.gpencil.draw.dat
index d05e67d276c..e71644f4968 100644
--- a/release/datafiles/icons/ops.gpencil.draw.dat
+++ b/release/datafiles/icons/ops.gpencil.draw.dat
Binary files differ
diff --git a/release/datafiles/icons/ops.gpencil.draw.eraser.dat b/release/datafiles/icons/ops.gpencil.draw.eraser.dat
index 2dc1653f810..44f65c4581d 100644
--- a/release/datafiles/icons/ops.gpencil.draw.eraser.dat
+++ b/release/datafiles/icons/ops.gpencil.draw.eraser.dat
Binary files differ
diff --git a/release/datafiles/icons/ops.gpencil.draw.line.dat b/release/datafiles/icons/ops.gpencil.draw.line.dat
index 53fa0193fa9..bbfaf28bc5e 100644
--- a/release/datafiles/icons/ops.gpencil.draw.line.dat
+++ b/release/datafiles/icons/ops.gpencil.draw.line.dat
Binary files differ
diff --git a/release/datafiles/icons/ops.gpencil.draw.poly.dat b/release/datafiles/icons/ops.gpencil.draw.poly.dat
index a223c9eb90c..e3ca2f35ece 100644
--- a/release/datafiles/icons/ops.gpencil.draw.poly.dat
+++ b/release/datafiles/icons/ops.gpencil.draw.poly.dat
Binary files differ
diff --git a/release/datafiles/icons/ops.gpencil.sculpt_smooth.dat b/release/datafiles/icons/ops.gpencil.sculpt_smooth.dat
index 90ba16d846a..d480913608d 100644
--- a/release/datafiles/icons/ops.gpencil.sculpt_smooth.dat
+++ b/release/datafiles/icons/ops.gpencil.sculpt_smooth.dat
Binary files differ
diff --git a/release/datafiles/icons/ops.gpencil.sculpt_strength.dat b/release/datafiles/icons/ops.gpencil.sculpt_strength.dat
index d3f48931831..3b1732ab1da 100644
--- a/release/datafiles/icons/ops.gpencil.sculpt_strength.dat
+++ b/release/datafiles/icons/ops.gpencil.sculpt_strength.dat
Binary files differ
diff --git a/release/datafiles/icons/ops.gpencil.sculpt_weight.dat b/release/datafiles/icons/ops.gpencil.sculpt_weight.dat
index d5441fcbeb9..01c9587ec2e 100644
--- a/release/datafiles/icons/ops.gpencil.sculpt_weight.dat
+++ b/release/datafiles/icons/ops.gpencil.sculpt_weight.dat
Binary files differ
diff --git a/release/datafiles/icons/ops.paint.weight_sample.dat b/release/datafiles/icons/ops.paint.weight_sample.dat
index 423365f5a55..a428ae67e4c 100644
--- a/release/datafiles/icons/ops.paint.weight_sample.dat
+++ b/release/datafiles/icons/ops.paint.weight_sample.dat
Binary files differ
diff --git a/release/datafiles/icons/ops.paint.weight_sample_group.dat b/release/datafiles/icons/ops.paint.weight_sample_group.dat
index b37eb59ad23..863496f8908 100644
--- a/release/datafiles/icons/ops.paint.weight_sample_group.dat
+++ b/release/datafiles/icons/ops.paint.weight_sample_group.dat
Binary files differ
diff --git a/release/datafiles/icons/ops.sculpt.cloth_filter.dat b/release/datafiles/icons/ops.sculpt.cloth_filter.dat
new file mode 100644
index 00000000000..dc20c8f0bfd
--- /dev/null
+++ b/release/datafiles/icons/ops.sculpt.cloth_filter.dat
Binary files differ
diff --git a/release/datafiles/locale b/release/datafiles/locale
-Subproject a7bbfac76c00edd0fb79a4766b7ac7c5dcbcac5
+Subproject 2b3c19f5f61fc72dba56a7edfdc4e55e2327dc1
diff --git a/release/datafiles/userdef/userdef_default_theme.c b/release/datafiles/userdef/userdef_default_theme.c
index 569ea25eb51..942ad2fff56 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),
@@ -711,7 +712,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/freedesktop/org.blender.Blender.appdata.xml b/release/freedesktop/org.blender.Blender.appdata.xml
index 3cb09ed4d21..edadf9d7438 100644
--- a/release/freedesktop/org.blender.Blender.appdata.xml
+++ b/release/freedesktop/org.blender.Blender.appdata.xml
@@ -40,6 +40,31 @@
</screenshot>
</screenshots>
<releases>
+ <release version="2.90" date="2020-08-31">
+ <description>
+ <p>New features:</p>
+ <ul>
+ <li>Nishita sky texture for Cycles</li>
+ <li>EEVEE motion blur</li>
+ <li>Cloth filter</li>
+ <li>Ocean modifier spray</li>
+ <li>Correct face attributes modeling</li>
+ <li>OpenVDB fluid integration with mantaflow</li>
+ <li>Nuke lens distortion model for motion tracking</li>
+ </ul>
+ <p>Enhancements:</p>
+ <ul>
+ <li>Intel Embree for faster motion blur in Cycles</li>
+ <li>Viewport Intel OpenImageDenoise</li>
+ <li>Shadow terminator offset setting</li>
+ <li>NVLink support for CUDA and Optix</li>
+ <li>Multires unsubdivide, rebuild and modes</li>
+ <li>Pose brush transformations</li>
+ <li>Correct face attributes modeling</li>
+ <li>User interface improvements: new search, modifiers, headings, stats</li>
+ </ul>
+ </description>
+ </release>
<release version="2.83" date="2020-06-03">
<description>
<p>New features:</p>
diff --git a/release/scripts/addons b/release/scripts/addons
-Subproject 82ed41ec632483fa9260d90dae7afdf3192c509
+Subproject 49c39f59fbc464dd34388990123f271c39eacbf
diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib
-Subproject f2f4a8b3bfa36ee49f7bdb3a1acb40ef4b39ee3
+Subproject a52733b58d95ce60ecde95a9eca242e7319c285
diff --git a/release/scripts/modules/addon_utils.py b/release/scripts/modules/addon_utils.py
index af6d4b1cd29..6fd091cefc2 100644
--- a/release/scripts/modules/addon_utils.py
+++ b/release/scripts/modules/addon_utils.py
@@ -492,6 +492,8 @@ def disable_all():
item for item in sys.modules.items()
if getattr(item[1], "__addon_enabled__", False)
]
+ # Check the enabled state again since it's possible the disable call
+ # of one add-on disables others.
for mod_name, mod in addon_modules:
if getattr(mod, "__addon_enabled__", False):
disable(mod_name)
diff --git a/release/scripts/modules/bpy/ops.py b/release/scripts/modules/bpy/ops.py
index 4e226f80f79..c2245d908b5 100644
--- a/release/scripts/modules/bpy/ops.py
+++ b/release/scripts/modules/bpy/ops.py
@@ -27,6 +27,7 @@ op_poll = ops_module.poll
op_call = ops_module.call
op_as_string = ops_module.as_string
op_get_rna_type = ops_module.get_rna_type
+op_get_bl_options = ops_module.get_bl_options
class BPyOps:
@@ -209,6 +210,10 @@ class BPyOpsSubModOp:
"""Internal function for introspection"""
return op_get_rna_type(self.idname())
+ @property
+ def bl_options(self):
+ return op_get_bl_options(self.idname())
+
def __repr__(self): # useful display, repr(op)
# import bpy
return op_as_string(self.idname())
diff --git a/release/scripts/modules/nodeitems_utils.py b/release/scripts/modules/nodeitems_utils.py
index 595b6c8ee5f..d1e1cc5e346 100644
--- a/release/scripts/modules/nodeitems_utils.py
+++ b/release/scripts/modules/nodeitems_utils.py
@@ -144,6 +144,14 @@ def node_categories_iter(context):
yield cat
+def has_node_categories(context):
+ for cat_type in _node_categories.values():
+ for cat in cat_type[0]:
+ if cat.poll and ((context is None) or cat.poll(context)):
+ return True
+ return False
+
+
def node_items_iter(context):
for cat in node_categories_iter(context):
for item in cat.items(context):
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/modules/rna_prop_ui.py b/release/scripts/modules/rna_prop_ui.py
index 662c1d908fc..98722ea322b 100644
--- a/release/scripts/modules/rna_prop_ui.py
+++ b/release/scripts/modules/rna_prop_ui.py
@@ -309,13 +309,15 @@ def draw(layout, context, context_member, property_type, use_edit=True):
# Do not allow editing of overridden properties (we cannot use a poll function of the operators here
# since they's have no access to the specific property...).
row.enabled = not(is_lib_override and key in rna_item.id_data.override_library.reference)
- if not is_rna:
+ if is_rna:
+ row.label(text="API Defined")
+ elif is_lib_override:
+ row.label(text="Library Override")
+ else:
props = row.operator("wm.properties_edit", text="Edit")
assign_props(props, val_draw, key)
props = row.operator("wm.properties_remove", text="", icon='REMOVE')
assign_props(props, val_draw, key)
- else:
- row.label(text="API Defined")
del flow
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/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
index 6556c2f34e0..c3c6e77067e 100644
--- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py
+++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
@@ -1926,6 +1926,12 @@ def km_file_browser_main(params):
items.extend([
("file.execute", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'},
{"properties": [("need_active", True)]}),
+ # Both .execute and .select are needed here. The former only works if
+ # there's a file operator (i.e. not in regular editor mode) but is
+ # needed to load files. The latter makes selection work if there's no
+ # operator (i.e. in regular editor mode).
+ ("file.select", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'},
+ {"properties": [("open", True), ("deselect_all", not params.legacy)]}),
("file.refresh", {"type": 'NUMPAD_PERIOD', "value": 'PRESS'}, None),
("file.select", {"type": 'LEFTMOUSE', "value": 'PRESS'},
{"properties": [("open", False), ("deselect_all", not params.legacy)]}),
@@ -3370,7 +3376,7 @@ def km_grease_pencil_stroke_paint_fill(params):
("gpencil.draw", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
{"properties": [("mode", 'DRAW'), ("wait_for_input", False), ("disable_straight", True), ("disable_stabilizer", True)]}),
# If press alternative key, the brush now it's for drawing lines
- ("gpencil.draw", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True, "shift": True},
+ ("gpencil.draw", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True},
{"properties": [("mode", 'DRAW'), ("wait_for_input", False), ("disable_straight", True), ("disable_stabilizer", True), ("disable_fill", True)]}),
])
@@ -4946,6 +4952,7 @@ def km_transform_modal_map(_params):
("CONFIRM", {"type": 'LEFTMOUSE', "value": 'PRESS', "any": True}, None),
("CONFIRM", {"type": 'RET', "value": 'PRESS', "any": True}, None),
("CONFIRM", {"type": 'NUMPAD_ENTER', "value": 'PRESS', "any": True}, None),
+ ("CONFIRM", {"type": 'SPACE', "value": 'PRESS', "any": True}, None),
("CANCEL", {"type": 'RIGHTMOUSE', "value": 'PRESS', "any": True}, None),
("CANCEL", {"type": 'ESC', "value": 'PRESS', "any": True}, None),
("AXIS_X", {"type": 'X', "value": 'PRESS', "repeat": False}, None),
@@ -4964,6 +4971,7 @@ def km_transform_modal_map(_params):
("SNAP_INV_ON", {"type": 'RIGHT_CTRL', "value": 'PRESS', "any": True}, None),
("SNAP_INV_OFF", {"type": 'RIGHT_CTRL', "value": 'RELEASE', "any": True}, None),
("ADD_SNAP", {"type": 'A', "value": 'PRESS', "repeat": False}, None),
+ ("ADD_SNAP", {"type": 'A', "value": 'PRESS', "ctrl": True, "repeat": False}, None),
("REMOVE_SNAP", {"type": 'A', "value": 'PRESS', "alt": True, "repeat": False}, None),
("PROPORTIONAL_SIZE_UP", {"type": 'PAGE_UP', "value": 'PRESS'}, None),
("PROPORTIONAL_SIZE_DOWN", {"type": 'PAGE_DOWN', "value": 'PRESS'}, None),
@@ -4976,11 +4984,17 @@ def km_transform_modal_map(_params):
("PROPORTIONAL_SIZE", {"type": 'TRACKPADPAN', "value": 'ANY'}, None),
("EDGESLIDE_EDGE_NEXT", {"type": 'WHEELDOWNMOUSE', "value": 'PRESS', "alt": True}, None),
("EDGESLIDE_PREV_NEXT", {"type": 'WHEELUPMOUSE', "value": 'PRESS', "alt": True}, None),
+ ("AUTOIK_CHAIN_LEN_UP", {"type": 'PAGE_UP', "value": 'PRESS'}, None),
+ ("AUTOIK_CHAIN_LEN_DOWN", {"type": 'PAGE_DOWN', "value": 'PRESS'}, None),
("AUTOIK_CHAIN_LEN_UP", {"type": 'PAGE_UP', "value": 'PRESS', "shift": True}, None),
("AUTOIK_CHAIN_LEN_DOWN", {"type": 'PAGE_DOWN', "value": 'PRESS', "shift": True}, None),
+ ("AUTOIK_CHAIN_LEN_UP", {"type": 'WHEELDOWNMOUSE', "value": 'PRESS'}, None),
+ ("AUTOIK_CHAIN_LEN_DOWN", {"type": 'WHEELUPMOUSE', "value": 'PRESS'}, None),
("AUTOIK_CHAIN_LEN_UP", {"type": 'WHEELDOWNMOUSE', "value": 'PRESS', "shift": True}, None),
("AUTOIK_CHAIN_LEN_DOWN", {"type": 'WHEELUPMOUSE', "value": 'PRESS', "shift": True}, None),
("INSERTOFS_TOGGLE_DIR", {"type": 'T', "value": 'PRESS', "repeat": False}, None),
+ ("AUTOCONSTRAIN", {"type": 'MIDDLEMOUSE', "value": 'PRESS', "repeat": False}, None),
+ ("AUTOCONSTRAIN", {"type": 'MIDDLEMOUSE', "value": 'PRESS', "repeat": False, "shift": True}, None),
])
return keymap
diff --git a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py
index 04721a1704a..bb921565374 100644
--- a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py
+++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py
@@ -2471,7 +2471,7 @@ def km_grease_pencil_stroke_paint_fill(params):
("gpencil.draw", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
{"properties": [("mode", 'DRAW'), ("wait_for_input", False), ("disable_straight", True), ("disable_stabilizer", True)]}),
# If press alternative key, the brush now it's for drawing lines
- ("gpencil.draw", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True, "shift": True},
+ ("gpencil.draw", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True},
{"properties": [("mode", 'DRAW'), ("wait_for_input", False), ("disable_straight", True), ("disable_stabilizer", True), ("disable_fill", True)]}),
])
@@ -3894,6 +3894,7 @@ def km_transform_modal_map(_params):
("CONFIRM", {"type": 'LEFTMOUSE', "value": 'PRESS', "any": True}, None),
("CONFIRM", {"type": 'RET', "value": 'PRESS', "any": True}, None),
("CONFIRM", {"type": 'NUMPAD_ENTER', "value": 'PRESS', "any": True}, None),
+ ("CONFIRM", {"type": 'SPACE', "value": 'PRESS', "any": True}, None),
("CANCEL", {"type": 'RIGHTMOUSE', "value": 'PRESS', "any": True}, None),
("CANCEL", {"type": 'ESC', "value": 'PRESS', "any": True}, None),
("AXIS_X", {"type": 'X', "value": 'PRESS'}, None),
@@ -3912,6 +3913,7 @@ def km_transform_modal_map(_params):
("SNAP_INV_ON", {"type": 'RIGHT_CTRL', "value": 'PRESS', "any": True}, None),
("SNAP_INV_OFF", {"type": 'RIGHT_CTRL', "value": 'RELEASE', "any": True}, None),
("ADD_SNAP", {"type": 'A', "value": 'PRESS'}, None),
+ ("ADD_SNAP", {"type": 'A', "value": 'PRESS', "ctrl": True}, None),
("REMOVE_SNAP", {"type": 'A', "value": 'PRESS', "alt": True}, None),
("PROPORTIONAL_SIZE_UP", {"type": 'PAGE_UP', "value": 'PRESS'}, None),
("PROPORTIONAL_SIZE_DOWN", {"type": 'PAGE_DOWN', "value": 'PRESS'}, None),
@@ -3924,11 +3926,17 @@ def km_transform_modal_map(_params):
("PROPORTIONAL_SIZE", {"type": 'TRACKPADPAN', "value": 'ANY'}, None),
("EDGESLIDE_EDGE_NEXT", {"type": 'WHEELDOWNMOUSE', "value": 'PRESS', "alt": True}, None),
("EDGESLIDE_PREV_NEXT", {"type": 'WHEELUPMOUSE', "value": 'PRESS', "alt": True}, None),
+ ("AUTOIK_CHAIN_LEN_UP", {"type": 'PAGE_UP', "value": 'PRESS'}, None),
+ ("AUTOIK_CHAIN_LEN_DOWN", {"type": 'PAGE_DOWN', "value": 'PRESS'}, None),
("AUTOIK_CHAIN_LEN_UP", {"type": 'PAGE_UP', "value": 'PRESS', "shift": True}, None),
("AUTOIK_CHAIN_LEN_DOWN", {"type": 'PAGE_DOWN', "value": 'PRESS', "shift": True}, None),
+ ("AUTOIK_CHAIN_LEN_UP", {"type": 'WHEELDOWNMOUSE', "value": 'PRESS'}, None),
+ ("AUTOIK_CHAIN_LEN_DOWN", {"type": 'WHEELUPMOUSE', "value": 'PRESS'}, None),
("AUTOIK_CHAIN_LEN_UP", {"type": 'WHEELDOWNMOUSE', "value": 'PRESS', "shift": True}, None),
("AUTOIK_CHAIN_LEN_DOWN", {"type": 'WHEELUPMOUSE', "value": 'PRESS', "shift": True}, None),
("INSERTOFS_TOGGLE_DIR", {"type": 'T', "value": 'PRESS'}, None),
+ ("AUTOCONSTRAIN", {"type": 'MIDDLEMOUSE', "value": 'PRESS'}, None),
+ ("AUTOCONSTRAIN", {"type": 'MIDDLEMOUSE', "value": 'PRESS', "shift": True}, None),
])
return keymap
diff --git a/release/scripts/startup/bl_app_templates_system/2D_Animation/__init__.py b/release/scripts/startup/bl_app_templates_system/2D_Animation/__init__.py
index 009cb65d150..1892bea41f7 100644
--- a/release/scripts/startup/bl_app_templates_system/2D_Animation/__init__.py
+++ b/release/scripts/startup/bl_app_templates_system/2D_Animation/__init__.py
@@ -55,6 +55,13 @@ def load_handler(dummy):
space.shading.type = 'MATERIAL'
space.shading.use_scene_world = True
+ # Grease pencil object
+ scene = bpy.data.scenes[0]
+ if scene:
+ for ob in scene.objects:
+ if ob.type == 'GPENCIL':
+ gpd = ob.data
+ gpd.onion_keyframe_type = 'ALL'
def register():
bpy.app.handlers.load_factory_startup_post.append(load_handler)
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/userpref.py b/release/scripts/startup/bl_operators/userpref.py
index e92f493960a..ceef2df63ff 100644
--- a/release/scripts/startup/bl_operators/userpref.py
+++ b/release/scripts/startup/bl_operators/userpref.py
@@ -354,7 +354,7 @@ class PREFERENCES_OT_keyitem_restore(Operator):
item_id: IntProperty(
name="Item Identifier",
- description="Identifier of the item to remove",
+ description="Identifier of the item to restore",
)
@classmethod
diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py
index 570b4663f1d..59abff12834 100644
--- a/release/scripts/startup/bl_operators/wm.py
+++ b/release/scripts/startup/bl_operators/wm.py
@@ -1276,21 +1276,19 @@ class WM_OT_properties_edit(Operator):
# First remove
item = eval("context.%s" % data_path)
+
+ if (item.id_data and item.id_data.override_library and item.id_data.override_library.reference):
+ self.report({'ERROR'}, "Cannot edit properties from override data")
+ return {'CANCELLED'}
+
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]
@@ -1375,9 +1373,14 @@ class WM_OT_properties_edit(Operator):
item = eval("context.%s" % data_path)
+ if (item.id_data and item.id_data.override_library and item.id_data.override_library.reference):
+ self.report({'ERROR'}, "Cannot edit properties from override data")
+ 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())
@@ -1498,6 +1501,10 @@ class WM_OT_properties_add(Operator):
data_path = self.data_path
item = eval("context.%s" % data_path)
+ if (item.id_data and item.id_data.override_library and item.id_data.override_library.reference):
+ self.report({'ERROR'}, "Cannot add properties to override data")
+ return {'CANCELLED'}
+
def unique_name(names):
prop = "prop"
prop_new = prop
@@ -1550,6 +1557,11 @@ class WM_OT_properties_remove(Operator):
)
data_path = self.data_path
item = eval("context.%s" % data_path)
+
+ if (item.id_data and item.id_data.override_library and item.id_data.override_library.reference):
+ self.report({'ERROR'}, "Cannot remove properties from override data")
+ return {'CANCELLED'}
+
prop = self.property
rna_idprop_ui_prop_update(item, prop)
del item[prop]
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 7ff2688f2a7..64bc694d5ca 100644
--- a/release/scripts/startup/bl_ui/properties_data_mesh.py
+++ b/release/scripts/startup/bl_ui/properties_data_mesh.py
@@ -468,7 +468,7 @@ class DATA_PT_sculpt_vertex_colors(MeshButtonsPanel, Panel):
@classmethod
def poll(cls, context):
- return context.preferences.experimental.use_sculpt_vertex_colors
+ return super().poll(context) and context.preferences.experimental.use_sculpt_vertex_colors
def draw(self, context):
layout = self.layout
diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py
index 397ce5d0324..a20de3e29db 100644
--- a/release/scripts/startup/bl_ui/properties_paint_common.py
+++ b/release/scripts/startup/bl_ui/properties_paint_common.py
@@ -313,6 +313,7 @@ class TextureMaskPanel(BrushPanel):
class StrokePanel(BrushPanel):
bl_label = "Stroke"
bl_options = {'DEFAULT_CLOSED'}
+ bl_ui_units_x = 13
def draw(self, context):
layout = self.layout
@@ -640,6 +641,8 @@ def brush_settings(layout, context, brush, popover=False):
elif sculpt_tool == 'POSE':
layout.separator()
+ layout.prop(brush, "deform_target")
+ layout.separator()
layout.prop(brush, "pose_deform_type")
layout.prop(brush, "pose_origin_type")
layout.prop(brush, "pose_offset")
@@ -720,8 +723,12 @@ def brush_settings(layout, context, brush, popover=False):
col.prop(brush, "smear_deform_type")
elif sculpt_tool == 'BOUNDARY':
+ layout.prop(brush, "deform_target")
+ layout.separator()
col = layout.column()
col.prop(brush, "boundary_deform_type")
+ col.prop(brush, "boundary_falloff_type")
+ col.prop(brush, "boundary_offset")
elif sculpt_tool == 'TOPOLOGY':
col = layout.column()
diff --git a/release/scripts/startup/bl_ui/properties_view_layer.py b/release/scripts/startup/bl_ui/properties_view_layer.py
index 3645f0dc2f2..36bbf78d8ab 100644
--- a/release/scripts/startup/bl_ui/properties_view_layer.py
+++ b/release/scripts/startup/bl_ui/properties_view_layer.py
@@ -111,9 +111,7 @@ class VIEWLAYER_PT_eevee_layer_passes_light(ViewLayerButtonsPanel, Panel):
col.prop(view_layer, "use_pass_emit", text="Emission")
col.prop(view_layer, "use_pass_environment")
col.prop(view_layer, "use_pass_shadow")
- row = col.row()
- row.prop(view_layer, "use_pass_ambient_occlusion", text="Ambient Occlusion")
- row.active = scene_eevee.use_gtao
+ col.prop(view_layer, "use_pass_ambient_occlusion", text="Ambient Occlusion")
class VIEWLAYER_PT_eevee_layer_passes_effects(ViewLayerButtonsPanel, Panel):
diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py
index 75c1bb5e3f9..3aedc04af88 100644
--- a/release/scripts/startup/bl_ui/space_image.py
+++ b/release/scripts/startup/bl_ui/space_image.py
@@ -1137,6 +1137,7 @@ class IMAGE_PT_paint_settings_advanced(Panel, ImagePaintPanel):
bl_parent_id = "IMAGE_PT_paint_settings"
bl_category = "Tool"
bl_label = "Advanced"
+ bl_ui_units_x = 12
def draw(self, context):
layout = self.layout
@@ -1193,6 +1194,7 @@ class IMAGE_PT_tools_brush_display(Panel, BrushButtonsPanel, DisplayPanel):
bl_category = "Tool"
bl_label = "Brush Tip"
bl_options = {'DEFAULT_CLOSED'}
+ bl_ui_units_x = 15
class IMAGE_PT_tools_brush_texture(BrushButtonsPanel, Panel):
@@ -1219,6 +1221,7 @@ class IMAGE_PT_tools_mask_texture(Panel, BrushButtonsPanel, TextureMaskPanel):
bl_parent_id = "IMAGE_PT_paint_settings"
bl_category = "Tool"
bl_label = "Texture Mask"
+ bl_ui_units_x = 12
class IMAGE_PT_paint_stroke(BrushButtonsPanel, Panel, StrokePanel):
diff --git a/release/scripts/startup/bl_ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py
index faf4036f9b3..93be870ce73 100644
--- a/release/scripts/startup/bl_ui/space_node.py
+++ b/release/scripts/startup/bl_ui/space_node.py
@@ -216,13 +216,15 @@ class NODE_MT_add(bpy.types.Menu):
layout = self.layout
layout.operator_context = 'INVOKE_DEFAULT'
- props = layout.operator("node.add_search", text="Search...", icon='VIEWZOOM')
- props.use_transform = True
- layout.separator()
+ if nodeitems_utils.has_node_categories(context):
+ props = layout.operator("node.add_search", text="Search...", icon='VIEWZOOM')
+ props.use_transform = True
+
+ layout.separator()
- # actual node submenus are defined by draw functions from node categories
- nodeitems_utils.draw_node_categories_menu(self, context)
+ # actual node submenus are defined by draw functions from node categories
+ nodeitems_utils.draw_node_categories_menu(self, context)
class NODE_MT_view(Menu):
diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py
index 9f251a9abad..1164ff82e3c 100644
--- a/release/scripts/startup/bl_ui/space_sequencer.py
+++ b/release/scripts/startup/bl_ui/space_sequencer.py
@@ -327,6 +327,7 @@ class SEQUENCER_MT_view(Menu):
if is_preview:
layout.separator()
if st.display_mode == 'IMAGE':
+ layout.prop(st, "use_zoom_to_fit")
layout.prop(ed, "show_overlay", text="Show Frame Overlay")
layout.prop(st, "show_safe_areas", text="Show Safe Areas")
layout.prop(st, "show_metadata", text="Show Metadata")
diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
index 38879d41a64..e336635a4ee 100644
--- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
@@ -1260,7 +1260,9 @@ class _defs_sculpt:
props = tool.operator_properties("sculpt.mesh_filter")
layout.prop(props, "type", expand=False)
layout.prop(props, "strength")
- layout.prop(props, "deform_axis")
+ row = layout.row(align=True)
+ row.prop(props, "deform_axis")
+ layout.prop(props, "orientation", expand=False)
layout.prop(props, "use_face_sets")
if props.type == 'SURFACE_SMOOTH':
layout.prop(props, "surface_smooth_shape_preservation", expand=False)
@@ -1285,6 +1287,9 @@ class _defs_sculpt:
props = tool.operator_properties("sculpt.cloth_filter")
layout.prop(props, "type", expand=False)
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_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py
index 03f85578b6e..9548de20752 100644
--- a/release/scripts/startup/bl_ui/space_userpref.py
+++ b/release/scripts/startup/bl_ui/space_userpref.py
@@ -2118,6 +2118,10 @@ class ExperimentalPanel:
url_prefix = "https://developer.blender.org/"
+ @classmethod
+ def poll(cls, context):
+ return bpy.app.version_cycle == 'alpha'
+
def _draw_items(self, context, items):
prefs = context.preferences
experimental = prefs.experimental
@@ -2178,6 +2182,12 @@ class USERPREF_PT_experimental_prototypes(ExperimentalPanel, Panel):
class USERPREF_PT_experimental_debugging(ExperimentalPanel, Panel):
bl_label = "Debugging"
+ @classmethod
+ def poll(cls, context):
+ # Unlike the other experimental panels, the debugging one is always visible
+ # even in beta or release.
+ return True
+
def draw(self, context):
self._draw_items(
context, (
diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index c251b4bfcac..c49f5b3e5d1 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -920,9 +920,8 @@ class ShowHideMenu:
layout.operator("%s.hide" % self._operator_name, text="Hide Unselected").unselected = True
-# Standard transforms which apply to all cases
-# NOTE: this doesn't seem to be able to be used directly
-class VIEW3D_MT_transform_base(Menu):
+# Standard transforms which apply to all cases (mix-in class, not used directly).
+class VIEW3D_MT_transform_base:
bl_label = "Transform"
bl_category = "View"
@@ -949,7 +948,7 @@ class VIEW3D_MT_transform_base(Menu):
# Generic transform menu - geometry types
-class VIEW3D_MT_transform(VIEW3D_MT_transform_base):
+class VIEW3D_MT_transform(VIEW3D_MT_transform_base, Menu):
def draw(self, context):
# base menu
VIEW3D_MT_transform_base.draw(self, context)
@@ -969,7 +968,7 @@ class VIEW3D_MT_transform(VIEW3D_MT_transform_base):
# Object-specific extensions to Transform menu
-class VIEW3D_MT_transform_object(VIEW3D_MT_transform_base):
+class VIEW3D_MT_transform_object(VIEW3D_MT_transform_base, Menu):
def draw(self, context):
layout = self.layout
@@ -1001,7 +1000,7 @@ class VIEW3D_MT_transform_object(VIEW3D_MT_transform_base):
# Armature EditMode extensions to Transform menu
-class VIEW3D_MT_transform_armature(VIEW3D_MT_transform_base):
+class VIEW3D_MT_transform_armature(VIEW3D_MT_transform_base, Menu):
def draw(self, context):
layout = self.layout
@@ -2287,6 +2286,7 @@ class VIEW3D_MT_object(Menu):
layout.separator()
layout.menu("VIEW3D_MT_object_showhide")
+ layout.menu("VIEW3D_MT_object_cleanup")
layout.separator()
@@ -2726,6 +2726,20 @@ class VIEW3D_MT_object_showhide(Menu):
layout.operator("object.hide_view_set", text="Hide Unselected").unselected = True
+class VIEW3D_MT_object_cleanup(Menu):
+ bl_label = "Clean Up"
+
+ def draw(self, _context):
+ layout = self.layout
+
+ layout.operator("object.vertex_group_clean", text="Clean Vertex Group Weights").group_select_mode = 'ALL'
+ layout.operator("object.vertex_group_limit_total", text="Limit Total Vertex Groups").group_select_mode = 'ALL'
+
+ layout.separator()
+
+ layout.operator("object.material_slot_remove_unused", text="Remove Unused Material Slots")
+
+
class VIEW3D_MT_make_single_user(Menu):
bl_label = "Make Single User"
@@ -5942,7 +5956,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 +5967,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 +6126,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 +6153,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="")
@@ -6480,7 +6500,7 @@ class VIEW3D_PT_overlay_vertex_paint(Panel):
col = layout.column()
col.active = display_all
- col.prop(overlay, "vertex_paint_mode_opacity", text="Opacity")
+ col.prop(overlay, "vertex_paint_mode_opacity")
col.prop(overlay, "show_paint_wire")
@@ -6569,6 +6589,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")
@@ -7376,7 +7398,6 @@ classes = (
VIEW3D_HT_tool_header,
VIEW3D_MT_editor_menus,
VIEW3D_MT_transform,
- VIEW3D_MT_transform_base,
VIEW3D_MT_transform_object,
VIEW3D_MT_transform_armature,
VIEW3D_MT_mirror,
@@ -7439,6 +7460,7 @@ classes = (
VIEW3D_MT_object_constraints,
VIEW3D_MT_object_quick_effects,
VIEW3D_MT_object_showhide,
+ VIEW3D_MT_object_cleanup,
VIEW3D_MT_make_single_user,
VIEW3D_MT_make_links,
VIEW3D_MT_brush_paint_modes,
diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
index 1b8869d0bc8..dfabf5e9d81 100644
--- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
@@ -392,6 +392,7 @@ class VIEW3D_PT_tools_brush_settings_advanced(Panel, View3DPaintBrushPanel):
bl_parent_id = "VIEW3D_PT_tools_brush_settings"
bl_label = "Advanced"
bl_options = {'DEFAULT_CLOSED'}
+ bl_ui_units_x = 14
def draw(self, context):
layout = self.layout
@@ -598,6 +599,7 @@ class VIEW3D_PT_tools_brush_display(Panel, View3DPaintBrushPanel, DisplayPanel):
bl_parent_id = "VIEW3D_PT_tools_brush_settings"
bl_label = "Cursor"
bl_options = {'DEFAULT_CLOSED'}
+ bl_ui_units_x = 12
# TODO, move to space_view3d.py
@@ -828,6 +830,7 @@ class VIEW3D_PT_sculpt_options(Panel, View3DPaintPanel):
bl_context = ".sculpt_mode" # dot on purpose (access from topbar)
bl_label = "Options"
bl_options = {'DEFAULT_CLOSED'}
+ bl_ui_units_x = 12
@classmethod
def poll(cls, context):
@@ -1375,7 +1378,7 @@ class VIEW3D_PT_tools_grease_pencil_brush_advanced(View3DPanel, Panel):
bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_brush_settings'
bl_category = "Tool"
bl_options = {'DEFAULT_CLOSED'}
- bl_ui_units_x = 12
+ bl_ui_units_x = 11
@classmethod
def poll(cls, context):
@@ -1449,6 +1452,7 @@ class VIEW3D_PT_tools_grease_pencil_brush_stroke(Panel, View3DPanel):
bl_label = "Stroke"
bl_category = "Tool"
bl_options = {'DEFAULT_CLOSED'}
+ bl_ui_units_x = 12
@classmethod
def poll(cls, context):
@@ -2125,6 +2129,7 @@ class VIEW3D_PT_tools_grease_pencil_paint_appearance(GreasePencilDisplayPanel, P
bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_brush_settings'
bl_label = "Cursor"
bl_category = "Tool"
+ bl_ui_units_x = 15
class VIEW3D_PT_tools_grease_pencil_sculpt_appearance(GreasePencilDisplayPanel, Panel, View3DPanel):
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/blenfont/BLF_api.h b/source/blender/blenfont/BLF_api.h
index 9013836fd1e..bf84f5c57b3 100644
--- a/source/blender/blenfont/BLF_api.h
+++ b/source/blender/blenfont/BLF_api.h
@@ -42,8 +42,7 @@ int BLF_init(void);
void BLF_exit(void);
void BLF_default_dpi(int dpi);
void BLF_default_set(int fontid);
-int BLF_default(void); /* get default font ID so we can pass it to other functions */
-void BLF_batch_reset(void); /* call when changing opengl context. */
+int BLF_default(void); /* get default font ID so we can pass it to other functions */
void BLF_cache_clear(void);
diff --git a/source/blender/blenfont/intern/blf.c b/source/blender/blenfont/intern/blf.c
index 95b074fa2df..c5c2bc3f3ba 100644
--- a/source/blender/blenfont/intern/blf.c
+++ b/source/blender/blenfont/intern/blf.c
@@ -125,11 +125,6 @@ void BLF_exit(void)
blf_font_exit();
}
-void BLF_batch_reset(void)
-{
- blf_batch_draw_vao_clear();
-}
-
void BLF_cache_clear(void)
{
FontBLF *font;
diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c
index ff31878a929..5724d844089 100644
--- a/source/blender/blenfont/intern/blf_font.c
+++ b/source/blender/blenfont/intern/blf_font.c
@@ -113,13 +113,6 @@ static void blf_batch_draw_exit(void)
GPU_BATCH_DISCARD_SAFE(g_batch.batch);
}
-void blf_batch_draw_vao_clear(void)
-{
- if (g_batch.batch) {
- GPU_batch_vao_cache_clear(g_batch.batch);
- }
-}
-
void blf_batch_draw_begin(FontBLF *font)
{
if (g_batch.batch == NULL) {
@@ -228,9 +221,7 @@ void blf_batch_draw(void)
return;
}
- GPU_blend(true);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
#ifndef BLF_STANDALONE
/* We need to flush widget base first to ensure correct ordering. */
@@ -246,7 +237,7 @@ void blf_batch_draw(void)
GPU_batch_uniform_1i(g_batch.batch, "glyph", 0);
GPU_batch_draw(g_batch.batch);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
/* restart to 1st vertex data pointers */
GPU_vertbuf_attr_get_raw_data(g_batch.verts, g_batch.pos_loc, &g_batch.pos_step);
diff --git a/source/blender/blenfont/intern/blf_internal.h b/source/blender/blenfont/intern/blf_internal.h
index ba0873f4fd4..b616f47a897 100644
--- a/source/blender/blenfont/intern/blf_internal.h
+++ b/source/blender/blenfont/intern/blf_internal.h
@@ -30,7 +30,6 @@ struct ResultBLF;
struct rctf;
struct rcti;
-void blf_batch_draw_vao_clear(void);
void blf_batch_draw_begin(struct FontBLF *font);
void blf_batch_draw(void);
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 6ea113d8828..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
@@ -50,6 +50,9 @@ extern "C" {
/** User readable version string. */
const char *BKE_blender_version_string(void);
+/* Returns true when version cycle is alpha, otherwise (beta, rc) returns false. */
+bool BKE_blender_version_is_alpha(void);
+
#ifdef __cplusplus
}
#endif
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_gpencil.h b/source/blender/blenkernel/BKE_gpencil.h
index 6defc2ffd68..88eef40ebd2 100644
--- a/source/blender/blenkernel/BKE_gpencil.h
+++ b/source/blender/blenkernel/BKE_gpencil.h
@@ -131,6 +131,11 @@ bool BKE_gpencil_merge_materials_table_get(struct Object *ob,
const float sat_threshold,
const float val_threshold,
struct GHash *r_mat_table);
+bool BKE_gpencil_merge_materials(struct Object *ob,
+ const float hue_threshold,
+ const float sat_threshold,
+ const float val_threshold,
+ int *r_removed);
/* statistics functions */
void BKE_gpencil_stats_update(struct bGPdata *gpd);
diff --git a/source/blender/blenkernel/BKE_gpencil_curve.h b/source/blender/blenkernel/BKE_gpencil_curve.h
index 3fbd0ce1a51..c61427c6c4a 100644
--- a/source/blender/blenkernel/BKE_gpencil_curve.h
+++ b/source/blender/blenkernel/BKE_gpencil_curve.h
@@ -35,9 +35,9 @@ void BKE_gpencil_convert_curve(struct Main *bmain,
struct Scene *scene,
struct Object *ob_gp,
struct Object *ob_cu,
- const bool gpencil_lines,
const bool use_collections,
- const bool only_stroke);
+ const float scale_thickness,
+ const float sample);
#ifdef __cplusplus
}
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_layer.h b/source/blender/blenkernel/BKE_layer.h
index 7fe932edf31..52503f08153 100644
--- a/source/blender/blenkernel/BKE_layer.h
+++ b/source/blender/blenkernel/BKE_layer.h
@@ -98,7 +98,7 @@ int BKE_layer_collection_findindex(struct ViewLayer *view_layer, const struct La
void BKE_main_collection_sync(const struct Main *bmain);
void BKE_scene_collection_sync(const struct Scene *scene);
void BKE_layer_collection_sync(const struct Scene *scene, struct ViewLayer *view_layer);
-void BKE_layer_collection_local_sync(struct ViewLayer *view_layer, struct View3D *v3d);
+void BKE_layer_collection_local_sync(struct ViewLayer *view_layer, const struct View3D *v3d);
void BKE_main_collection_sync_remap(const struct Main *bmain);
@@ -129,7 +129,7 @@ void BKE_layer_collection_isolate_global(struct Scene *scene,
struct LayerCollection *lc,
bool extend);
void BKE_layer_collection_isolate_local(struct ViewLayer *view_layer,
- struct View3D *v3d,
+ const struct View3D *v3d,
struct LayerCollection *lc,
bool extend);
void BKE_layer_collection_set_visible(struct ViewLayer *view_layer,
@@ -150,7 +150,7 @@ void BKE_layer_eval_view_layer_indexed(struct Depsgraph *depsgraph,
typedef struct ObjectsVisibleIteratorData {
struct ViewLayer *view_layer;
- struct View3D *v3d;
+ const struct View3D *v3d;
} ObjectsVisibleIteratorData;
void BKE_view_layer_selected_objects_iterator_begin(BLI_Iterator *iter, void *data_in);
@@ -169,7 +169,7 @@ struct ObjectsInModeIteratorData {
int object_mode;
int object_type;
struct ViewLayer *view_layer;
- struct View3D *v3d;
+ const struct View3D *v3d;
struct Base *base_active;
};
@@ -352,6 +352,23 @@ void BKE_view_layer_visible_bases_iterator_end(BLI_Iterator *iter);
/* layer_utils.c */
+struct ObjectsInViewLayerParams {
+ uint no_dup_data : 1;
+
+ bool (*filter_fn)(struct Object *ob, void *user_data);
+ void *filter_userdata;
+};
+
+struct Object **BKE_view_layer_array_selected_objects_params(
+ struct ViewLayer *view_layer,
+ const struct View3D *v3d,
+ uint *r_len,
+ const struct ObjectsInViewLayerParams *params);
+
+#define BKE_view_layer_array_selected_objects(view_layer, v3d, r_len, ...) \
+ BKE_view_layer_array_selected_objects_params( \
+ view_layer, v3d, r_len, &(const struct ObjectsInViewLayerParams)__VA_ARGS__)
+
struct ObjectsInModeParams {
int object_mode;
uint no_dup_data : 1;
@@ -361,13 +378,13 @@ struct ObjectsInModeParams {
};
Base **BKE_view_layer_array_from_bases_in_mode_params(struct ViewLayer *view_layer,
- struct View3D *v3d,
+ const struct View3D *v3d,
uint *r_len,
const struct ObjectsInModeParams *params);
struct Object **BKE_view_layer_array_from_objects_in_mode_params(
struct ViewLayer *view_layer,
- struct View3D *v3d,
+ const struct View3D *v3d,
uint *len,
const struct ObjectsInModeParams *params);
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_runtime.h b/source/blender/blenkernel/BKE_mesh_runtime.h
index adb7c357049..87b55c581a2 100644
--- a/source/blender/blenkernel/BKE_mesh_runtime.h
+++ b/source/blender/blenkernel/BKE_mesh_runtime.h
@@ -71,10 +71,10 @@ struct Mesh *mesh_get_eval_deform(struct Depsgraph *depsgraph,
struct Object *ob,
const struct CustomData_MeshMasks *dataMask);
-struct Mesh *mesh_create_eval_final_render(struct Depsgraph *depsgraph,
- struct Scene *scene,
- struct Object *ob,
- 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 Mesh *mesh_create_eval_final_index_render(struct Depsgraph *depsgraph,
struct Scene *scene,
@@ -82,11 +82,6 @@ struct Mesh *mesh_create_eval_final_index_render(struct Depsgraph *depsgraph,
const struct CustomData_MeshMasks *dataMask,
int index);
-struct Mesh *mesh_create_eval_final_view(struct Depsgraph *depsgraph,
- struct Scene *scene,
- struct Object *ob,
- const struct CustomData_MeshMasks *dataMask);
-
struct Mesh *mesh_create_eval_no_deform(struct Depsgraph *depsgraph,
struct Scene *scene,
struct Object *ob,
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 58687858a9e..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;
@@ -337,6 +350,11 @@ typedef struct SculptBoundary {
int vertices_capacity;
int num_vertices;
+ /* Distance from a vertex in the boundary to initial vertex indexed by vertex index, taking into
+ * account the length of all edges between them. Any vertex that is not in the boundary will have
+ * a distance of 0. */
+ float *distance;
+
/* Data for drawing the preview. */
SculptBoundaryPreviewEdge *edges;
int edges_capacity;
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 8cd86593873..05966ec3989 100644
--- a/source/blender/blenkernel/BKE_scene.h
+++ b/source/blender/blenkernel/BKE_scene.h
@@ -110,6 +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);
+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);
@@ -145,7 +146,7 @@ void BKE_scene_update_tag_audio_volume(struct Depsgraph *, struct Scene *scene);
void BKE_scene_graph_update_tagged(struct Depsgraph *depsgraph, struct Main *bmain);
void BKE_scene_graph_evaluated_ensure(struct Depsgraph *depsgraph, struct Main *bmain);
-void BKE_scene_graph_update_for_newframe(struct Depsgraph *depsgraph, struct Main *bmain);
+void BKE_scene_graph_update_for_newframe(struct Depsgraph *depsgraph);
void BKE_scene_view_layer_graph_evaluated_ensure(struct Main *bmain,
struct Scene *scene,
@@ -218,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_screen.h b/source/blender/blenkernel/BKE_screen.h
index edab543fc37..1090deae93f 100644
--- a/source/blender/blenkernel/BKE_screen.h
+++ b/source/blender/blenkernel/BKE_screen.h
@@ -183,6 +183,16 @@ typedef struct ARegionType {
/* return context data */
int (*context)(const struct bContext *C, const char *member, struct bContextDataResult *result);
+ /* Is called whenever the current visible View2D's region changes.
+ *
+ * Used from user code such as view navigation/zoom operators to inform region about changes.
+ * The goal is to support zoom-to-fit features which gets disabled when manual navigation is
+ * performed.
+ *
+ * This callback is not called on indirect changes of the current viewport (which could happen
+ * when the `v2d->tot is changed and `cur` is adopted accordingly). */
+ void (*on_view2d_changed)(const struct bContext *C, struct ARegion *region);
+
/* custom drawing callbacks */
ListBase drawcalls;
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/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index ada341ff570..6f32eb8a90f 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -36,8 +36,8 @@ set(INC
../makesrna
../modifiers
../nodes
- ../simulation
../shader_fx
+ ../simulation
../render/extern/include
../../../intern/ghost
../../../intern/glew-mx
@@ -365,8 +365,8 @@ set(SRC
BKE_packedFile.h
BKE_paint.h
BKE_particle.h
- BKE_persistent_data_handle.hh
BKE_pbvh.h
+ BKE_persistent_data_handle.hh
BKE_pointcache.h
BKE_pointcloud.h
BKE_report.h
@@ -440,9 +440,9 @@ set(LIB
bf_intern_opensubdiv # Uses stub when disabled.
bf_modifiers
bf_nodes
- bf_simulation
bf_rna
bf_shader_fx
+ bf_simulation
)
if(WITH_BINRELOC)
diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c
index 63e7933dd56..263f63cb6da 100644
--- a/source/blender/blenkernel/intern/DerivedMesh.c
+++ b/source/blender/blenkernel/intern/DerivedMesh.c
@@ -909,8 +909,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
const int required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime;
/* Sculpt can skip certain modifiers. */
- MultiresModifierData *mmd = get_multires_modifier(scene, ob, 0);
- const bool has_multires = (mmd && mmd->sculptlvl != 0);
+ const bool has_multires = BKE_sculpt_multires_active(scene, ob) != NULL;
bool multires_applied = false;
const bool sculpt_mode = ob->mode & OB_MODE_SCULPT && ob->sculpt && !use_render;
const bool sculpt_dyntopo = (sculpt_mode && ob->sculpt->bm) && !use_render;
@@ -1810,6 +1809,12 @@ static void mesh_build_data(struct Depsgraph *depsgraph,
BKE_object_boundbox_calc_from_mesh(ob, mesh_eval);
+ /* Make sure that drivers can target shapekey properties.
+ * Note that this causes a potential inconsistency, as the shapekey may have a
+ * different topology than the evaluated mesh. */
+ BLI_assert(mesh->key == NULL || DEG_is_evaluated_id(&mesh->key->id));
+ mesh_eval->key = mesh->key;
+
if ((ob->mode & OB_MODE_ALL_SCULPT) && ob->sculpt) {
if (DEG_is_active(depsgraph)) {
BKE_sculpt_update_object_after_eval(depsgraph, ob);
@@ -1985,10 +1990,10 @@ Mesh *mesh_get_eval_deform(struct Depsgraph *depsgraph,
return ob->runtime.mesh_deform_eval;
}
-Mesh *mesh_create_eval_final_render(Depsgraph *depsgraph,
- Scene *scene,
- Object *ob,
- const CustomData_MeshMasks *dataMask)
+Mesh *mesh_create_eval_final(Depsgraph *depsgraph,
+ Scene *scene,
+ Object *ob,
+ const CustomData_MeshMasks *dataMask)
{
Mesh *final;
@@ -2010,26 +2015,6 @@ Mesh *mesh_create_eval_final_index_render(Depsgraph *depsgraph,
return final;
}
-Mesh *mesh_create_eval_final_view(Depsgraph *depsgraph,
- Scene *scene,
- Object *ob,
- const CustomData_MeshMasks *dataMask)
-{
- Mesh *final;
-
- /* XXX hack
- * psys modifier updates particle state when called during dupli-list generation,
- * which can lead to wrong transforms. This disables particle system modifier execution.
- */
- ob->transflag |= OB_NO_PSYS_UPDATE;
-
- mesh_calc_modifiers(depsgraph, scene, ob, 1, false, dataMask, -1, false, false, NULL, &final);
-
- ob->transflag &= ~OB_NO_PSYS_UPDATE;
-
- return final;
-}
-
Mesh *mesh_create_eval_no_deform(Depsgraph *depsgraph,
Scene *scene,
Object *ob,
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/blender.c b/source/blender/blenkernel/intern/blender.c
index e8aa13a8beb..1d5c8f76cc5 100644
--- a/source/blender/blenkernel/intern/blender.c
+++ b/source/blender/blenkernel/intern/blender.c
@@ -135,6 +135,12 @@ const char *BKE_blender_version_string(void)
return blender_version_string;
}
+bool BKE_blender_version_is_alpha(void)
+{
+ bool is_alpha = STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "alpha");
+ return is_alpha;
+}
+
void BKE_blender_globals_init(void)
{
blender_version_init();
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..b35d2b199aa 100644
--- a/source/blender/blenkernel/intern/brush.c
+++ b/source/blender/blenkernel/intern/brush.c
@@ -35,6 +35,7 @@
#include "BKE_brush.h"
#include "BKE_colortools.h"
#include "BKE_context.h"
+#include "BKE_gpencil.h"
#include "BKE_icons.h"
#include "BKE_idtype.h"
#include "BKE_lib_id.h"
@@ -210,6 +211,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;
@@ -472,6 +479,12 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type)
CurveMapping *custom_curve = NULL;
+ /* Optionally assign a material preset. */
+ enum {
+ PRESET_MATERIAL_NONE = 0,
+ PRESET_MATERIAL_DOT_STROKE,
+ } material_preset = PRESET_MATERIAL_NONE;
+
/* Set general defaults at brush level. */
brush->smooth_stroke_radius = SMOOTH_STROKE_RADIUS;
brush->smooth_stroke_factor = SMOOTH_STROKE_FACTOR;
@@ -515,19 +528,10 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type)
brush->gpencil_tool = GPAINT_TOOL_DRAW;
brush->gpencil_settings->icon_id = GP_BRUSH_ICON_AIRBRUSH;
- /* Create and link Black Dots material to brush.
- * This material is required because the brush uses the material to define how the stroke is
- * drawn. */
- Material *ma = BLI_findstring(&bmain->materials, "Dots Stroke", offsetof(ID, name) + 2);
- if (ma == NULL) {
- ma = BKE_gpencil_material_add(bmain, "Dots Stroke");
- ma->gp_style->mode = GP_MATERIAL_MODE_DOT;
- }
- brush->gpencil_settings->material = ma;
- /* Pin the matterial to the brush. */
- brush->gpencil_settings->flag |= GP_BRUSH_MATERIAL_PINNED;
-
zero_v3(brush->secondary_rgb);
+
+ material_preset = PRESET_MATERIAL_DOT_STROKE;
+
break;
}
case GP_BRUSH_PRESET_INK_PEN: {
@@ -740,19 +744,10 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type)
brush->gpencil_settings->icon_id = GP_BRUSH_ICON_PENCIL;
brush->gpencil_tool = GPAINT_TOOL_DRAW;
- /* Create and link Black Dots material to brush.
- * This material is required because the brush uses the material to define how the stroke is
- * drawn. */
- Material *ma = BLI_findstring(&bmain->materials, "Dots Stroke", offsetof(ID, name) + 2);
- if (ma == NULL) {
- ma = BKE_gpencil_material_add(bmain, "Dots Stroke");
- ma->gp_style->mode = GP_MATERIAL_MODE_DOT;
- }
- brush->gpencil_settings->material = ma;
- /* Pin the matterial to the brush. */
- brush->gpencil_settings->flag |= GP_BRUSH_MATERIAL_PINNED;
-
zero_v3(brush->secondary_rgb);
+
+ material_preset = PRESET_MATERIAL_DOT_STROKE;
+
break;
}
case GP_BRUSH_PRESET_PENCIL: {
@@ -1064,6 +1059,30 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type)
default:
break;
}
+
+ switch (material_preset) {
+ case PRESET_MATERIAL_NONE:
+ break;
+ case PRESET_MATERIAL_DOT_STROKE: {
+ /* Create and link Black Dots material to brush.
+ * This material is required because the brush uses the material
+ * to define how the stroke is drawn. */
+ const char *ma_id = "Dots Stroke";
+ Material *ma = BLI_findstring(&bmain->materials, ma_id, offsetof(ID, name) + 2);
+ if (ma == NULL) {
+ ma = BKE_gpencil_material_add(bmain, ma_id);
+ ma->gp_style->mode = GP_MATERIAL_MODE_DOT;
+ BLI_assert(ma->id.us == 1);
+ id_us_min(&ma->id);
+ }
+
+ BKE_gpencil_brush_material_set(brush, ma);
+
+ /* Pin the material to the brush. */
+ brush->gpencil_settings->flag |= GP_BRUSH_MATERIAL_PINNED;
+ break;
+ }
+ }
}
static Brush *gpencil_brush_ensure(
@@ -1539,7 +1558,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/cloth.c b/source/blender/blenkernel/intern/cloth.c
index 027761335b0..24b4b85d0d4 100644
--- a/source/blender/blenkernel/intern/cloth.c
+++ b/source/blender/blenkernel/intern/cloth.c
@@ -1611,7 +1611,6 @@ static int cloth_build_springs(ClothModifierData *clmd, Mesh *mesh)
if (use_internal_springs && numpolys > 0) {
BVHTreeFromMesh treedata = {NULL};
unsigned int tar_v_idx;
- BLI_bitmap *verts_used = NULL;
Mesh *tmp_mesh = NULL;
RNG *rng;
@@ -1622,7 +1621,7 @@ static int cloth_build_springs(ClothModifierData *clmd, Mesh *mesh)
BKE_mesh_calc_normals(tmp_mesh);
}
- verts_used = BLI_BITMAP_NEW(mvert_num * mvert_num, __func__);
+ EdgeSet *existing_vert_pairs = BLI_edgeset_new("cloth_sewing_edges_graph");
BKE_bvhtree_from_mesh_get(&treedata, tmp_mesh ? tmp_mesh : mesh, BVHTREE_FROM_LOOPTRI, 2);
rng = BLI_rng_new_srandom(0);
@@ -1635,12 +1634,12 @@ static int cloth_build_springs(ClothModifierData *clmd, Mesh *mesh)
clmd->sim_parms->internal_spring_max_diversion,
(clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_INTERNAL_SPRINGS_NORMAL),
&tar_v_idx)) {
- if (BLI_BITMAP_TEST_BOOL(verts_used, i * mvert_num + tar_v_idx)) {
+ if (BLI_edgeset_haskey(existing_vert_pairs, i, tar_v_idx)) {
+ /* We have already created a spring between these verts! */
continue;
}
- BLI_BITMAP_ENABLE(verts_used, i * mvert_num + tar_v_idx);
- BLI_BITMAP_ENABLE(verts_used, tar_v_idx * mvert_num + i);
+ BLI_edgeset_insert(existing_vert_pairs, i, tar_v_idx);
spring = (ClothSpring *)MEM_callocN(sizeof(ClothSpring), "cloth spring");
@@ -1666,7 +1665,7 @@ static int cloth_build_springs(ClothModifierData *clmd, Mesh *mesh)
}
else {
cloth_free_errorsprings(cloth, edgelist, spring_ref);
- MEM_freeN(verts_used);
+ BLI_edgeset_free(existing_vert_pairs);
free_bvhtree_from_mesh(&treedata);
if (tmp_mesh) {
BKE_mesh_free(tmp_mesh);
@@ -1675,7 +1674,7 @@ static int cloth_build_springs(ClothModifierData *clmd, Mesh *mesh)
}
}
}
- MEM_freeN(verts_used);
+ BLI_edgeset_free(existing_vert_pairs);
free_bvhtree_from_mesh(&treedata);
if (tmp_mesh) {
BKE_mesh_free(tmp_mesh);
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 f358355912b..115980d577e 100644
--- a/source/blender/blenkernel/intern/collision.c
+++ b/source/blender/blenkernel/intern/collision.c
@@ -647,6 +647,31 @@ DO_INLINE void collision_interpolateOnTriangle(float to[3],
VECADDMUL(to, v3, w3);
}
+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);
+
+ if ((clamp_sq > 0.0f) && (impulse_len_sq > clamp_sq)) {
+ return;
+ }
+
+ if (fabsf(vert->impulse[0]) < fabsf(impulse[0])) {
+ vert->impulse[0] = impulse[0];
+ }
+
+ if (fabsf(vert->impulse[1]) < fabsf(impulse[1])) {
+ vert->impulse[1] = impulse[1];
+ }
+
+ if (fabsf(vert->impulse[2]) < fabsf(impulse[2])) {
+ vert->impulse[2] = impulse[2];
+ }
+
+ vert->impulse_count++;
+}
+
static int cloth_collision_response_static(ClothModifierData *clmd,
CollisionModifierData *collmd,
Object *collob,
@@ -655,18 +680,17 @@ static int cloth_collision_response_static(ClothModifierData *clmd,
const float dt)
{
int result = 0;
- Cloth *cloth1;
- float w1, w2, w3, u1, u2, u3;
- float v1[3], v2[3], relativeVelocity[3];
- float magrelVel;
- float epsilon2 = BLI_bvhtree_get_epsilon(collmd->bvhtree);
- const bool is_hair = (clmd->hairdata != NULL);
-
- cloth1 = clmd->clothObject;
+ Cloth *cloth = clmd->clothObject;
+ 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);
+ const bool is_hair = (clmd->hairdata != NULL);
for (int i = 0; i < collision_count; i++, collpair++) {
float i1[3], i2[3], i3[3];
-
+ float w1, w2, w3, u1, u2, u3;
+ float v1[3], v2[3], relativeVelocity[3];
zero_v3(i1);
zero_v3(i2);
zero_v3(i3);
@@ -679,25 +703,25 @@ static int cloth_collision_response_static(ClothModifierData *clmd,
/* Compute barycentric coordinates and relative "velocity" for both collision points. */
if (is_hair) {
w2 = line_point_factor_v3(
- collpair->pa, cloth1->verts[collpair->ap1].tx, cloth1->verts[collpair->ap2].tx);
+ collpair->pa, cloth->verts[collpair->ap1].tx, cloth->verts[collpair->ap2].tx);
w1 = 1.0f - w2;
- interp_v3_v3v3(v1, cloth1->verts[collpair->ap1].tv, cloth1->verts[collpair->ap2].tv, w2);
+ interp_v3_v3v3(v1, cloth->verts[collpair->ap1].tv, cloth->verts[collpair->ap2].tv, w2);
}
else {
collision_compute_barycentric(collpair->pa,
- cloth1->verts[collpair->ap1].tx,
- cloth1->verts[collpair->ap2].tx,
- cloth1->verts[collpair->ap3].tx,
+ cloth->verts[collpair->ap1].tx,
+ cloth->verts[collpair->ap2].tx,
+ cloth->verts[collpair->ap3].tx,
&w1,
&w2,
&w3);
collision_interpolateOnTriangle(v1,
- cloth1->verts[collpair->ap1].tv,
- cloth1->verts[collpair->ap2].tv,
- cloth1->verts[collpair->ap3].tv,
+ cloth->verts[collpair->ap1].tv,
+ cloth->verts[collpair->ap2].tv,
+ cloth->verts[collpair->ap3].tv,
w1,
w2,
w3);
@@ -723,16 +747,16 @@ static int cloth_collision_response_static(ClothModifierData *clmd,
/* Calculate the normal component of the relative velocity
* (actually only the magnitude - the direction is stored in 'normal'). */
- magrelVel = dot_v3v3(relativeVelocity, collpair->normal);
+ const float magrelVel = dot_v3v3(relativeVelocity, collpair->normal);
+ const float d = min_distance - collpair->distance;
/* If magrelVel < 0 the edges are approaching each other. */
if (magrelVel > 0.0f) {
/* Calculate Impulse magnitude to stop all motion in normal direction. */
- float magtangent = 0, repulse = 0, d = 0;
+ float magtangent = 0, repulse = 0;
double impulse = 0.0;
float vrel_t_pre[3];
float temp[3];
- float time_multiplier;
/* Calculate tangential velocity. */
copy_v3_v3(temp, collpair->normal);
@@ -750,32 +774,23 @@ static int cloth_collision_response_static(ClothModifierData *clmd,
impulse = magtangent / 1.5;
- VECADDMUL(i1, vrel_t_pre, w1 * impulse);
- VECADDMUL(i2, vrel_t_pre, w2 * impulse);
+ VECADDMUL(i1, vrel_t_pre, (double)w1 * impulse);
+ VECADDMUL(i2, vrel_t_pre, (double)w2 * impulse);
if (!is_hair) {
- VECADDMUL(i3, vrel_t_pre, w3 * impulse);
+ VECADDMUL(i3, vrel_t_pre, (double)w3 * impulse);
}
}
/* Apply velocity stopping impulse. */
impulse = magrelVel / 1.5f;
- VECADDMUL(i1, collpair->normal, w1 * impulse);
- cloth1->verts[collpair->ap1].impulse_count++;
-
- VECADDMUL(i2, collpair->normal, w2 * impulse);
- cloth1->verts[collpair->ap2].impulse_count++;
-
+ VECADDMUL(i1, collpair->normal, (double)w1 * impulse);
+ VECADDMUL(i2, collpair->normal, (double)w2 * impulse);
if (!is_hair) {
- VECADDMUL(i3, collpair->normal, w3 * impulse);
- cloth1->verts[collpair->ap3].impulse_count++;
+ VECADDMUL(i3, collpair->normal, (double)w3 * impulse);
}
- time_multiplier = 1.0f / (clmd->sim_parms->dt * clmd->sim_parms->timescale);
-
- d = clmd->coll_parms->epsilon * 8.0f / 9.0f + epsilon2 * 8.0f / 9.0f - collpair->distance;
-
if ((magrelVel < 0.1f * d * time_multiplier) && (d > ALMOST_ZERO)) {
repulse = MIN2(d / time_multiplier, 0.1f * d * time_multiplier - magrelVel);
@@ -790,7 +805,6 @@ static int cloth_collision_response_static(ClothModifierData *clmd,
VECADDMUL(i1, collpair->normal, impulse);
VECADDMUL(i2, collpair->normal, impulse);
-
if (!is_hair) {
VECADDMUL(i3, collpair->normal, impulse);
}
@@ -798,60 +812,26 @@ static int cloth_collision_response_static(ClothModifierData *clmd,
result = 1;
}
- else {
- float time_multiplier = 1.0f / (clmd->sim_parms->dt * clmd->sim_parms->timescale);
- float d;
-
- d = clmd->coll_parms->epsilon * 8.0f / 9.0f + epsilon2 * 8.0f / 9.0f - collpair->distance;
-
- if (d > ALMOST_ZERO) {
- /* Stay on the safe side and clamp repulse. */
- float repulse = d / time_multiplier;
- float impulse = repulse / 4.5f;
+ else if (d > ALMOST_ZERO) {
+ /* Stay on the safe side and clamp repulse. */
+ float repulse = d / time_multiplier;
+ float impulse = repulse / 4.5f;
- VECADDMUL(i1, collpair->normal, w1 * impulse);
- VECADDMUL(i2, collpair->normal, w2 * impulse);
-
- if (!is_hair) {
- VECADDMUL(i3, collpair->normal, w3 * impulse);
- }
-
- cloth1->verts[collpair->ap1].impulse_count++;
- cloth1->verts[collpair->ap2].impulse_count++;
-
- if (!is_hair) {
- cloth1->verts[collpair->ap3].impulse_count++;
- }
+ VECADDMUL(i1, collpair->normal, w1 * impulse);
+ VECADDMUL(i2, collpair->normal, w2 * impulse);
- result = 1;
+ if (!is_hair) {
+ VECADDMUL(i3, collpair->normal, w3 * impulse);
}
+
+ result = 1;
}
if (result) {
- float clamp = clmd->coll_parms->clamp * dt;
-
- if ((clamp > 0.0f) &&
- ((len_v3(i1) > clamp) || (len_v3(i2) > clamp) || (len_v3(i3) > clamp))) {
- return 0;
- }
-
- for (int j = 0; j < 3; j++) {
- if (cloth1->verts[collpair->ap1].impulse_count > 0 &&
- fabsf(cloth1->verts[collpair->ap1].impulse[j]) < fabsf(i1[j])) {
- cloth1->verts[collpair->ap1].impulse[j] = i1[j];
- }
-
- if (cloth1->verts[collpair->ap2].impulse_count > 0 &&
- fabsf(cloth1->verts[collpair->ap2].impulse[j]) < fabsf(i2[j])) {
- cloth1->verts[collpair->ap2].impulse[j] = i2[j];
- }
-
- if (!is_hair) {
- if (cloth1->verts[collpair->ap3].impulse_count > 0 &&
- fabsf(cloth1->verts[collpair->ap3].impulse[j]) < fabsf(i3[j])) {
- cloth1->verts[collpair->ap3].impulse[j] = i3[j];
- }
- }
+ 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_collision_impulse_vert(clamp_sq, i3, &cloth->verts[collpair->ap3]);
}
}
}
@@ -859,47 +839,22 @@ static int cloth_collision_response_static(ClothModifierData *clmd,
return result;
}
-static void cloth_selfcollision_impulse_vert(const float clamp_sq,
- const float impulse[3],
- struct ClothVertex *vert)
-{
- float impulse_len_sq = len_squared_v3(impulse);
-
- if ((clamp_sq > 0.0f) && (impulse_len_sq > clamp_sq)) {
- return;
- }
-
- if (fabsf(vert->impulse[0]) < fabsf(impulse[0])) {
- vert->impulse[0] = impulse[0];
- }
-
- if (fabsf(vert->impulse[1]) < fabsf(impulse[1])) {
- vert->impulse[1] = impulse[1];
- }
-
- if (fabsf(vert->impulse[2]) < fabsf(impulse[2])) {
- vert->impulse[2] = impulse[2];
- }
-
- vert->impulse_count++;
-}
-
static int cloth_selfcollision_response_static(ClothModifierData *clmd,
CollPair *collpair,
uint collision_count,
const float dt)
{
int result = 0;
- Cloth *cloth1;
- float w1, w2, w3, u1, u2, u3;
- float v1[3], v2[3], relativeVelocity[3];
- float magrelVel;
-
- cloth1 = clmd->clothObject;
+ Cloth *cloth = clmd->clothObject;
+ const float clamp_sq = square_f(clmd->coll_parms->self_clamp * dt);
+ const float time_multiplier = 1.0f / (clmd->sim_parms->dt * clmd->sim_parms->timescale);
+ const float min_distance = (2.0f * clmd->coll_parms->selfepsilon) * (8.0f / 9.0f);
for (int i = 0; i < collision_count; i++, collpair++) {
float ia[3][3] = {{0.0f}};
float ib[3][3] = {{0.0f}};
+ float w1, w2, w3, u1, u2, u3;
+ float v1[3], v2[3], relativeVelocity[3];
/* Only handle static collisions here. */
if (collpair->flag & (COLLISION_IN_FUTURE | COLLISION_INACTIVE)) {
@@ -908,34 +863,34 @@ static int cloth_selfcollision_response_static(ClothModifierData *clmd,
/* Compute barycentric coordinates for both collision points. */
collision_compute_barycentric(collpair->pa,
- cloth1->verts[collpair->ap1].tx,
- cloth1->verts[collpair->ap2].tx,
- cloth1->verts[collpair->ap3].tx,
+ cloth->verts[collpair->ap1].tx,
+ cloth->verts[collpair->ap2].tx,
+ cloth->verts[collpair->ap3].tx,
&w1,
&w2,
&w3);
collision_compute_barycentric(collpair->pb,
- cloth1->verts[collpair->bp1].tx,
- cloth1->verts[collpair->bp2].tx,
- cloth1->verts[collpair->bp3].tx,
+ cloth->verts[collpair->bp1].tx,
+ cloth->verts[collpair->bp2].tx,
+ cloth->verts[collpair->bp3].tx,
&u1,
&u2,
&u3);
/* Calculate relative "velocity". */
collision_interpolateOnTriangle(v1,
- cloth1->verts[collpair->ap1].tv,
- cloth1->verts[collpair->ap2].tv,
- cloth1->verts[collpair->ap3].tv,
+ cloth->verts[collpair->ap1].tv,
+ cloth->verts[collpair->ap2].tv,
+ cloth->verts[collpair->ap3].tv,
w1,
w2,
w3);
collision_interpolateOnTriangle(v2,
- cloth1->verts[collpair->bp1].tv,
- cloth1->verts[collpair->bp2].tv,
- cloth1->verts[collpair->bp3].tv,
+ cloth->verts[collpair->bp1].tv,
+ cloth->verts[collpair->bp2].tv,
+ cloth->verts[collpair->bp3].tv,
u1,
u2,
u3);
@@ -944,7 +899,8 @@ static int cloth_selfcollision_response_static(ClothModifierData *clmd,
/* Calculate the normal component of the relative velocity
* (actually only the magnitude - the direction is stored in 'normal'). */
- magrelVel = dot_v3v3(relativeVelocity, collpair->normal);
+ const float magrelVel = dot_v3v3(relativeVelocity, collpair->normal);
+ const float d = min_distance - collpair->distance;
/* TODO: Impulses should be weighed by mass as this is self col,
* this has to be done after mass distribution is implemented. */
@@ -952,10 +908,10 @@ static int cloth_selfcollision_response_static(ClothModifierData *clmd,
/* If magrelVel < 0 the edges are approaching each other. */
if (magrelVel > 0.0f) {
/* Calculate Impulse magnitude to stop all motion in normal direction. */
- float magtangent = 0, repulse = 0, d = 0;
+ float magtangent = 0, repulse = 0;
double impulse = 0.0;
float vrel_t_pre[3];
- float temp[3], time_multiplier;
+ float temp[3];
/* Calculate tangential velocity. */
copy_v3_v3(temp, collpair->normal);
@@ -973,29 +929,25 @@ static int cloth_selfcollision_response_static(ClothModifierData *clmd,
impulse = magtangent / 1.5;
- VECADDMUL(ia[0], vrel_t_pre, w1 * impulse);
- VECADDMUL(ia[1], vrel_t_pre, w2 * impulse);
- VECADDMUL(ia[2], vrel_t_pre, w3 * impulse);
+ VECADDMUL(ia[0], vrel_t_pre, (double)w1 * impulse);
+ VECADDMUL(ia[1], vrel_t_pre, (double)w2 * impulse);
+ VECADDMUL(ia[2], vrel_t_pre, (double)w3 * impulse);
- VECADDMUL(ib[0], vrel_t_pre, -u1 * impulse);
- VECADDMUL(ib[1], vrel_t_pre, -u2 * impulse);
- VECADDMUL(ib[2], vrel_t_pre, -u3 * impulse);
+ VECADDMUL(ib[0], vrel_t_pre, (double)u1 * -impulse);
+ VECADDMUL(ib[1], vrel_t_pre, (double)u2 * -impulse);
+ VECADDMUL(ib[2], vrel_t_pre, (double)u3 * -impulse);
}
/* Apply velocity stopping impulse. */
impulse = magrelVel / 3.0f;
- VECADDMUL(ia[0], collpair->normal, w1 * impulse);
- VECADDMUL(ia[1], collpair->normal, w2 * impulse);
- VECADDMUL(ia[2], collpair->normal, w3 * impulse);
-
- VECADDMUL(ib[0], collpair->normal, -u1 * impulse);
- VECADDMUL(ib[1], collpair->normal, -u2 * impulse);
- VECADDMUL(ib[2], collpair->normal, -u3 * impulse);
-
- time_multiplier = 1.0f / (clmd->sim_parms->dt * clmd->sim_parms->timescale);
+ VECADDMUL(ia[0], collpair->normal, (double)w1 * impulse);
+ VECADDMUL(ia[1], collpair->normal, (double)w2 * impulse);
+ VECADDMUL(ia[2], collpair->normal, (double)w3 * impulse);
- d = clmd->coll_parms->selfepsilon * 8.0f / 9.0f * 2.0f - collpair->distance;
+ VECADDMUL(ib[0], collpair->normal, (double)u1 * -impulse);
+ VECADDMUL(ib[1], collpair->normal, (double)u2 * -impulse);
+ VECADDMUL(ib[2], collpair->normal, (double)u3 * -impulse);
if ((magrelVel < 0.1f * d * time_multiplier) && (d > ALMOST_ZERO)) {
repulse = MIN2(d / time_multiplier, 0.1f * d * time_multiplier - magrelVel);
@@ -1005,54 +957,43 @@ static int cloth_selfcollision_response_static(ClothModifierData *clmd,
}
repulse = max_ff(impulse, repulse);
-
impulse = repulse / 1.5f;
- VECADDMUL(ia[0], collpair->normal, w1 * impulse);
- VECADDMUL(ia[1], collpair->normal, w2 * impulse);
- VECADDMUL(ia[2], collpair->normal, w3 * impulse);
+ VECADDMUL(ia[0], collpair->normal, (double)w1 * impulse);
+ VECADDMUL(ia[1], collpair->normal, (double)w2 * impulse);
+ VECADDMUL(ia[2], collpair->normal, (double)w3 * impulse);
- VECADDMUL(ib[0], collpair->normal, -u1 * impulse);
- VECADDMUL(ib[1], collpair->normal, -u2 * impulse);
- VECADDMUL(ib[2], collpair->normal, -u3 * impulse);
+ VECADDMUL(ib[0], collpair->normal, (double)u1 * -impulse);
+ VECADDMUL(ib[1], collpair->normal, (double)u2 * -impulse);
+ VECADDMUL(ib[2], collpair->normal, (double)u3 * -impulse);
}
result = 1;
}
- else {
- float time_multiplier = 1.0f / (clmd->sim_parms->dt * clmd->sim_parms->timescale);
- float d;
-
- d = clmd->coll_parms->selfepsilon * 8.0f / 9.0f * 2.0f - collpair->distance;
-
- if (d > ALMOST_ZERO) {
- /* Stay on the safe side and clamp repulse. */
- float repulse = d * 1.0f / time_multiplier;
- float impulse = repulse / 9.0f;
+ else if (d > ALMOST_ZERO) {
+ /* Stay on the safe side and clamp repulse. */
+ float repulse = d * 1.0f / time_multiplier;
+ float impulse = repulse / 9.0f;
- VECADDMUL(ia[0], collpair->normal, w1 * impulse);
- VECADDMUL(ia[1], collpair->normal, w2 * impulse);
- VECADDMUL(ia[2], collpair->normal, w3 * impulse);
+ VECADDMUL(ia[0], collpair->normal, w1 * impulse);
+ VECADDMUL(ia[1], collpair->normal, w2 * impulse);
+ VECADDMUL(ia[2], collpair->normal, w3 * impulse);
- VECADDMUL(ib[0], collpair->normal, -u1 * impulse);
- VECADDMUL(ib[1], collpair->normal, -u2 * impulse);
- VECADDMUL(ib[2], collpair->normal, -u3 * impulse);
+ VECADDMUL(ib[0], collpair->normal, u1 * -impulse);
+ VECADDMUL(ib[1], collpair->normal, u2 * -impulse);
+ VECADDMUL(ib[2], collpair->normal, u3 * -impulse);
- result = 1;
- }
+ result = 1;
}
if (result) {
- float clamp_sq = clmd->coll_parms->self_clamp * dt;
- clamp_sq *= clamp_sq;
-
- cloth_selfcollision_impulse_vert(clamp_sq, ia[0], &cloth1->verts[collpair->ap1]);
- cloth_selfcollision_impulse_vert(clamp_sq, ia[1], &cloth1->verts[collpair->ap2]);
- cloth_selfcollision_impulse_vert(clamp_sq, ia[2], &cloth1->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], &cloth1->verts[collpair->bp1]);
- cloth_selfcollision_impulse_vert(clamp_sq, ib[1], &cloth1->verts[collpair->bp2]);
- cloth_selfcollision_impulse_vert(clamp_sq, ib[2], &cloth1->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/constraint.c b/source/blender/blenkernel/intern/constraint.c
index 01ce95d9d70..ee386b3403b 100644
--- a/source/blender/blenkernel/intern/constraint.c
+++ b/source/blender/blenkernel/intern/constraint.c
@@ -602,8 +602,7 @@ static void constraint_target_to_mat4(Object *ob,
pchan = BKE_pose_channel_find_name(ob->pose, substring);
if (pchan) {
/* Multiply the PoseSpace accumulation/final matrix for this
- * PoseChannel by the Armature Object's Matrix to get a worldspace
- * matrix.
+ * PoseChannel by the Armature Object's Matrix to get a world-space matrix.
*/
bool is_bbone = (pchan->bone) && (pchan->bone->segments > 1) &&
(flag & CONSTRAINT_BBONE_SHAPE);
@@ -972,7 +971,7 @@ static void childof_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *tar
}
/* Multiply together the target (parent) matrix, parent inverse,
- * and the owner transform matrixto get the effect of this constraint
+ * and the owner transform matrix to get the effect of this constraint
* (i.e. owner is 'parented' to parent). */
float orig_cob_matrix[4][4];
copy_m4_m4(orig_cob_matrix, cob->matrix);
@@ -6033,7 +6032,7 @@ void BKE_constraints_solve(struct Depsgraph *depsgraph,
*/
enf = con->enforce;
- /* make copy of worldspace matrix pre-constraint for use with blending later */
+ /* make copy of world-space matrix pre-constraint for use with blending later */
copy_m4_m4(oldmat, cob->matrix);
/* move owner matrix into right space */
@@ -6054,16 +6053,16 @@ void BKE_constraints_solve(struct Depsgraph *depsgraph,
cti->flush_constraint_targets(con, &targets, 1);
}
- /* move owner back into worldspace for next constraint/other business */
+ /* move owner back into world-space for next constraint/other business */
if ((con->flag & CONSTRAINT_SPACEONCE) == 0) {
BKE_constraint_mat_convertspace(
cob->ob, cob->pchan, cob->matrix, con->ownspace, CONSTRAINT_SPACE_WORLD, false);
}
/* Interpolate the enforcement, to blend result of constraint into final owner transform
- * - all this happens in worldspace to prevent any weirdness creeping in
+ * - all this happens in world-space to prevent any weirdness creeping in
* (T26014 and T25725), since some constraints may not convert the solution back to the input
- * space before blending but all are guaranteed to end up in good "worldspace" result.
+ * space before blending but all are guaranteed to end up in good "world-space" result.
*/
/* Note: all kind of stuff here before (caused trouble), much easier to just interpolate,
* or did I miss something? -jahka (r.32105) */
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..4d6b8feea26 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],
@@ -3315,8 +3321,9 @@ static void calchandlesNurb_intern(Nurb *nu, eBezTriple_Flag handle_sel_flag, bo
}
}
-/* A utility function for allocating a number of arrays of the same length
- * with easy error checking and deallocation, and an easy way to add or remove
+/**
+ * A utility function for allocating a number of arrays of the same length
+ * with easy error checking and de-allocation, and an easy way to add or remove
* arrays that are processed in this way when changing code.
*
* floats, chars: NULL-terminated arrays of pointers to array pointers that need to be allocated.
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..00fd30da4cd 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 vertex group allocation system. */
+ MDeformWeight *dw;
+ if (mdverts->dw && (dw = BLO_read_get_new_data_address(reader, mdverts->dw))) {
+ const size_t dw_len = sizeof(MDeformWeight) * mdverts->totweight;
+ 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 c64f1b86eab..1e37ae3892b 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,
};
/* ************************************************** */
@@ -532,6 +538,7 @@ bGPdata *BKE_gpencil_data_addnew(Main *bmain, const char name[])
gpd->grid.lines = GP_DEFAULT_GRID_LINES; /* Number of lines */
/* Onion-skinning settings (data-block level) */
+ gpd->onion_keytype = -1; /* All by default. */
gpd->onion_flag |= (GP_ONION_GHOST_PREVCOL | GP_ONION_GHOST_NEXTCOL);
gpd->onion_flag |= GP_ONION_FADE;
gpd->onion_mode = GP_ONION_MODE_RELATIVE;
@@ -1879,6 +1886,7 @@ bool BKE_gpencil_merge_materials_table_get(Object *ob,
Material *ma_secondary = NULL;
MaterialGPencilStyle *gp_style_primary = NULL;
MaterialGPencilStyle *gp_style_secondary = NULL;
+ GHash *mat_used = BLI_ghash_int_new(__func__);
short *totcol = BKE_object_material_len_p(ob);
if (totcol == 0) {
@@ -1891,8 +1899,15 @@ bool BKE_gpencil_merge_materials_table_get(Object *ob,
if (ma_primary == NULL) {
continue;
}
+ for (int idx_secondary = 0; idx_secondary < *totcol; idx_secondary++) {
+ if ((idx_secondary == idx_primary) ||
+ BLI_ghash_haskey(r_mat_table, POINTER_FROM_INT(idx_secondary))) {
+ continue;
+ }
+ if (BLI_ghash_haskey(mat_used, POINTER_FROM_INT(idx_secondary))) {
+ continue;
+ }
- for (int idx_secondary = idx_primary + 1; idx_secondary < *totcol; idx_secondary++) {
/* Read secondary material to compare with primary material. */
ma_secondary = BKE_gpencil_material(ob, idx_secondary + 1);
if ((ma_secondary == NULL) ||
@@ -1930,6 +1945,11 @@ bool BKE_gpencil_merge_materials_table_get(Object *ob,
}
float s_hsv_a[3], s_hsv_b[3], f_hsv_a[3], f_hsv_b[3], col[3];
+ zero_v3(s_hsv_a);
+ zero_v3(s_hsv_b);
+ zero_v3(f_hsv_a);
+ zero_v3(f_hsv_b);
+
copy_v3_v3(col, gp_style_primary->stroke_rgba);
rgb_to_hsv_compat_v(col, s_hsv_a);
copy_v3_v3(col, gp_style_secondary->stroke_rgba);
@@ -1940,24 +1960,102 @@ bool BKE_gpencil_merge_materials_table_get(Object *ob,
copy_v3_v3(col, gp_style_secondary->fill_rgba);
rgb_to_hsv_compat_v(col, f_hsv_b);
- /* Check stroke and fill color (only Hue and Saturation). */
+ /* Check stroke and fill color. */
if ((!compare_ff(s_hsv_a[0], s_hsv_b[0], hue_threshold)) ||
(!compare_ff(s_hsv_a[1], s_hsv_b[1], sat_threshold)) ||
+ (!compare_ff(s_hsv_a[2], s_hsv_b[2], val_threshold)) ||
(!compare_ff(f_hsv_a[0], f_hsv_b[0], hue_threshold)) ||
(!compare_ff(f_hsv_a[1], f_hsv_b[1], sat_threshold)) ||
- (!compare_ff(s_hsv_a[2], s_hsv_b[2], val_threshold)) ||
- (!compare_ff(s_hsv_a[2], s_hsv_b[2], val_threshold)) ||
- (!compare_ff(s_hsv_a[2], s_hsv_b[2], val_threshold)) ||
- (!compare_ff(s_hsv_a[2], s_hsv_b[2], val_threshold))) {
+ (!compare_ff(f_hsv_a[2], f_hsv_b[2], val_threshold)) ||
+ (!compare_ff(gp_style_primary->stroke_rgba[3],
+ gp_style_secondary->stroke_rgba[3],
+ val_threshold)) ||
+ (!compare_ff(
+ gp_style_primary->fill_rgba[3], gp_style_secondary->fill_rgba[3], val_threshold))) {
continue;
}
/* Save conversion indexes. */
- BLI_ghash_insert(
- r_mat_table, POINTER_FROM_INT(idx_secondary), POINTER_FROM_INT(idx_primary));
- changed = true;
+ if (!BLI_ghash_haskey(r_mat_table, POINTER_FROM_INT(idx_secondary))) {
+ BLI_ghash_insert(
+ r_mat_table, POINTER_FROM_INT(idx_secondary), POINTER_FROM_INT(idx_primary));
+ changed = true;
+
+ if (!BLI_ghash_haskey(mat_used, POINTER_FROM_INT(idx_primary))) {
+ BLI_ghash_insert(mat_used, POINTER_FROM_INT(idx_primary), POINTER_FROM_INT(idx_primary));
+ }
+ }
}
}
+ /* Free hash memory. */
+ BLI_ghash_free(mat_used, NULL, NULL);
+
+ return changed;
+}
+
+/**
+ * Merge similar materials
+ * \param ob: Grease pencil object
+ * \param hue_threshold: Threshold for Hue
+ * \param sat_threshold: Threshold for Saturation
+ * \param val_threshold: Threshold for Value
+ * \param r_removed: Number of materials removed
+ * \return True if done
+ */
+bool BKE_gpencil_merge_materials(Object *ob,
+ const float hue_threshold,
+ const float sat_threshold,
+ const float val_threshold,
+ int *r_removed)
+{
+ bGPdata *gpd = ob->data;
+
+ short *totcol = BKE_object_material_len_p(ob);
+ if (totcol == 0) {
+ *r_removed = 0;
+ return 0;
+ }
+
+ /* Review materials. */
+ GHash *mat_table = BLI_ghash_int_new(__func__);
+
+ bool changed = BKE_gpencil_merge_materials_table_get(
+ ob, hue_threshold, sat_threshold, val_threshold, mat_table);
+
+ *r_removed = BLI_ghash_len(mat_table);
+
+ /* Update stroke material index. */
+ if (changed) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ if (gpl->flag & GP_LAYER_HIDE) {
+ continue;
+ }
+
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ /* Check if the color is editable. */
+ MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
+ if (gp_style != NULL) {
+ if (gp_style->flag & GP_MATERIAL_HIDE) {
+ continue;
+ }
+ if (((gpl->flag & GP_LAYER_UNLOCK_COLOR) == 0) &&
+ (gp_style->flag & GP_MATERIAL_LOCKED)) {
+ continue;
+ }
+ }
+
+ if (BLI_ghash_haskey(mat_table, POINTER_FROM_INT(gps->mat_nr))) {
+ int *idx = BLI_ghash_lookup(mat_table, POINTER_FROM_INT(gps->mat_nr));
+ gps->mat_nr = POINTER_AS_INT(idx);
+ }
+ }
+ }
+ }
+ }
+
+ /* Free hash memory. */
+ BLI_ghash_free(mat_table, NULL, NULL);
return changed;
}
@@ -2012,7 +2110,6 @@ int BKE_gpencil_object_material_index_get(Object *ob, Material *ma)
*/
void BKE_gpencil_palette_ensure(Main *bmain, Scene *scene)
{
- const int totcol = 120;
const char *hexcol[] = {
"FFFFFF", "F2F2F2", "E6E6E6", "D9D9D9", "CCCCCC", "BFBFBF", "B2B2B2", "A6A6A6", "999999",
"8C8C8C", "808080", "737373", "666666", "595959", "4C4C4C", "404040", "333333", "262626",
@@ -2030,33 +2127,34 @@ void BKE_gpencil_palette_ensure(Main *bmain, Scene *scene)
"0000FF", "3F007F", "00007F"};
ToolSettings *ts = scene->toolsettings;
- GpPaint *gp_paint = ts->gp_paint;
- Paint *paint = &gp_paint->paint;
-
- if (paint->palette != NULL) {
+ if (ts->gp_paint->paint.palette != NULL) {
return;
}
- paint->palette = BLI_findstring(&bmain->palettes, "Palette", offsetof(ID, name) + 2);
- /* Try with first palette. */
- if (bmain->palettes.first != NULL) {
- paint->palette = bmain->palettes.first;
- ts->gp_vertexpaint->paint.palette = paint->palette;
- return;
+ /* Try to find the default palette. */
+ const char *palette_id = "Palette";
+ struct Palette *palette = BLI_findstring(&bmain->palettes, palette_id, offsetof(ID, name) + 2);
+
+ if (palette == NULL) {
+ /* Fall back to the first palette. */
+ palette = bmain->palettes.first;
}
- if (paint->palette == NULL) {
- paint->palette = BKE_palette_add(bmain, "Palette");
- ts->gp_vertexpaint->paint.palette = paint->palette;
+ if (palette == NULL) {
+ /* Fall back to creating a palette. */
+ palette = BKE_palette_add(bmain, palette_id);
+ id_us_min(&palette->id);
/* Create Colors. */
- for (int i = 0; i < totcol; i++) {
- PaletteColor *palcol = BKE_palette_color_add(paint->palette);
- if (palcol) {
- hex_to_rgb((char *)hexcol[i], palcol->rgb, palcol->rgb + 1, palcol->rgb + 2);
- }
+ for (int i = 0; i < ARRAY_SIZE(hexcol); i++) {
+ PaletteColor *palcol = BKE_palette_color_add(palette);
+ hex_to_rgb(hexcol[i], palcol->rgb, palcol->rgb + 1, palcol->rgb + 2);
}
}
+
+ BLI_assert(palette != NULL);
+ BKE_paint_palette_set(&ts->gp_paint->paint, palette);
+ BKE_paint_palette_set(&ts->gp_vertexpaint->paint, palette);
}
/**
diff --git a/source/blender/blenkernel/intern/gpencil_curve.c b/source/blender/blenkernel/intern/gpencil_curve.c
index 6db5eaf28e0..6b3f752120a 100644
--- a/source/blender/blenkernel/intern/gpencil_curve.c
+++ b/source/blender/blenkernel/intern/gpencil_curve.c
@@ -50,60 +50,111 @@
#include "DEG_depsgraph_query.h"
/* Helper: Check materials with same color. */
-static int gpencil_check_same_material_color(Object *ob_gp, const float color[4], Material **r_mat)
+static int gpencil_check_same_material_color(Object *ob_gp,
+ const float color_stroke[4],
+ const float color_fill[4],
+ const bool do_fill,
+ const bool do_stroke,
+ Material **r_mat)
{
+ int index = -1;
Material *ma = NULL;
+ *r_mat = NULL;
float color_cu[4];
- copy_v4_v4(color_cu, color);
+ float hsv_stroke[4], hsv_fill[4];
+
+ copy_v4_v4(color_cu, color_stroke);
+ zero_v3(hsv_stroke);
+ rgb_to_hsv_v(color_cu, hsv_stroke);
+ hsv_stroke[3] = color_stroke[3];
+
+ copy_v4_v4(color_cu, color_fill);
+ zero_v3(hsv_fill);
+ rgb_to_hsv_v(color_cu, hsv_fill);
+ hsv_fill[3] = color_fill[3];
- float hsv1[4];
- rgb_to_hsv_v(color_cu, hsv1);
- hsv1[3] = color[3];
+ bool match_stroke = false;
+ bool match_fill = false;
for (int i = 1; i <= ob_gp->totcol; i++) {
ma = BKE_object_material_get(ob_gp, i);
MaterialGPencilStyle *gp_style = ma->gp_style;
- /* Check color with small tolerance (better in HSV). */
+ const bool fill = (gp_style->fill_style == GP_MATERIAL_FILL_STYLE_SOLID);
+ const bool stroke = (gp_style->fill_style == GP_MATERIAL_STROKE_STYLE_SOLID);
+
+ if (do_fill && !fill) {
+ continue;
+ }
+
+ if (do_stroke && !stroke) {
+ continue;
+ }
+
+ /* Check color with small tolerance (better result in HSV). */
float hsv2[4];
- rgb_to_hsv_v(gp_style->fill_rgba, hsv2);
- hsv2[3] = gp_style->fill_rgba[3];
- if ((gp_style->fill_style == GP_MATERIAL_FILL_STYLE_SOLID) &&
- (compare_v4v4(hsv1, hsv2, 0.01f))) {
- *r_mat = ma;
- return i - 1;
+ if (do_fill) {
+ zero_v3(hsv2);
+ rgb_to_hsv_v(gp_style->fill_rgba, hsv2);
+ hsv2[3] = gp_style->fill_rgba[3];
+ if (compare_v4v4(hsv_fill, hsv2, 0.01f)) {
+ *r_mat = ma;
+ index = i - 1;
+ match_fill = true;
+ }
+ }
+ else {
+ match_fill = true;
+ }
+
+ if (do_stroke) {
+ zero_v3(hsv2);
+ rgb_to_hsv_v(gp_style->stroke_rgba, hsv2);
+ hsv2[3] = gp_style->stroke_rgba[3];
+ if (compare_v4v4(hsv_stroke, hsv2, 0.01f)) {
+ *r_mat = ma;
+ index = i - 1;
+ match_stroke = true;
+ }
+ }
+ else {
+ match_stroke = true;
+ }
+
+ /* If match, don't look for more. */
+ if (match_stroke || match_fill) {
+ break;
}
}
- *r_mat = NULL;
- return -1;
+ if (!match_stroke || !match_fill) {
+ *r_mat = NULL;
+ index = -1;
+ }
+
+ return index;
}
/* Helper: Add gpencil material using curve material as base. */
static Material *gpencil_add_from_curve_material(Main *bmain,
Object *ob_gp,
- const float cu_color[4],
- const bool gpencil_lines,
+ const float stroke_color[4],
+ const float fill_color[4],
+ const bool stroke,
const bool fill,
int *r_idx)
{
- Material *mat_gp = BKE_gpencil_object_material_new(
- bmain, ob_gp, (fill) ? "Material" : "Unassigned", r_idx);
+ Material *mat_gp = BKE_gpencil_object_material_new(bmain, ob_gp, "Material", r_idx);
MaterialGPencilStyle *gp_style = mat_gp->gp_style;
/* Stroke color. */
- if (gpencil_lines) {
- ARRAY_SET_ITEMS(gp_style->stroke_rgba, 0.0f, 0.0f, 0.0f, 1.0f);
+ if (stroke) {
+ copy_v4_v4(mat_gp->gp_style->stroke_rgba, stroke_color);
gp_style->flag |= GP_MATERIAL_STROKE_SHOW;
}
- else {
- copy_v4_v4(mat_gp->gp_style->stroke_rgba, cu_color);
- gp_style->flag &= ~GP_MATERIAL_STROKE_SHOW;
- }
/* Fill color. */
- copy_v4_v4(mat_gp->gp_style->fill_rgba, cu_color);
- /* Fill is false if the original curve hasn't material assigned, so enable it. */
if (fill) {
+ copy_v4_v4(mat_gp->gp_style->fill_rgba, fill_color);
gp_style->flag |= GP_MATERIAL_FILL_SHOW;
}
@@ -118,13 +169,18 @@ static Material *gpencil_add_from_curve_material(Main *bmain,
/* Helper: Create new stroke section. */
static void gpencil_add_new_points(bGPDstroke *gps,
- float *coord_array,
- float pressure,
- int init,
- int totpoints,
+ const float *coord_array,
+ const float pressure_start,
+ const float pressure_end,
+ const int init,
+ const int totpoints,
const float init_co[3],
- bool last)
+ const bool last)
{
+ BLI_assert(totpoints > 0);
+
+ const float step = 1.0f / ((float)totpoints - 1.0f);
+ float factor = 0.0f;
for (int i = 0; i < totpoints; i++) {
bGPDspoint *pt = &gps->points[i + init];
copy_v3_v3(&pt->x, &coord_array[3 * i]);
@@ -139,8 +195,9 @@ static void gpencil_add_new_points(bGPDstroke *gps,
}
}
- pt->pressure = pressure;
pt->strength = 1.0f;
+ pt->pressure = interpf(pressure_end, pressure_start, factor);
+ factor += step;
}
}
@@ -159,17 +216,85 @@ static Collection *gpencil_get_parent_collection(Scene *scene, Object *ob)
return mycol;
}
+static int gpencil_get_stroke_material_fromcurve(
+ Main *bmain, Object *ob_gp, Object *ob_cu, bool *do_stroke, bool *do_fill)
+{
+ Curve *cu = (Curve *)ob_cu->data;
+
+ Material *mat_gp = NULL;
+ Material *mat_curve_stroke = NULL;
+ Material *mat_curve_fill = NULL;
+
+ float color_stroke[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+ float color_fill[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+
+ /* If the curve has 2 materials, the first is considered as Fill and the second as Stroke.
+ * If the has only one material, if the name contains _stroke, the is used
+ * as stroke, else as fill.*/
+ if (ob_cu->totcol >= 2) {
+ *do_stroke = true;
+ *do_fill = true;
+ mat_curve_fill = BKE_object_material_get(ob_cu, 1);
+ mat_curve_stroke = BKE_object_material_get(ob_cu, 2);
+ }
+ else if (ob_cu->totcol == 1) {
+ mat_curve_stroke = BKE_object_material_get(ob_cu, 1);
+ if ((mat_curve_stroke) && (strstr(mat_curve_stroke->id.name, "_stroke") != NULL)) {
+ *do_stroke = true;
+ *do_fill = false;
+ mat_curve_fill = NULL;
+ }
+ else {
+ *do_stroke = false;
+ *do_fill = true;
+ /* Invert materials. */
+ mat_curve_fill = mat_curve_stroke;
+ mat_curve_stroke = NULL;
+ }
+ }
+ else {
+ /* No materials in the curve. */
+ *do_fill = false;
+ return -1;
+ }
+
+ if (mat_curve_stroke) {
+ copy_v4_v4(color_stroke, &mat_curve_stroke->r);
+ }
+ if (mat_curve_fill) {
+ copy_v4_v4(color_fill, &mat_curve_fill->r);
+ }
+
+ int r_idx = gpencil_check_same_material_color(
+ ob_gp, color_stroke, color_fill, *do_stroke, *do_fill, &mat_gp);
+
+ if ((ob_gp->totcol < r_idx) || (r_idx < 0)) {
+ mat_gp = gpencil_add_from_curve_material(
+ bmain, ob_gp, color_stroke, color_fill, *do_stroke, *do_fill, &r_idx);
+ }
+
+ /* Set fill and stroke depending of curve type (3D or 2D). */
+ if ((cu->flag & CU_3D) || ((cu->flag & (CU_FRONT | CU_BACK)) == 0)) {
+ mat_gp->gp_style->flag |= GP_MATERIAL_STROKE_SHOW;
+ mat_gp->gp_style->flag &= ~GP_MATERIAL_FILL_SHOW;
+ }
+ else {
+ mat_gp->gp_style->flag &= ~GP_MATERIAL_STROKE_SHOW;
+ mat_gp->gp_style->flag |= GP_MATERIAL_FILL_SHOW;
+ }
+
+ return r_idx;
+}
/* Helper: Convert one spline to grease pencil stroke. */
static void gpencil_convert_spline(Main *bmain,
Object *ob_gp,
Object *ob_cu,
- const bool gpencil_lines,
- const bool only_stroke,
+ const float scale_thickness,
+ const float sample,
bGPDframe *gpf,
Nurb *nu)
{
- Curve *cu = (Curve *)ob_cu->data;
bool cyclic = true;
/* Create Stroke. */
@@ -204,75 +329,8 @@ static void gpencil_convert_spline(Main *bmain,
/* Materials
* Notice: The color of the material is the color of viewport and not the final shader color.
*/
- Material *mat_gp = NULL;
- bool fill = true;
- /* Check if grease pencil has a material with same color.*/
- float color[4];
- if ((cu->mat) && (*cu->mat)) {
- Material *mat_cu = *cu->mat;
- copy_v4_v4(color, &mat_cu->r);
- }
- else {
- /* Gray (unassigned from SVG add-on) */
- zero_v4(color);
- add_v3_fl(color, 0.6f);
- color[3] = 1.0f;
- fill = false;
- }
-
- /* Special case: If the color was created by the SVG add-on and the name contains '_stroke' and
- * there is only one color, the stroke must not be closed, fill to false and use for
- * stroke the fill color.
- */
- bool do_stroke = false;
- if (ob_cu->totcol == 1) {
- Material *ma_stroke = BKE_object_material_get(ob_cu, 1);
- if ((ma_stroke) && (strstr(ma_stroke->id.name, "_stroke") != NULL)) {
- do_stroke = true;
- }
- }
-
- int r_idx = gpencil_check_same_material_color(ob_gp, color, &mat_gp);
- if ((ob_cu->totcol > 0) && (r_idx < 0)) {
- Material *mat_curve = BKE_object_material_get(ob_cu, 1);
- mat_gp = gpencil_add_from_curve_material(bmain, ob_gp, color, gpencil_lines, fill, &r_idx);
-
- if ((mat_curve) && (mat_curve->gp_style != NULL)) {
- MaterialGPencilStyle *gp_style_cur = mat_curve->gp_style;
- MaterialGPencilStyle *gp_style_gp = mat_gp->gp_style;
-
- copy_v4_v4(gp_style_gp->mix_rgba, gp_style_cur->mix_rgba);
- gp_style_gp->fill_style = gp_style_cur->fill_style;
- gp_style_gp->mix_factor = gp_style_cur->mix_factor;
- }
-
- /* If object has more than 1 material, use second material for stroke color. */
- if ((!only_stroke) && (ob_cu->totcol > 1) && (BKE_object_material_get(ob_cu, 2))) {
- mat_curve = BKE_object_material_get(ob_cu, 2);
- if (mat_curve) {
- copy_v4_v4(mat_gp->gp_style->stroke_rgba, &mat_curve->r);
- }
- }
- else if ((only_stroke) || (do_stroke)) {
- /* Also use the first color if the fill is none for stroke color. */
- if (ob_cu->totcol > 0) {
- mat_curve = BKE_object_material_get(ob_cu, 1);
- if (mat_curve) {
- copy_v3_v3(mat_gp->gp_style->stroke_rgba, &mat_curve->r);
- mat_gp->gp_style->stroke_rgba[3] = mat_curve->a;
- /* Set fill and stroke depending of curve type (3D or 2D). */
- if ((cu->flag & CU_3D) || ((cu->flag & (CU_FRONT | CU_BACK)) == 0)) {
- mat_gp->gp_style->flag |= GP_MATERIAL_STROKE_SHOW;
- mat_gp->gp_style->flag &= ~GP_MATERIAL_FILL_SHOW;
- }
- else {
- mat_gp->gp_style->flag &= ~GP_MATERIAL_STROKE_SHOW;
- mat_gp->gp_style->flag |= GP_MATERIAL_FILL_SHOW;
- }
- }
- }
- }
- }
+ bool do_stroke, do_fill;
+ int r_idx = gpencil_get_stroke_material_fromcurve(bmain, ob_gp, ob_cu, &do_stroke, &do_fill);
CLAMP_MIN(r_idx, 0);
/* Assign material index to stroke. */
@@ -336,7 +394,11 @@ static void gpencil_convert_spline(Main *bmain,
copy_v3_v3(init_co, &coord_array[0]);
}
/* Add points to the stroke */
- gpencil_add_new_points(gps, coord_array, bezt->radius, init, resolu, init_co, last);
+ float radius_start = prevbezt->radius * scale_thickness;
+ float radius_end = bezt->radius * scale_thickness;
+
+ gpencil_add_new_points(
+ gps, coord_array, radius_start, radius_end, init, resolu, init_co, last);
/* Free memory. */
MEM_SAFE_FREE(coord_array);
@@ -367,7 +429,7 @@ static void gpencil_convert_spline(Main *bmain,
gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points");
/* Add points. */
- gpencil_add_new_points(gps, coord_array, 1.0f, 0, gps->totpoints, init_co, false);
+ gpencil_add_new_points(gps, coord_array, 1.0f, 1.0f, 0, gps->totpoints, init_co, false);
MEM_SAFE_FREE(coord_array);
}
@@ -378,10 +440,14 @@ static void gpencil_convert_spline(Main *bmain,
}
}
/* Cyclic curve, close stroke. */
- if ((cyclic) && (!do_stroke)) {
+ if (cyclic) {
BKE_gpencil_stroke_close(gps);
}
+ if (sample > 0.0f) {
+ BKE_gpencil_stroke_sample(gps, sample, false);
+ }
+
/* Recalc fill geometry. */
BKE_gpencil_stroke_geometry_update(gps);
}
@@ -393,17 +459,17 @@ static void gpencil_convert_spline(Main *bmain,
* \param scene: Original scene.
* \param ob_gp: Grease pencil object to add strokes.
* \param ob_cu: Curve to convert.
- * \param gpencil_lines: Use lines for strokes.
* \param use_collections: Create layers using collection names.
- * \param only_stroke: The material must be only stroke without fill.
+ * \param scale_thickness: Scale thickness factor.
+ * \param sample: Sample distance, zero to disable.
*/
void BKE_gpencil_convert_curve(Main *bmain,
Scene *scene,
Object *ob_gp,
Object *ob_cu,
- const bool gpencil_lines,
const bool use_collections,
- const bool only_stroke)
+ const float scale_thickness,
+ const float sample)
{
if (ELEM(NULL, ob_gp, ob_cu) || (ob_gp->type != OB_GPENCIL) || (ob_gp->data == NULL)) {
return;
@@ -441,11 +507,32 @@ void BKE_gpencil_convert_curve(Main *bmain,
/* Read all splines of the curve and create a stroke for each. */
LISTBASE_FOREACH (Nurb *, nu, &cu->nurb) {
- gpencil_convert_spline(bmain, ob_gp, ob_cu, gpencil_lines, only_stroke, gpf, nu);
+ gpencil_convert_spline(bmain, ob_gp, ob_cu, scale_thickness, sample, gpf, nu);
}
+ /* Merge any similar material. */
+ int removed = 0;
+ BKE_gpencil_merge_materials(ob_gp, 0.001f, 0.001f, 0.001f, &removed);
+
+ /* Remove any unused slot. */
+ int actcol = ob_gp->actcol;
+
+ for (int slot = 1; slot <= ob_gp->totcol; slot++) {
+ while (slot <= ob_gp->totcol && !BKE_object_material_slot_used(ob_gp->data, slot)) {
+ ob_gp->actcol = slot;
+ BKE_object_material_slot_remove(bmain, ob_gp);
+
+ if (actcol >= slot) {
+ actcol--;
+ }
+ }
+ }
+
+ ob_gp->actcol = actcol;
+
/* Tag for recalculation */
DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
+ DEG_id_tag_update(&ob_gp->id, ID_RECALC_GEOMETRY);
}
/** \} */
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/layer.c b/source/blender/blenkernel/intern/layer.c
index cf6139d9449..4da59ff302d 100644
--- a/source/blender/blenkernel/intern/layer.c
+++ b/source/blender/blenkernel/intern/layer.c
@@ -1088,7 +1088,7 @@ bool BKE_base_is_visible(const View3D *v3d, const Base *base)
return base->flag & BASE_VISIBLE_VIEWLAYER;
}
-bool BKE_object_is_visible_in_viewport(const struct View3D *v3d, const struct Object *ob)
+bool BKE_object_is_visible_in_viewport(const View3D *v3d, const struct Object *ob)
{
BLI_assert(v3d != NULL);
@@ -1231,7 +1231,7 @@ static void layer_collection_local_sync(ViewLayer *view_layer,
}
}
-void BKE_layer_collection_local_sync(ViewLayer *view_layer, View3D *v3d)
+void BKE_layer_collection_local_sync(ViewLayer *view_layer, const View3D *v3d)
{
const unsigned short local_collections_uuid = v3d->local_collections_uuid;
@@ -1251,7 +1251,7 @@ void BKE_layer_collection_local_sync(ViewLayer *view_layer, View3D *v3d)
* Same as BKE_layer_collection_isolate_local but for a viewport
*/
void BKE_layer_collection_isolate_local(ViewLayer *view_layer,
- View3D *v3d,
+ const View3D *v3d,
LayerCollection *lc,
bool extend)
{
@@ -1463,11 +1463,11 @@ bool BKE_scene_has_object(Scene *scene, Object *ob)
* \{ */
typedef struct LayerObjectBaseIteratorData {
- View3D *v3d;
+ const View3D *v3d;
Base *base;
} LayerObjectBaseIteratorData;
-static bool object_bases_iterator_is_valid(View3D *v3d, Base *base, const int flag)
+static bool object_bases_iterator_is_valid(const View3D *v3d, Base *base, const int flag)
{
BLI_assert((v3d == NULL) || (v3d->spacetype == SPACE_VIEW3D));
@@ -1484,7 +1484,7 @@ static void object_bases_iterator_begin(BLI_Iterator *iter, void *data_in_v, con
{
ObjectsVisibleIteratorData *data_in = data_in_v;
ViewLayer *view_layer = data_in->view_layer;
- View3D *v3d = data_in->v3d;
+ const View3D *v3d = data_in->v3d;
Base *base = view_layer->object_bases.first;
/* when there are no objects */
diff --git a/source/blender/blenkernel/intern/layer_utils.c b/source/blender/blenkernel/intern/layer_utils.c
index d77c214130a..513bc9e1420 100644
--- a/source/blender/blenkernel/intern/layer_utils.c
+++ b/source/blender/blenkernel/intern/layer_utils.c
@@ -34,8 +34,69 @@
#include "MEM_guardedalloc.h"
+/* -------------------------------------------------------------------- */
+/** \name Selected Object Array
+ * \{ */
+
+Object **BKE_view_layer_array_selected_objects_params(
+ struct ViewLayer *view_layer,
+ const struct View3D *v3d,
+ uint *r_len,
+ const struct ObjectsInViewLayerParams *params)
+{
+ if (params->no_dup_data) {
+ FOREACH_SELECTED_OBJECT_BEGIN (view_layer, v3d, ob_iter) {
+ ID *id = ob_iter->data;
+ if (id) {
+ id->tag |= LIB_TAG_DOIT;
+ }
+ }
+ FOREACH_SELECTED_OBJECT_END;
+ }
+
+ Object **object_array = NULL;
+ BLI_array_declare(object_array);
+
+ FOREACH_SELECTED_OBJECT_BEGIN (view_layer, v3d, ob_iter) {
+ if (params->filter_fn) {
+ if (!params->filter_fn(ob_iter, params->filter_userdata)) {
+ continue;
+ }
+ }
+
+ if (params->no_dup_data) {
+ ID *id = ob_iter->data;
+ if (id) {
+ if (id->tag & LIB_TAG_DOIT) {
+ id->tag &= ~LIB_TAG_DOIT;
+ }
+ else {
+ continue;
+ }
+ }
+ }
+
+ BLI_array_append(object_array, ob_iter);
+ }
+ FOREACH_SELECTED_OBJECT_END;
+
+ object_array = MEM_reallocN(object_array, sizeof(*object_array) * BLI_array_len(object_array));
+ /* We always need a valid allocation (prevent crash on free). */
+ if (object_array == NULL) {
+ object_array = MEM_mallocN(0, __func__);
+ }
+ *r_len = BLI_array_len(object_array);
+ return object_array;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Objects in Mode Array
+ * \{ */
+
Base **BKE_view_layer_array_from_bases_in_mode_params(ViewLayer *view_layer,
- View3D *v3d,
+ const View3D *v3d,
uint *r_len,
const struct ObjectsInModeParams *params)
{
@@ -83,7 +144,7 @@ Base **BKE_view_layer_array_from_bases_in_mode_params(ViewLayer *view_layer,
}
Object **BKE_view_layer_array_from_objects_in_mode_params(ViewLayer *view_layer,
- View3D *v3d,
+ const View3D *v3d,
uint *r_len,
const struct ObjectsInModeParams *params)
{
@@ -97,6 +158,12 @@ Object **BKE_view_layer_array_from_objects_in_mode_params(ViewLayer *view_layer,
return (Object **)base_array;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Filter Functions
+ * \{ */
+
bool BKE_view_layer_filter_edit_mesh_has_uvs(Object *ob, void *UNUSED(user_data))
{
if (ob->type == OB_MESH) {
@@ -124,3 +191,5 @@ bool BKE_view_layer_filter_edit_mesh_has_edges(Object *ob, void *UNUSED(user_dat
}
return false;
}
+
+/** \} */
diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c
index 3b7aae98327..f8f171bd9d7 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
@@ -337,13 +339,16 @@ static int lib_id_expand_local_cb(LibraryIDLinkCallbackData *cb_data)
int const cb_flag = cb_data->cb_flag;
if (cb_flag & IDWALK_CB_LOOPBACK) {
- /* We should never have anything to do with loopback pointers here. */
+ /* We should never have anything to do with loop-back pointers here. */
return IDWALK_RET_NOP;
}
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 12d7f0e9e8e..2908ef133f0 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);
+ }
+ }
+ }
+}
+
+/**
+ * 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);
}
- 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);
+ /* 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)
{
@@ -1070,7 +1281,7 @@ bool BKE_lib_override_library_operations_create(Main *bmain, ID *local)
if (!is_template) {
/* Do not attempt to generate overriding rules from an empty place-holder generated by link
- * code when it cannot find to actual library/ID. Much better to keep the local datablock as
+ * code when it cannot find to actual library/ID. Much better to keep the local data-block as
* is in the file in that case, until broken lib is fixed. */
if (ID_MISSING(local->override_library->reference)) {
return ret;
@@ -1146,7 +1357,15 @@ void BKE_lib_override_library_main_operations_create(Main *bmain, const bool for
FOREACH_MAIN_ID_BEGIN (bmain, id) {
if (ID_IS_OVERRIDE_LIBRARY_REAL(id) &&
(force_auto || (id->tag & LIB_TAG_OVERRIDE_LIBRARY_AUTOREFRESH))) {
- BLI_task_pool_push(task_pool, lib_override_library_operations_create_cb, id, false, NULL);
+ /* Only check overrides if we do have the real reference data available, and not some empty
+ * 'placeholder' for missing data (broken links). */
+ if ((id->override_library->reference->tag & LIB_TAG_MISSING) == 0) {
+ BLI_task_pool_push(task_pool, lib_override_library_operations_create_cb, id, false, NULL);
+ }
+ else {
+ BKE_lib_override_library_properties_tag(
+ id->override_library, IDOVERRIDE_LIBRARY_TAG_UNUSED, false);
+ }
}
id->tag &= ~LIB_TAG_OVERRIDE_LIBRARY_AUTOREFRESH;
}
@@ -1386,7 +1605,7 @@ void BKE_lib_override_library_update(Main *bmain, ID *local)
}
/* Do not attempt to apply overriding rules over an empty place-holder generated by link code
- * when it cannot find to actual library/ID. Much better to keep the local datablock as loaded
+ * when it cannot find to actual library/ID. Much better to keep the local data-block as loaded
* from the file in that case, until broken lib is fixed. */
if (ID_MISSING(local->override_library->reference)) {
return;
@@ -1426,7 +1645,7 @@ void BKE_lib_override_library_update(Main *bmain, ID *local)
* manual handling here. */
BLI_strncpy(tmp_id->name, local->name, sizeof(tmp_id->name));
- /* Those ugly loopback pointers again... Luckily we only need to deal with the shape keys here,
+ /* Those ugly loop-back pointers again... Luckily we only need to deal with the shape keys here,
* collections' parents are fully runtime and reconstructed later. */
Key *local_key = BKE_key_from_id(local);
Key *tmp_key = BKE_key_from_id(tmp_id);
@@ -1466,8 +1685,22 @@ 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) {
+ /* Fun times 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 pose-bones 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. */
+ /* We know this data-block is not used anywhere besides local->override->storage. */
/* XXX For until we get fully shadow copies, we still need to ensure storage releases
* its usage of any ID pointers it may have. */
BKE_id_free_ex(bmain, local->override_library->storage, LIB_ID_FREE_NO_UI_USER, true);
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 de07c96e3f0..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 */
@@ -196,11 +202,11 @@ MetaElem *BKE_mball_element_add(MetaBall *mb, const int type)
return ml;
}
/**
- * Compute bounding box of all MetaElems/MetaBalls.
+ * Compute bounding box of all #MetaElem / #MetaBall
*
- * Bounding box is computed from polygonized surface. Object *ob is
- * basic MetaBall (usually with name Meta). All other MetaBalls (with
- * names Meta.001, Meta.002, etc) are included in this Bounding Box.
+ * Bounding box is computed from polygonized surface. \a ob is
+ * basic meta-balls (with name `Meta` for example). All other meta-ball objects
+ * (with names `Meta.001`, `Meta.002`, etc) are included in this bounding-box.
*/
void BKE_mball_texspace_calc(Object *ob)
{
@@ -298,25 +304,30 @@ float *BKE_mball_make_orco(Object *ob, ListBase *dispbase)
return orcodata;
}
-/* Note on mball basis stuff 2.5x (this is a can of worms)
+/**
+ * \brief Test, if \a ob is a basis meta-ball.
+ *
+ * It test last character of Object ID name. If last character
+ * is digit it return 0, else it return 1.
+ *
+ *
+ * Meta-Ball Basis Notes from Blender-2.5x
+ * =======================================
+ *
+ * This is a can of worms.
+ *
* This really needs a rewrite/refactor its totally broken in anything other then basic cases
- * Multiple Scenes + Set Scenes & mixing mball basis SHOULD work but fails to update the depsgraph
- * on rename and linking into scenes or removal of basis mball.
+ * Multiple Scenes + Set Scenes & mixing meta-ball basis _should_ work but fails to update the
+ * depsgraph on rename and linking into scenes or removal of basis meta-ball.
* So take care when changing this code.
*
- * Main idiot thing here is that the system returns find_basis_mball()
- * objects which fail a is_basis_mball() test.
+ * Main idiot thing here is that the system returns #BKE_mball_basis_find()
+ * objects which fail a #BKE_mball_is_basis() test.
*
- * Not only that but the depsgraph and their areas depend on this behavior!,
+ * Not only that but the depsgraph and their areas depend on this behavior,
* so making small fixes here isn't worth it.
* - Campbell
*/
-
-/** \brief Test, if Object *ob is basic MetaBall.
- *
- * It test last character of Object ID name. If last character
- * is digit it return 0, else it return 1.
- */
bool BKE_mball_is_basis(Object *ob)
{
/* just a quick test */
@@ -378,12 +389,12 @@ bool BKE_mball_is_any_unselected(const MetaBall *mb)
}
/**
- * \brief copy some properties from object to other metaball object with same base name
+ * \brief copy some properties from object to other meta-ball object with same base name
*
- * When some properties (wiresize, threshold, update flags) of metaball are changed, then this
- * properties are copied to all metaballs in same "group" (metaballs with same base name: MBall,
- * MBall.001, MBall.002, etc). The most important is to copy properties to the base metaball,
- * because this metaball influence polygonization of metaballs. */
+ * When some properties (wire-size, threshold, update flags) of meta-ball are changed, then this
+ * properties are copied to all meta-balls in same "group" (meta-balls with same base name:
+ * `MBall`, `MBall.001`, `MBall.002`, etc). The most important is to copy properties to the base
+ * meta-ball, because this meta-ball influence polygonization of meta-balls. */
void BKE_mball_properties_copy(Scene *scene, Object *active_object)
{
Scene *sce_iter = scene;
@@ -397,7 +408,7 @@ void BKE_mball_properties_copy(Scene *scene, Object *active_object)
BLI_split_name_num(basisname, &basisnr, active_object->id.name + 2, '.');
/* Pass depsgraph as NULL, which means we will not expand into
- * duplis unlike when we generate the mball. Expanding duplis
+ * duplis unlike when we generate the meta-ball. Expanding duplis
* would not be compatible when editing multiple view layers. */
BKE_scene_base_iter_next(NULL, &iter, &sce_iter, 0, NULL, NULL);
while (BKE_scene_base_iter_next(NULL, &iter, &sce_iter, 1, &base, &ob)) {
@@ -424,12 +435,11 @@ void BKE_mball_properties_copy(Scene *scene, Object *active_object)
/** \brief This function finds basic MetaBall.
*
- * Basic MetaBall doesn't include any number at the end of
- * its name. All MetaBalls with same base of name can be
- * blended. MetaBalls with different basic name can't be
- * blended.
+ * Basic meta-ball doesn't include any number at the end of
+ * its name. All meta-balls with same base of name can be
+ * blended. meta-balls with different basic name can't be blended.
*
- * warning!, is_basis_mball() can fail on returned object, see long note above.
+ * \warning #BKE_mball_is_basis() can fail on returned object, see function docs for details.
*/
Object *BKE_mball_basis_find(Scene *scene, Object *basis)
{
diff --git a/source/blender/blenkernel/intern/mball_tessellate.c b/source/blender/blenkernel/intern/mball_tessellate.c
index 92e16be878f..2a7d3f1797d 100644
--- a/source/blender/blenkernel/intern/mball_tessellate.c
+++ b/source/blender/blenkernel/intern/mball_tessellate.c
@@ -405,7 +405,7 @@ static float densfunc(const MetaElem *ball, float x, float y, float z)
}
/**
- * Computes density at given position form all metaballs which contain this point in their box.
+ * Computes density at given position form all meta-balls which contain this point in their box.
* Traverses BVH using a queue.
*/
static float metaball(PROCESS *process, float x, float y, float z)
@@ -1443,9 +1443,9 @@ void BKE_mball_polygonize(Depsgraph *depsgraph, Scene *scene, Object *ob, ListBa
if (process.totelem > 0) {
build_bvh_spatial(&process, &process.metaball_bvh, 0, process.totelem, &process.allbb);
- /* Don't polygonize metaballs with too high resolution (base mball to small)
- * note: Eps was 0.0001f but this was giving problems for blood animation for durian,
- * using 0.00001f. */
+ /* Don't polygonize meta-balls with too high resolution (base mball to small)
+ * note: Eps was 0.0001f but this was giving problems for blood animation for
+ * the open movie "Sintel", using 0.00001f. */
if (ob->scale[0] > 0.00001f * (process.allbb.max[0] - process.allbb.min[0]) ||
ob->scale[1] > 0.00001f * (process.allbb.max[1] - process.allbb.min[1]) ||
ob->scale[2] > 0.00001f * (process.allbb.max[2] - process.allbb.min[2])) {
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_convert.c b/source/blender/blenkernel/intern/mesh_convert.c
index 76a6d23bc8f..9426d09885e 100644
--- a/source/blender/blenkernel/intern/mesh_convert.c
+++ b/source/blender/blenkernel/intern/mesh_convert.c
@@ -1111,15 +1111,7 @@ static Mesh *mesh_new_from_mesh_object_with_layers(Depsgraph *depsgraph, Object
Scene *scene = DEG_get_evaluated_scene(depsgraph);
CustomData_MeshMasks mask = CD_MASK_MESH;
- Mesh *result;
-
- if (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER) {
- result = mesh_create_eval_final_render(depsgraph, scene, &object_for_eval, &mask);
- }
- else {
- result = mesh_create_eval_final_view(depsgraph, scene, &object_for_eval, &mask);
- }
-
+ Mesh *result = mesh_create_eval_final(depsgraph, scene, &object_for_eval, &mask);
return result;
}
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..499e2311297 100644
--- a/source/blender/blenkernel/intern/node.c
+++ b/source/blender/blenkernel/intern/node.c
@@ -106,8 +106,6 @@ static void ntree_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, c
{
bNodeTree *ntree_dst = (bNodeTree *)id_dst;
const bNodeTree *ntree_src = (const bNodeTree *)id_src;
- bNodeSocket *sock_dst, *sock_src;
- bNodeLink *link_dst;
/* We never handle usercount here for own data. */
const int flag_subdata = flag | LIB_ID_CREATE_NO_USER_REFCOUNT;
@@ -144,7 +142,7 @@ static void ntree_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, c
/* copy links */
BLI_duplicatelist(&ntree_dst->links, &ntree_src->links);
- for (link_dst = ntree_dst->links.first; link_dst; link_dst = link_dst->next) {
+ LISTBASE_FOREACH (bNodeLink *, link_dst, &ntree_dst->links) {
link_dst->fromnode = BLI_ghash_lookup_default(new_pointers, link_dst->fromnode, NULL);
link_dst->fromsock = BLI_ghash_lookup_default(new_pointers, link_dst->fromsock, NULL);
link_dst->tonode = BLI_ghash_lookup_default(new_pointers, link_dst->tonode, NULL);
@@ -157,6 +155,7 @@ static void ntree_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, c
/* copy interface sockets */
BLI_duplicatelist(&ntree_dst->inputs, &ntree_src->inputs);
+ bNodeSocket *sock_dst, *sock_src;
for (sock_dst = ntree_dst->inputs.first, sock_src = ntree_src->inputs.first; sock_dst != NULL;
sock_dst = sock_dst->next, sock_src = sock_src->next) {
node_socket_copy(sock_dst, sock_src, flag_subdata);
@@ -201,8 +200,6 @@ static void ntree_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, c
static void ntree_free_data(ID *id)
{
bNodeTree *ntree = (bNodeTree *)id;
- bNode *node, *next;
- bNodeSocket *sock, *nextsock;
/* XXX hack! node trees should not store execution graphs at all.
* This should be removed when old tree types no longer require it.
@@ -229,19 +226,16 @@ static void ntree_free_data(ID *id)
BLI_freelistN(&ntree->links); /* do first, then unlink_node goes fast */
- for (node = ntree->nodes.first; node; node = next) {
- next = node->next;
+ LISTBASE_FOREACH_MUTABLE (bNode *, node, &ntree->nodes) {
node_free_node(ntree, node);
}
/* free interface sockets */
- for (sock = ntree->inputs.first; sock; sock = nextsock) {
- nextsock = sock->next;
+ LISTBASE_FOREACH_MUTABLE (bNodeSocket *, sock, &ntree->inputs) {
node_socket_interface_free(ntree, sock, false);
MEM_freeN(sock);
}
- for (sock = ntree->outputs.first; sock; sock = nextsock) {
- nextsock = sock->next;
+ LISTBASE_FOREACH_MUTABLE (bNodeSocket *, sock, &ntree->outputs) {
node_socket_interface_free(ntree, sock, false);
MEM_freeN(sock);
}
@@ -333,7 +327,7 @@ static void node_foreach_cache(ID *id,
#endif
if (nodetree->type == NTREE_COMPOSIT) {
- for (bNode *node = nodetree->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, &nodetree->nodes) {
if (node->type == CMP_NODE_MOVIEDISTORTION) {
key.offset_in_ID = (size_t)BLI_ghashutil_strhash_p(node->name);
key.cache_v = node->storage;
@@ -359,6 +353,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)
@@ -523,9 +522,6 @@ static void update_typeinfo(Main *bmain,
}
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
- bNode *node;
- bNodeSocket *sock;
-
ntree->init |= NTREE_TYPE_INIT;
if (treetype && STREQ(ntree->idname, treetype->idname)) {
@@ -533,18 +529,18 @@ static void update_typeinfo(Main *bmain,
}
/* initialize nodes */
- for (node = ntree->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (nodetype && STREQ(node->idname, nodetype->idname)) {
node_set_typeinfo(C, ntree, node, unregister ? NULL : nodetype);
}
/* initialize node sockets */
- for (sock = node->inputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
if (socktype && STREQ(sock->idname, socktype->idname)) {
node_socket_set_typeinfo(ntree, sock, unregister ? NULL : socktype);
}
}
- for (sock = node->outputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
if (socktype && STREQ(sock->idname, socktype->idname)) {
node_socket_set_typeinfo(ntree, sock, unregister ? NULL : socktype);
}
@@ -552,12 +548,12 @@ static void update_typeinfo(Main *bmain,
}
/* initialize tree sockets */
- for (sock = ntree->inputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) {
if (socktype && STREQ(sock->idname, socktype->idname)) {
node_socket_set_typeinfo(ntree, sock, unregister ? NULL : socktype);
}
}
- for (sock = ntree->outputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) {
if (socktype && STREQ(sock->idname, socktype->idname)) {
node_socket_set_typeinfo(ntree, sock, unregister ? NULL : socktype);
}
@@ -574,28 +570,25 @@ static void update_typeinfo(Main *bmain,
*/
void ntreeSetTypes(const struct bContext *C, bNodeTree *ntree)
{
- bNode *node;
- bNodeSocket *sock;
-
ntree->init |= NTREE_TYPE_INIT;
ntree_set_typeinfo(ntree, ntreeTypeFind(ntree->idname));
- for (node = ntree->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
node_set_typeinfo(C, ntree, node, nodeTypeFind(node->idname));
- for (sock = node->inputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
node_socket_set_typeinfo(ntree, sock, nodeSocketTypeFind(sock->idname));
}
- for (sock = node->outputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
node_socket_set_typeinfo(ntree, sock, nodeSocketTypeFind(sock->idname));
}
}
- for (sock = ntree->inputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) {
node_socket_set_typeinfo(ntree, sock, nodeSocketTypeFind(sock->idname));
}
- for (sock = ntree->outputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) {
node_socket_set_typeinfo(ntree, sock, nodeSocketTypeFind(sock->idname));
}
}
@@ -3645,8 +3638,6 @@ static void ntreeUpdateSimulationDependencies(Main *main, bNodeTree *simulation_
void ntreeUpdateTree(Main *bmain, bNodeTree *ntree)
{
- bNode *node;
-
if (!ntree) {
return;
}
@@ -3663,7 +3654,7 @@ void ntreeUpdateTree(Main *bmain, bNodeTree *ntree)
}
/* update individual nodes */
- for (node = ntree->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
/* node tree update tags override individual node update flags */
if ((node->update & NODE_UPDATE) || (ntree->update & NTREE_UPDATE)) {
if (node->typeinfo->updatefunc) {
@@ -3706,7 +3697,7 @@ void ntreeUpdateTree(Main *bmain, bNodeTree *ntree)
}
/* clear update flags */
- for (node = ntree->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
node->update = 0;
}
ntree->update = 0;
diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c
index 31420b3adc6..e2bed85399b 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)
@@ -1585,13 +1580,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/object_dupli.c b/source/blender/blenkernel/intern/object_dupli.c
index de14d2ceff6..d69f4a39263 100644
--- a/source/blender/blenkernel/intern/object_dupli.c
+++ b/source/blender/blenkernel/intern/object_dupli.c
@@ -30,6 +30,7 @@
#include "BLI_listbase.h"
#include "BLI_string_utf8.h"
+#include "BLI_alloca.h"
#include "BLI_math.h"
#include "BLI_rand.h"
@@ -44,6 +45,7 @@
#include "BKE_collection.h"
#include "BKE_duplilist.h"
#include "BKE_editmesh.h"
+#include "BKE_editmesh_cache.h"
#include "BKE_font.h"
#include "BKE_global.h"
#include "BKE_idprop.h"
@@ -84,11 +86,11 @@ typedef struct DupliContext {
const struct DupliGenerator *gen;
/** Result containers. */
- ListBase *duplilist; /* legacy doubly-linked list */
+ ListBase *duplilist; /* Legacy doubly-linked list. */
} DupliContext;
typedef struct DupliGenerator {
- short type; /* dupli type */
+ short type; /* Dupli Type, see members of #OB_DUPLI. */
void (*make_duplis)(const DupliContext *ctx);
} DupliGenerator;
@@ -160,7 +162,7 @@ static DupliObject *make_dupli(const DupliContext *ctx,
DupliObject *dob;
int i;
- /* add a DupliObject instance to the result container */
+ /* Add a #DupliObject instance to the result container. */
if (ctx->duplilist) {
dob = MEM_callocN(sizeof(DupliObject), "dupli object");
BLI_addtail(ctx->duplilist, dob);
@@ -173,28 +175,28 @@ static DupliObject *make_dupli(const DupliContext *ctx,
mul_m4_m4m4(dob->mat, (float(*)[4])ctx->space_mat, mat);
dob->type = ctx->gen->type;
- /* set persistent id, which is an array with a persistent index for each level
+ /* Set persistent id, which is an array with a persistent index for each level
* (particle number, vertex number, ..). by comparing this we can find the same
- * dupli object between frames, which is needed for motion blur. last level
- * goes first in the array. */
+ * dupli-object between frames, which is needed for motion blur.
+ * The last level is ordered first in the array. */
dob->persistent_id[0] = index;
for (i = 1; i < ctx->level + 1; i++) {
dob->persistent_id[i] = ctx->persistent_id[ctx->level - i];
}
- /* fill rest of values with INT_MAX which index will never have as value */
+ /* Fill rest of values with #INT_MAX which index will never have as value. */
for (; i < MAX_DUPLI_RECUR; i++) {
dob->persistent_id[i] = INT_MAX;
}
- /* metaballs never draw in duplis, they are instead merged into one by the basis
- * mball outside of the group. this does mean that if that mball is not in the
+ /* Meta-balls never draw in duplis, they are instead merged into one by the basis
+ * meta-ball outside of the group. this does mean that if that meta-ball is not in the
* scene, they will not show up at all, limitation that should be solved once. */
if (ob->type == OB_MBALL) {
dob->no_draw = true;
}
- /* random number */
- /* the logic here is designed to match Cycles */
+ /* Random number.
+ * The logic here is designed to match Cycles. */
dob->random_id = BLI_hash_string(dob->ob->id.name + 2);
if (dob->persistent_id[0] != INT_MAX) {
@@ -214,16 +216,16 @@ static DupliObject *make_dupli(const DupliContext *ctx,
}
/**
- * Recursive dupli objects.
+ * Recursive dupli-objects.
*
- * \param space_mat: is the local dupli space (excluding dupli #Object.obmat).
+ * \param space_mat: is the local dupli-space (excluding dupli #Object.obmat).
*/
static void make_recursive_duplis(const DupliContext *ctx,
Object *ob,
const float space_mat[4][4],
int index)
{
- /* simple preventing of too deep nested collections with MAX_DUPLI_RECUR */
+ /* Simple preventing of too deep nested collections with #MAX_DUPLI_RECUR. */
if (ctx->level < MAX_DUPLI_RECUR) {
DupliContext rctx;
copy_dupli_context(&rctx, ctx, ob, space_mat, index);
@@ -269,9 +271,9 @@ static void make_child_duplis(const DupliContext *ctx,
DupliContext pctx;
copy_dupli_context(&pctx, ctx, ctx->object, NULL, _base_id);
- /* metaballs have a different dupli handling */
+ /* Meta-balls have a different dupli handling. */
if (ob->type != OB_MBALL) {
- ob->flag |= OB_DONE; /* doesn't render */
+ ob->flag |= OB_DONE; /* Doesn't render. */
}
make_child_duplis_cb(&pctx, userdata, ob);
}
@@ -287,9 +289,9 @@ static void make_child_duplis(const DupliContext *ctx,
DupliContext pctx;
copy_dupli_context(&pctx, ctx, ctx->object, NULL, baseid);
- /* metaballs have a different dupli handling */
+ /* Meta-balls have a different dupli-handling. */
if (ob->type != OB_MBALL) {
- ob->flag |= OB_DONE; /* doesn't render */
+ ob->flag |= OB_DONE; /* Doesn't render. */
}
make_child_duplis_cb(&pctx, userdata, ob);
@@ -301,6 +303,57 @@ static void make_child_duplis(const DupliContext *ctx,
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Internal Data Access Utilities
+ * \{ */
+
+static Mesh *mesh_data_from_duplicator_object(Object *ob,
+ BMEditMesh **r_em,
+ const float (**r_vert_coords)[3],
+ const float (**r_vert_normals)[3])
+{
+ /* Gather mesh info. */
+ BMEditMesh *em = BKE_editmesh_from_object(ob);
+ Mesh *me_eval;
+
+ *r_em = NULL;
+ *r_vert_coords = NULL;
+ if (r_vert_normals != NULL) {
+ *r_vert_normals = NULL;
+ }
+
+ /* We do not need any render-specific handling anymore, depsgraph takes care of that. */
+ /* NOTE: Do direct access to the evaluated mesh: this function is used
+ * during meta balls evaluation. But even without those all the objects
+ * which are needed for correct instancing are already evaluated. */
+ if (em != NULL) {
+ /* Note that this will only show deformation if #eModifierMode_OnCage is enabled.
+ * We could change this but it matches 2.7x behavior. */
+ me_eval = em->mesh_eval_cage;
+ if ((me_eval == NULL) || (me_eval->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH)) {
+ EditMeshData *emd = me_eval ? me_eval->runtime.edit_data : NULL;
+
+ /* Only assign edit-mesh in the case we can't use `me_eval`. */
+ *r_em = em;
+ me_eval = NULL;
+
+ if ((emd != NULL) && (emd->vertexCos != NULL)) {
+ *r_vert_coords = emd->vertexCos;
+ if (r_vert_normals != NULL) {
+ BKE_editmesh_cache_ensure_vert_normals(em, emd);
+ *r_vert_normals = emd->vertexNos;
+ }
+ }
+ }
+ }
+ else {
+ me_eval = BKE_object_get_evaluated_mesh(ob);
+ }
+ return me_eval;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Dupli-Collection Implementation (#OB_DUPLICOLLECTION)
* \{ */
@@ -315,23 +368,23 @@ static void make_duplis_collection(const DupliContext *ctx)
}
collection = ob->instance_collection;
- /* combine collection offset and obmat */
+ /* Combine collection offset and `obmat`. */
unit_m4(collection_mat);
sub_v3_v3(collection_mat[3], collection->instance_offset);
mul_m4_m4m4(collection_mat, ob->obmat, collection_mat);
- /* don't access 'ob->obmat' from now on. */
+ /* Don't access 'ob->obmat' from now on. */
eEvaluationMode mode = DEG_get_mode(ctx->depsgraph);
FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (collection, cob, mode) {
if (cob != ob) {
float mat[4][4];
- /* collection dupli offset, should apply after everything else */
+ /* Collection dupli-offset, should apply after everything else. */
mul_m4_m4m4(mat, collection_mat, cob->obmat);
make_dupli(ctx, cob, mat, _base_id);
- /* recursion */
+ /* Recursion. */
make_recursive_duplis(ctx, cob, collection_mat, _base_id);
}
}
@@ -349,124 +402,207 @@ static const DupliGenerator gen_dupli_collection = {
/** \name Dupli-Vertices Implementation (#OB_DUPLIVERTS for Geometry)
* \{ */
-typedef struct VertexDupliData {
- Mesh *me_eval;
- BMEditMesh *edit_mesh;
- int totvert;
- float (*orco)[3];
+/** Values shared between different mesh types. */
+typedef struct VertexDupliData_Params {
+ /**
+ * It's important we use this context instead of the `ctx` passed into #make_child_duplis
+ * since these won't match in the case of recursion.
+ */
+ const DupliContext *ctx;
+
bool use_rotation;
+} VertexDupliData_Params;
- const DupliContext *ctx;
- Object *inst_ob; /* object to instantiate (argument for vertex map callback) */
- float child_imat[4][4];
-} VertexDupliData;
+typedef struct VertexDupliData_Mesh {
+ VertexDupliData_Params params;
+
+ int totvert;
+ const MVert *mvert;
+ const float (*orco)[3];
+} VertexDupliData_Mesh;
+
+typedef struct VertexDupliData_EditMesh {
+ VertexDupliData_Params params;
+
+ BMEditMesh *em;
+
+ /* Can be NULL. */
+ const float (*vert_coords)[3];
+ const float (*vert_normals)[3];
+
+ /**
+ * \note The edit-mesh may assign #DupliObject.orco in cases when a regular mesh wouldn't.
+ * For edit-meshes we only check for deformation, for regular meshes we check if #CD_ORCO exists.
+ *
+ * At the moment this isn't a meaningful difference since requesting #CD_ORCO causes the
+ * edit-mesh to be converted into a mesh.
+ */
+ bool has_orco;
+} VertexDupliData_EditMesh;
+
+/**
+ * \param no: The direction,
+ * currently this is copied from a `short[3]` normal without division.
+ * Can be null when \a use_rotation is false.
+ */
static void get_duplivert_transform(const float co[3],
- const short no[3],
- bool use_rotation,
- short axis,
- short upflag,
- float mat[4][4])
+ const float no[3],
+ const bool use_rotation,
+ const short axis,
+ const short upflag,
+ float r_mat[4][4])
{
float quat[4];
const float size[3] = {1.0f, 1.0f, 1.0f};
if (use_rotation) {
- /* construct rotation matrix from normals */
- float nor_f[3];
- nor_f[0] = (float)-no[0];
- nor_f[1] = (float)-no[1];
- nor_f[2] = (float)-no[2];
- vec_to_quat(quat, nor_f, axis, upflag);
+ /* Construct rotation matrix from normals. */
+ float no_flip[3];
+ negate_v3_v3(no_flip, no);
+ vec_to_quat(quat, no_flip, axis, upflag);
}
else {
unit_qt(quat);
}
- loc_quat_size_to_mat4(mat, co, quat, size);
+ loc_quat_size_to_mat4(r_mat, co, quat, size);
}
-static void vertex_dupli(const VertexDupliData *vdd,
- int index,
- const float co[3],
- const short no[3])
+static DupliObject *vertex_dupli(const DupliContext *ctx,
+ Object *inst_ob,
+ const float child_imat[4][4],
+ int index,
+ const float co[3],
+ const float no[3],
+ const bool use_rotation)
{
- Object *inst_ob = vdd->inst_ob;
- DupliObject *dob;
- float obmat[4][4], space_mat[4][4];
+ /* `obmat` is transform to vertex. */
+ float obmat[4][4];
+ get_duplivert_transform(co, no, use_rotation, inst_ob->trackflag, inst_ob->upflag, obmat);
+
+ float space_mat[4][4];
- /* obmat is transform to vertex */
- get_duplivert_transform(co, no, vdd->use_rotation, inst_ob->trackflag, inst_ob->upflag, obmat);
- /* make offset relative to inst_ob using relative child transform */
- mul_mat3_m4_v3((float(*)[4])vdd->child_imat, obmat[3]);
- /* apply obmat _after_ the local vertex transform */
+ /* Make offset relative to inst_ob using relative child transform. */
+ mul_mat3_m4_v3(child_imat, obmat[3]);
+ /* Apply `obmat` _after_ the local vertex transform. */
mul_m4_m4m4(obmat, inst_ob->obmat, obmat);
- /* space matrix is constructed by removing obmat transform,
- * this yields the worldspace transform for recursive duplis
- */
+ /* Space matrix is constructed by removing `obmat` transform,
+ * this yields the world-space transform for recursive duplis. */
mul_m4_m4m4(space_mat, obmat, inst_ob->imat);
- dob = make_dupli(vdd->ctx, vdd->inst_ob, obmat, index);
+ DupliObject *dob = make_dupli(ctx, inst_ob, obmat, index);
- if (vdd->orco) {
- copy_v3_v3(dob->orco, vdd->orco[index]);
- }
+ /* Recursion. */
+ make_recursive_duplis(ctx, inst_ob, space_mat, index);
- /* recursion */
- make_recursive_duplis(vdd->ctx, vdd->inst_ob, space_mat, index);
+ return dob;
}
-static void make_child_duplis_verts(const DupliContext *ctx, void *userdata, Object *child)
+static void make_child_duplis_verts_from_mesh(const DupliContext *ctx,
+ void *userdata,
+ Object *inst_ob)
{
- VertexDupliData *vdd = userdata;
- Mesh *me_eval = vdd->me_eval;
+ VertexDupliData_Mesh *vdd = userdata;
+ const bool use_rotation = vdd->params.use_rotation;
- vdd->inst_ob = child;
- invert_m4_m4(child->imat, child->obmat);
- /* relative transform from parent to child space */
- mul_m4_m4m4(vdd->child_imat, child->imat, ctx->object->obmat);
+ const MVert *mvert = vdd->mvert;
+ const int totvert = vdd->totvert;
- const MVert *mvert = me_eval->mvert;
- for (int i = 0; i < me_eval->totvert; i++) {
- vertex_dupli(vdd, i, mvert[i].co, mvert[i].no);
+ invert_m4_m4(inst_ob->imat, inst_ob->obmat);
+ /* Relative transform from parent to child space. */
+ float child_imat[4][4];
+ mul_m4_m4m4(child_imat, inst_ob->imat, ctx->object->obmat);
+
+ const MVert *mv = mvert;
+ for (int i = 0; i < totvert; i++, mv++) {
+ const float *co = mv->co;
+ const float no[3] = {UNPACK3(mv->no)};
+ DupliObject *dob = vertex_dupli(vdd->params.ctx, inst_ob, child_imat, i, co, no, use_rotation);
+ if (vdd->orco) {
+ copy_v3_v3(dob->orco, vdd->orco[i]);
+ }
}
}
-static void make_duplis_verts(const DupliContext *ctx)
+static void make_child_duplis_verts_from_editmesh(const DupliContext *ctx,
+ void *userdata,
+ Object *inst_ob)
{
- Object *parent = ctx->object;
- VertexDupliData vdd;
+ VertexDupliData_EditMesh *vdd = userdata;
+ BMEditMesh *em = vdd->em;
+ const bool use_rotation = vdd->params.use_rotation;
+
+ invert_m4_m4(inst_ob->imat, inst_ob->obmat);
+ /* Relative transform from parent to child space. */
+ float child_imat[4][4];
+ mul_m4_m4m4(child_imat, inst_ob->imat, ctx->object->obmat);
- vdd.ctx = ctx;
- vdd.use_rotation = parent->transflag & OB_DUPLIROT;
+ BMVert *v;
+ BMIter iter;
+ int i;
- /* gather mesh info */
- {
- vdd.edit_mesh = BKE_editmesh_from_object(parent);
-
- /* We do not need any render-specific handling anymore, depsgraph takes care of that. */
- /* NOTE: Do direct access to the evaluated mesh: this function is used
- * during meta balls evaluation. But even without those all the objects
- * which are needed for correct instancing are already evaluated. */
- if (vdd.edit_mesh != NULL) {
- vdd.me_eval = vdd.edit_mesh->mesh_eval_cage;
+ const float(*vert_coords)[3] = vdd->vert_coords;
+ const float(*vert_normals)[3] = vdd->vert_normals;
+
+ BM_ITER_MESH_INDEX (v, &iter, em->bm, BM_VERTS_OF_MESH, i) {
+ const float *co, *no;
+ if (vert_coords != NULL) {
+ co = vert_coords[i];
+ no = vert_normals ? vert_normals[i] : NULL;
}
else {
- vdd.me_eval = BKE_object_get_evaluated_mesh(parent);
+ co = v->co;
+ no = v->no;
}
- if (vdd.me_eval == NULL) {
- return;
+ DupliObject *dob = vertex_dupli(vdd->params.ctx, inst_ob, child_imat, i, co, no, use_rotation);
+ if (vdd->has_orco) {
+ copy_v3_v3(dob->orco, v->co);
}
-
- vdd.orco = CustomData_get_layer(&vdd.me_eval->vdata, CD_ORCO);
- vdd.totvert = vdd.me_eval->totvert;
}
+}
- make_child_duplis(ctx, &vdd, make_child_duplis_verts);
+static void make_duplis_verts(const DupliContext *ctx)
+{
+ Object *parent = ctx->object;
+ const bool use_rotation = parent->transflag & OB_DUPLIROT;
+
+ /* Gather mesh info. */
+ BMEditMesh *em = NULL;
+ const float(*vert_coords)[3] = NULL;
+ const float(*vert_normals)[3] = NULL;
+ Mesh *me_eval = mesh_data_from_duplicator_object(
+ parent, &em, &vert_coords, use_rotation ? &vert_normals : NULL);
+ if (em == NULL && me_eval == NULL) {
+ return;
+ }
- vdd.me_eval = NULL;
+ VertexDupliData_Params vdd_params = {
+ .ctx = ctx,
+ .use_rotation = use_rotation,
+ };
+
+ if (em != NULL) {
+ VertexDupliData_EditMesh vdd = {
+ .params = vdd_params,
+ .em = em,
+ .vert_coords = vert_coords,
+ .vert_normals = vert_normals,
+ .has_orco = (vert_coords != NULL),
+ };
+ make_child_duplis(ctx, &vdd, make_child_duplis_verts_from_editmesh);
+ }
+ else {
+ VertexDupliData_Mesh vdd = {
+ .params = vdd_params,
+ .totvert = me_eval->totvert,
+ .mvert = me_eval->mvert,
+ .orco = CustomData_get_layer(&me_eval->vdata, CD_ORCO),
+ };
+ make_child_duplis(ctx, &vdd, make_child_duplis_verts_from_mesh);
+ }
}
static const DupliGenerator gen_dupli_verts = {
@@ -496,7 +632,7 @@ static Object *find_family_object(
ch_utf8_len = BLI_str_utf8_from_unicode(ch, ch_utf8);
ch_utf8[ch_utf8_len] = '\0';
- ch_utf8_len += 1; /* compare with null terminator */
+ ch_utf8_len += 1; /* Compare with null terminator. */
for (ob = bmain->objects.first; ob; ob = ob->id.next) {
if (STREQLEN(ob->id.name + 2 + family_len, ch_utf8, ch_utf8_len)) {
@@ -506,7 +642,7 @@ static Object *find_family_object(
}
}
- /* inserted value can be NULL, just to save searches in future */
+ /* Inserted value can be NULL, just to save searches in future. */
BLI_ghash_insert(family_gh, ch_key, ob);
}
@@ -526,14 +662,14 @@ static void make_duplis_font(const DupliContext *ctx)
const char32_t *text = NULL;
bool text_free = false;
- /* font dupliverts not supported inside collections */
+ /* Font dupli-verts not supported inside collections. */
if (ctx->collection) {
return;
}
copy_m4_m4(pmat, par->obmat);
- /* in par the family name is stored, use this to find the other objects */
+ /* In `par` the family name is stored, use this to find the other objects. */
BKE_vfont_to_curve_ex(
par, par->data, FO_DUPLI, NULL, &text, &text_len, &text_free, &chartransdata);
@@ -549,7 +685,7 @@ static void make_duplis_font(const DupliContext *ctx)
ct = chartransdata;
- /* cache result */
+ /* Cache result. */
family_len = strlen(cu->family);
family_gh = BLI_ghash_int_new_ex(__func__, 256);
@@ -559,9 +695,9 @@ static void make_duplis_font(const DupliContext *ctx)
/* Advance matching BLI_str_utf8_as_utf32. */
for (a = 0; a < text_len; a++, ct++) {
- /* XXX That G.main is *really* ugly, but not sure what to do here...
- * Definitively don't think it would be safe to put back Main *bmain pointer
- * in DupliContext as done in 2.7x? */
+ /* XXX That G.main is *really* ugly, but not sure what to do here.
+ * Definitively don't think it would be safe to put back `Main *bmain` pointer
+ * in #DupliContext as done in 2.7x? */
ob = find_family_object(G.main, cu->family, family_len, (unsigned int)text[a], family_gh);
if (is_eval_curve) {
@@ -674,37 +810,65 @@ static const DupliGenerator gen_dupli_verts_pointcloud = {
/** \name Dupli-Faces Implementation (#OB_DUPLIFACES)
* \{ */
-typedef struct FaceDupliData {
- Mesh *me_eval;
- int totface;
- MPoly *mpoly;
- MLoop *mloop;
- MVert *mvert;
- float (*orco)[3];
- MLoopUV *mloopuv;
+/** Values shared between different mesh types. */
+typedef struct FaceDupliData_Params {
+ /**
+ * It's important we use this context instead of the `ctx` passed into #make_child_duplis
+ * since these won't match in the case of recursion.
+ */
+ const DupliContext *ctx;
+
bool use_scale;
-} FaceDupliData;
+} FaceDupliData_Params;
-static void get_dupliface_transform(
- MPoly *mpoly, MLoop *mloop, MVert *mvert, bool use_scale, float scale_fac, float mat[4][4])
+typedef struct FaceDupliData_Mesh {
+ FaceDupliData_Params params;
+
+ int totface;
+ const MPoly *mpoly;
+ const MLoop *mloop;
+ const MVert *mvert;
+ const float (*orco)[3];
+ const MLoopUV *mloopuv;
+} FaceDupliData_Mesh;
+
+typedef struct FaceDupliData_EditMesh {
+ FaceDupliData_Params params;
+
+ BMEditMesh *em;
+
+ bool has_orco, has_uvs;
+ int cd_loop_uv_offset;
+ /* Can be NULL. */
+ const float (*vert_coords)[3];
+} FaceDupliData_EditMesh;
+
+static void get_dupliface_transform_from_coords(const float coords[][3],
+ const int coords_len,
+ const bool use_scale,
+ const float scale_fac,
+ float r_mat[4][4])
{
float loc[3], quat[4], scale, size[3];
- float f_no[3];
- /* location */
- BKE_mesh_calc_poly_center(mpoly, mloop, mvert, loc);
- /* rotation */
+ /* Location. */
{
- const float *v1, *v2, *v3;
- BKE_mesh_calc_poly_normal(mpoly, mloop, mvert, f_no);
- v1 = mvert[mloop[0].v].co;
- v2 = mvert[mloop[1].v].co;
- v3 = mvert[mloop[2].v].co;
- tri_to_quat_ex(quat, v1, v2, v3, f_no);
+ const float w = 1.0f / (float)coords_len;
+ zero_v3(loc);
+ for (int i = 0; i < coords_len; i++) {
+ madd_v3_v3fl(loc, coords[i], w);
+ }
+ }
+ /* Rotation. */
+ {
+ float f_no[3];
+ cross_poly_v3(f_no, coords, (uint)coords_len);
+ normalize_v3(f_no);
+ tri_to_quat_ex(quat, coords[0], coords[1], coords[2], f_no);
}
- /* scale */
+ /* Scale. */
if (use_scale) {
- float area = BKE_mesh_calc_poly_area(mpoly, mloop, mvert);
+ const float area = area_poly_v3(coords, (uint)coords_len);
scale = sqrtf(area) * scale_fac;
}
else {
@@ -712,58 +876,131 @@ static void get_dupliface_transform(
}
size[0] = size[1] = size[2] = scale;
- loc_quat_size_to_mat4(mat, loc, quat, size);
+ loc_quat_size_to_mat4(r_mat, loc, quat, size);
}
-static void make_child_duplis_faces(const DupliContext *ctx, void *userdata, Object *inst_ob)
+static DupliObject *face_dupli(const DupliContext *ctx,
+ Object *inst_ob,
+ const float child_imat[4][4],
+ const int index,
+ const bool use_scale,
+ const float scale_fac,
+ const float (*coords)[3],
+ const int coords_len)
{
- FaceDupliData *fdd = userdata;
- MPoly *mpoly = fdd->mpoly, *mp;
- MLoop *mloop = fdd->mloop;
- MVert *mvert = fdd->mvert;
- float(*orco)[3] = fdd->orco;
- MLoopUV *mloopuv = fdd->mloopuv;
- int a, totface = fdd->totface;
- float child_imat[4][4];
- DupliObject *dob;
+ float obmat[4][4];
+ float space_mat[4][4];
- invert_m4_m4(inst_ob->imat, inst_ob->obmat);
- /* relative transform from parent to child space */
- mul_m4_m4m4(child_imat, inst_ob->imat, ctx->object->obmat);
+ /* `obmat` is transform to face. */
+ get_dupliface_transform_from_coords(coords, coords_len, use_scale, scale_fac, obmat);
- for (a = 0, mp = mpoly; a < totface; a++, mp++) {
- MLoop *loopstart = mloop + mp->loopstart;
- float space_mat[4][4], obmat[4][4];
+ /* Make offset relative to inst_ob using relative child transform. */
+ mul_mat3_m4_v3(child_imat, obmat[3]);
- if (UNLIKELY(mp->totloop < 3)) {
- continue;
- }
+ /* XXX ugly hack to ensure same behavior as in master.
+ * This should not be needed, #Object.parentinv is not consistent outside of parenting. */
+ {
+ float imat[3][3];
+ copy_m3_m4(imat, inst_ob->parentinv);
+ mul_m4_m3m4(obmat, imat, obmat);
+ }
- /* obmat is transform to face */
- get_dupliface_transform(
- mp, loopstart, mvert, fdd->use_scale, ctx->object->instance_faces_scale, obmat);
- /* make offset relative to inst_ob using relative child transform */
- mul_mat3_m4_v3(child_imat, obmat[3]);
-
- /* XXX ugly hack to ensure same behavior as in master
- * this should not be needed, parentinv is not consistent
- * outside of parenting.
- */
- {
- float imat[3][3];
- copy_m3_m4(imat, inst_ob->parentinv);
- mul_m4_m3m4(obmat, imat, obmat);
- }
+ /* Apply `obmat` _after_ the local face transform. */
+ mul_m4_m4m4(obmat, inst_ob->obmat, obmat);
+
+ /* Space matrix is constructed by removing `obmat` transform,
+ * this yields the world-space transform for recursive duplis. */
+ mul_m4_m4m4(space_mat, obmat, inst_ob->imat);
+
+ DupliObject *dob = make_dupli(ctx, inst_ob, obmat, index);
+
+ /* Recursion. */
+ make_recursive_duplis(ctx, inst_ob, space_mat, index);
+
+ return dob;
+}
+
+/** Wrap #face_dupli, needed since we can't #alloca in a loop. */
+static DupliObject *face_dupli_from_mesh(const DupliContext *ctx,
+ Object *inst_ob,
+ const float child_imat[4][4],
+ const int index,
+ const bool use_scale,
+ const float scale_fac,
+
+ /* Mesh variables. */
+ const MPoly *mpoly,
+ const MLoop *mloopstart,
+ const MVert *mvert)
+{
+ const int coords_len = mpoly->totloop;
+ float(*coords)[3] = BLI_array_alloca(coords, (size_t)coords_len);
+
+ const MLoop *ml = mloopstart;
+ for (int i = 0; i < coords_len; i++, ml++) {
+ copy_v3_v3(coords[i], mvert[ml->v].co);
+ }
+
+ return face_dupli(ctx, inst_ob, child_imat, index, use_scale, scale_fac, coords, coords_len);
+}
+
+/** Wrap #face_dupli, needed since we can't #alloca in a loop. */
+static DupliObject *face_dupli_from_editmesh(const DupliContext *ctx,
+ Object *inst_ob,
+ const float child_imat[4][4],
+ const int index,
+ const bool use_scale,
+ const float scale_fac,
+
+ /* Mesh variables. */
+ BMFace *f,
+ const float (*vert_coords)[3])
+{
+ const int coords_len = f->len;
+ float(*coords)[3] = BLI_array_alloca(coords, (size_t)coords_len);
+
+ BMLoop *l_first, *l_iter;
+ int i = 0;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ if (vert_coords != NULL) {
+ do {
+ copy_v3_v3(coords[i++], vert_coords[BM_elem_index_get(l_iter->v)]);
+ } while ((l_iter = l_iter->next) != l_first);
+ }
+ else {
+ do {
+ copy_v3_v3(coords[i++], l_iter->v->co);
+ } while ((l_iter = l_iter->next) != l_first);
+ }
- /* apply obmat _after_ the local face transform */
- mul_m4_m4m4(obmat, inst_ob->obmat, obmat);
+ return face_dupli(ctx, inst_ob, child_imat, index, use_scale, scale_fac, coords, coords_len);
+}
- /* space matrix is constructed by removing obmat transform,
- * this yields the worldspace transform for recursive duplis
- */
- mul_m4_m4m4(space_mat, obmat, inst_ob->imat);
+static void make_child_duplis_faces_from_mesh(const DupliContext *ctx,
+ void *userdata,
+ Object *inst_ob)
+{
+ FaceDupliData_Mesh *fdd = userdata;
+ const MPoly *mpoly = fdd->mpoly, *mp;
+ const MLoop *mloop = fdd->mloop;
+ const MVert *mvert = fdd->mvert;
+ const float(*orco)[3] = fdd->orco;
+ const MLoopUV *mloopuv = fdd->mloopuv;
+ const int totface = fdd->totface;
+ const bool use_scale = fdd->params.use_scale;
+ int a;
- dob = make_dupli(ctx, inst_ob, obmat, a);
+ float child_imat[4][4];
+
+ invert_m4_m4(inst_ob->imat, inst_ob->obmat);
+ /* Relative transform from parent to child space. */
+ mul_m4_m4m4(child_imat, inst_ob->imat, ctx->object->obmat);
+ const float scale_fac = ctx->object->instance_faces_scale;
+
+ for (a = 0, mp = mpoly; a < totface; a++, mp++) {
+ const MLoop *loopstart = mloop + mp->loopstart;
+ DupliObject *dob = face_dupli_from_mesh(
+ fdd->params.ctx, inst_ob, child_imat, a, use_scale, scale_fac, mp, loopstart, mvert);
const float w = 1.0f / (float)mp->totloop;
if (orco) {
@@ -776,51 +1013,90 @@ static void make_child_duplis_faces(const DupliContext *ctx, void *userdata, Obj
madd_v2_v2fl(dob->uv, mloopuv[mp->loopstart + j].uv, w);
}
}
-
- /* recursion */
- make_recursive_duplis(ctx, inst_ob, space_mat, a);
}
}
-static void make_duplis_faces(const DupliContext *ctx)
+static void make_child_duplis_faces_from_editmesh(const DupliContext *ctx,
+ void *userdata,
+ Object *inst_ob)
{
- Object *parent = ctx->object;
- FaceDupliData fdd;
+ FaceDupliData_EditMesh *fdd = userdata;
+ BMEditMesh *em = fdd->em;
+ float child_imat[4][4];
+ int a;
+ BMFace *f;
+ BMIter iter;
+ const bool use_scale = fdd->params.use_scale;
- fdd.use_scale = ((parent->transflag & OB_DUPLIFACES_SCALE) != 0);
+ const float(*vert_coords)[3] = fdd->vert_coords;
- /* gather mesh info */
- {
- BMEditMesh *em = BKE_editmesh_from_object(parent);
-
- /* We do not need any render-smecific handling anymore, depsgraph takes care of that. */
- /* NOTE: Do direct access to the evaluated mesh: this function is used
- * during meta balls evaluation. But even without those all the objects
- * which are needed for correct instancing are already evaluated. */
- if (em != NULL) {
- fdd.me_eval = em->mesh_eval_cage;
- }
- else {
- fdd.me_eval = BKE_object_get_evaluated_mesh(parent);
- }
+ BLI_assert((vert_coords == NULL) || (em->bm->elem_index_dirty & BM_VERT) == 0);
- if (fdd.me_eval == NULL) {
- return;
+ invert_m4_m4(inst_ob->imat, inst_ob->obmat);
+ /* Relative transform from parent to child space. */
+ mul_m4_m4m4(child_imat, inst_ob->imat, ctx->object->obmat);
+ const float scale_fac = ctx->object->instance_faces_scale;
+
+ BM_ITER_MESH_INDEX (f, &iter, em->bm, BM_FACES_OF_MESH, a) {
+ DupliObject *dob = face_dupli_from_editmesh(
+ fdd->params.ctx, inst_ob, child_imat, a, use_scale, scale_fac, f, vert_coords);
+
+ if (fdd->has_orco) {
+ const float w = 1.0f / (float)f->len;
+ BMLoop *l_first, *l_iter;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ madd_v3_v3fl(dob->orco, l_iter->v->co, w);
+ } while ((l_iter = l_iter->next) != l_first);
+ }
+ if (fdd->has_uvs) {
+ BM_face_uv_calc_center_median(f, fdd->cd_loop_uv_offset, dob->uv);
}
+ }
+}
- fdd.orco = CustomData_get_layer(&fdd.me_eval->vdata, CD_ORCO);
- const int uv_idx = CustomData_get_render_layer(&fdd.me_eval->ldata, CD_MLOOPUV);
- fdd.mloopuv = CustomData_get_layer_n(&fdd.me_eval->ldata, CD_MLOOPUV, uv_idx);
+static void make_duplis_faces(const DupliContext *ctx)
+{
+ Object *parent = ctx->object;
- fdd.totface = fdd.me_eval->totpoly;
- fdd.mpoly = fdd.me_eval->mpoly;
- fdd.mloop = fdd.me_eval->mloop;
- fdd.mvert = fdd.me_eval->mvert;
+ /* Gather mesh info. */
+ BMEditMesh *em = NULL;
+ const float(*vert_coords)[3] = NULL;
+ Mesh *me_eval = mesh_data_from_duplicator_object(parent, &em, &vert_coords, NULL);
+ if (em == NULL && me_eval == NULL) {
+ return;
}
- make_child_duplis(ctx, &fdd, make_child_duplis_faces);
-
- fdd.me_eval = NULL;
+ FaceDupliData_Params fdd_params = {
+ .ctx = ctx,
+ .use_scale = parent->transflag & OB_DUPLIFACES_SCALE,
+ };
+
+ if (em != NULL) {
+ const int uv_idx = CustomData_get_render_layer(&em->bm->ldata, CD_MLOOPUV);
+ FaceDupliData_EditMesh fdd = {
+ .params = fdd_params,
+ .em = em,
+ .vert_coords = vert_coords,
+ .has_orco = (vert_coords != NULL),
+ .has_uvs = (uv_idx != -1),
+ .cd_loop_uv_offset = CustomData_get_n_offset(&em->bm->ldata, CD_MLOOPUV, uv_idx),
+ };
+ make_child_duplis(ctx, &fdd, make_child_duplis_faces_from_editmesh);
+ }
+ else {
+ const int uv_idx = CustomData_get_render_layer(&me_eval->ldata, CD_MLOOPUV);
+ FaceDupliData_Mesh fdd = {
+ .params = fdd_params,
+ .totface = me_eval->totpoly,
+ .mpoly = me_eval->mpoly,
+ .mloop = me_eval->mloop,
+ .mvert = me_eval->mvert,
+ .mloopuv = CustomData_get_layer_n(&me_eval->ldata, CD_MLOOPUV, uv_idx),
+ .orco = CustomData_get_layer(&me_eval->vdata, CD_ORCO),
+ };
+ make_child_duplis(ctx, &fdd, make_child_duplis_faces_from_mesh);
+ }
}
static const DupliGenerator gen_dupli_faces = {
@@ -874,7 +1150,7 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem
no_draw_flag |= PARS_NO_DISP;
}
- /* NOTE: in old animsys, used parent object's timeoffset... */
+ /* NOTE: in old animation system, used parent object's time-offset. */
ctime = DEG_get_ctime(ctx->depsgraph);
totpart = psys->totpart;
@@ -888,16 +1164,16 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem
sim.ob = par;
sim.psys = psys;
sim.psmd = psys_get_modifier(par, psys);
- /* make sure emitter imat is in global coordinates instead of render view coordinates */
+ /* Make sure emitter `imat` is in global coordinates instead of render view coordinates. */
invert_m4_m4(par->imat, par->obmat);
- /* first check for loops (particle system object used as dupli object) */
+ /* First check for loops (particle system object used as dupli-object). */
if (part->ren_as == PART_DRAW_OB) {
if (ELEM(part->instance_object, NULL, par)) {
return;
}
}
- else { /*PART_DRAW_GR */
+ else { /* #PART_DRAW_GR. */
if (part->instance_collection == NULL) {
return;
}
@@ -913,7 +1189,7 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem
}
}
- /* if we have a hair particle system, use the path cache */
+ /* If we have a hair particle system, use the path cache. */
if (part->type == PART_HAIR) {
if (psys->flag & PSYS_HAIR_DONE) {
hair = (totchild == 0 || psys->childcache) && psys->pathcache;
@@ -922,7 +1198,7 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem
return;
}
- /* we use cache, update totchild according to cached data */
+ /* We use cache, update `totchild` according to cached data. */
totchild = psys->totchildcache;
totpart = psys->totcached;
}
@@ -931,7 +1207,7 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem
psys->lattice_deform_data = psys_create_lattice_deform_data(&sim);
- /* gather list of objects or single object */
+ /* Gather list of objects or single object. */
int totcollection = 0;
const bool use_whole_collection = part->draw & PART_DRAW_WHOLE_GR;
@@ -1000,23 +1276,27 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem
for (pa = psys->particles; a < totpart + totchild; a++, pa++) {
if (a < totpart) {
- /* handle parent particle */
+ /* Handle parent particle. */
if (pa->flag & no_draw_flag) {
continue;
}
- /* pa_num = pa->num; */ /* UNUSED */
+#if 0 /* UNUSED */
+ pa_num = pa->num;
+#endif
size = pa->size;
}
else {
- /* handle child particle */
+ /* Handle child particle. */
cpa = &psys->child[a - totpart];
- /* pa_num = a; */ /* UNUSED */
+#if 0 /* UNUSED */
+ pa_num = a;
+#endif
size = psys_get_child_size(psys, cpa, ctime, NULL);
}
- /* some hair paths might be non-existent so they can't be used for duplication */
+ /* Some hair paths might be non-existent so they can't be used for duplication. */
if (hair && psys->pathcache &&
((a < totpart && psys->pathcache[a]->segments < 0) ||
(a >= totpart && psys->childcache[a - totpart]->segments < 0))) {
@@ -1024,12 +1304,12 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem
}
if (part->ren_as == PART_DRAW_GR) {
- /* prevent divide by zero below [#28336] */
+ /* Prevent divide by zero below T28336. */
if (totcollection == 0) {
continue;
}
- /* for collections, pick the object based on settings */
+ /* For collections, pick the object based on settings. */
if (part->draw & PART_DRAW_RAND_GR && !use_whole_collection) {
b = BLI_rng_get_int(rng) % totcollection;
}
@@ -1041,7 +1321,7 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem
}
if (hair) {
- /* hair we handle separate and compute transform based on hair keys */
+ /* Hair we handle separate and compute transform based on hair keys. */
if (a < totpart) {
cache = psys->pathcache[a];
psys_get_dupli_path_transform(&sim, pa, NULL, cache, pamat, &scale);
@@ -1055,7 +1335,7 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem
pamat[3][3] = 1.0f;
}
else {
- /* first key */
+ /* First key. */
state.time = ctime;
if (psys_get_particle_state(&sim, a, &state, 0) == 0) {
continue;
@@ -1074,16 +1354,16 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem
part->instance_collection, object, mode) {
copy_m4_m4(tmat, oblist[b]->obmat);
- /* apply particle scale */
+ /* Apply particle scale. */
mul_mat3_m4_fl(tmat, size * scale);
mul_v3_fl(tmat[3], size * scale);
- /* collection dupli offset, should apply after everything else */
+ /* Collection dupli-offset, should apply after everything else. */
if (!is_zero_v3(part->instance_collection->instance_offset)) {
sub_v3_v3(tmat[3], part->instance_collection->instance_offset);
}
- /* individual particle transform */
+ /* Individual particle transform. */
mul_m4_m4m4(mat, pamat, tmat);
dob = make_dupli(ctx, object, mat, a);
@@ -1117,13 +1397,13 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem
quat_to_mat4(obmat, q);
obmat[3][3] = 1.0f;
- /* add scaling if requested */
+ /* Add scaling if requested. */
if ((part->draw & PART_DRAW_NO_SCALE_OB) == 0) {
mul_m4_m4m4(obmat, obmat, size_mat);
}
}
else if (part->draw & PART_DRAW_NO_SCALE_OB) {
- /* remove scaling */
+ /* Remove scaling. */
float size_mat[4][4], original_size[3];
mat4_to_size(original_size, obmat);
@@ -1151,7 +1431,7 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem
BLI_rng_free(rng);
}
- /* clean up */
+ /* Clean up. */
if (oblist) {
MEM_freeN(oblist);
}
@@ -1167,9 +1447,9 @@ static void make_duplis_particles(const DupliContext *ctx)
ParticleSystem *psys;
int psysid;
- /* particle system take up one level in id, the particles another */
+ /* Particle system take up one level in id, the particles another. */
for (psys = ctx->object->particlesystem.first, psysid = 0; psys; psys = psys->next, psysid++) {
- /* particles create one more level for persistent psys index */
+ /* Particles create one more level for persistent `psys` index. */
DupliContext pctx;
copy_dupli_context(&pctx, ctx, ctx->object, NULL, psysid);
make_duplis_particle_system(&pctx, psys);
@@ -1196,7 +1476,7 @@ static const DupliGenerator *get_dupli_generator(const DupliContext *ctx)
return NULL;
}
- /* Should the dupli's be generated for this object? - Respect restrict flags */
+ /* Should the dupli's be generated for this object? - Respect restrict flags. */
if (DEG_get_mode(ctx->depsgraph) == DAG_EVAL_RENDER ? (restrictflag & OB_RESTRICT_RENDER) :
(restrictflag & OB_RESTRICT_VIEWPORT)) {
return NULL;
diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c
index 19d5c34ad73..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};
@@ -1421,7 +1433,7 @@ MultiresModifierData *BKE_sculpt_multires_active(Scene *scene, Object *ob)
continue;
}
- if (mmd->sculptlvl > 0) {
+ if (mmd->sculptlvl > 0 && !(mmd->flags & eMultiresModifierFlag_UseSculptBaseMesh)) {
return mmd;
}
@@ -1437,10 +1449,9 @@ static bool sculpt_modifiers_active(Scene *scene, Sculpt *sd, Object *ob)
{
ModifierData *md;
Mesh *me = (Mesh *)ob->data;
- MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob);
VirtualModifierData virtualModifierData;
- if (mmd || ob->sculpt->bm) {
+ if (ob->sculpt->bm || BKE_sculpt_multires_active(scene, ob)) {
return false;
}
@@ -1458,7 +1469,10 @@ static bool sculpt_modifiers_active(Scene *scene, Sculpt *sd, Object *ob)
continue;
}
if (md->type == eModifierType_Multires && (ob->mode & OB_MODE_SCULPT)) {
- continue;
+ MultiresModifierData *mmd = (MultiresModifierData *)md;
+ if (!(mmd->flags & eMultiresModifierFlag_UseSculptBaseMesh)) {
+ continue;
+ }
}
if (md->type == eModifierType_ShapeKey) {
continue;
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/pointcache.c b/source/blender/blenkernel/intern/pointcache.c
index 64e642462af..8c5915d3768 100644
--- a/source/blender/blenkernel/intern/pointcache.c
+++ b/source/blender/blenkernel/intern/pointcache.c
@@ -4025,7 +4025,6 @@ static void ptcache_dt_to_str(char *str, double dtime)
/* if bake is not given run simulations to current frame */
void BKE_ptcache_bake(PTCacheBaker *baker)
{
- Main *bmain = baker->bmain;
Scene *scene = baker->scene;
ViewLayer *view_layer = baker->view_layer;
struct Depsgraph *depsgraph = baker->depsgraph;
@@ -4156,7 +4155,7 @@ void BKE_ptcache_bake(PTCacheBaker *baker)
stime = ptime = PIL_check_seconds_timer();
for (int fr = CFRA; fr <= endframe; fr += baker->quick_step, CFRA = fr) {
- BKE_scene_graph_update_for_newframe(depsgraph, bmain);
+ BKE_scene_graph_update_for_newframe(depsgraph);
if (baker->update_progress) {
float progress = ((float)(CFRA - startframe) / (float)(endframe - startframe));
@@ -4255,7 +4254,7 @@ void BKE_ptcache_bake(PTCacheBaker *baker)
CFRA = cfrao;
if (bake) { /* already on cfra unless baking */
- BKE_scene_graph_update_for_newframe(depsgraph, bmain);
+ BKE_scene_graph_update_for_newframe(depsgraph);
}
/* TODO: call redraw all windows somehow */
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 bdda03bab12..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";
@@ -1002,7 +1007,7 @@ Scene *BKE_scene_set_name(Main *bmain, const char *name)
return NULL;
}
-/* Used by metaballs, return *all* objects (including duplis)
+/* Used by meta-balls, return *all* objects (including duplis)
* existing in the scene (including scene's sets). */
int BKE_scene_base_iter_next(
Depsgraph *depsgraph, SceneBaseIter *iter, Scene **scene, int val, Base **base, Object **ob)
@@ -1073,7 +1078,7 @@ int BKE_scene_base_iter_next(
else {
if (iter->phase != F_DUPLI) {
if (depsgraph && (*base)->object->transflag & OB_DUPLI) {
- /* collections cannot be duplicated for metaballs yet,
+ /* Collections cannot be duplicated for meta-balls yet,
* this enters eternal loop because of
* makeDispListMBall getting called inside of collection_duplilist */
if ((*base)->object->instance_collection == NULL) {
@@ -1129,6 +1134,11 @@ int BKE_scene_base_iter_next(
return iter->phase;
}
+bool BKE_scene_has_view_layer(const Scene *scene, const ViewLayer *layer)
+{
+ return BLI_findindex(&scene->view_layers, layer) != -1;
+}
+
Scene *BKE_scene_find_from_collection(const Main *bmain, const Collection *collection)
{
for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) {
@@ -1485,7 +1495,7 @@ static void scene_graph_update_tagged(Depsgraph *depsgraph, Main *bmain, bool on
for (int pass = 0; pass < 2; pass++) {
/* (Re-)build dependency graph if needed. */
- DEG_graph_relations_update(depsgraph, bmain, scene, view_layer);
+ DEG_graph_relations_update(depsgraph);
/* Uncomment this to check if graph was properly tagged for update. */
// DEG_debug_graph_relations_validate(depsgraph, bmain, scene);
/* Flush editing data if needed. */
@@ -1493,7 +1503,7 @@ static void scene_graph_update_tagged(Depsgraph *depsgraph, Main *bmain, bool on
/* Update all objects: drivers, matrices, displists, etc. flags set
* by depgraph or manual, no layer check here, gets correct flushed.
*/
- DEG_evaluate_on_refresh(bmain, depsgraph);
+ DEG_evaluate_on_refresh(depsgraph);
/* Update sound system. */
BKE_scene_update_sound(depsgraph, bmain);
/* Notify python about depsgraph update. */
@@ -1512,7 +1522,7 @@ static void scene_graph_update_tagged(Depsgraph *depsgraph, Main *bmain, bool on
* be tagged for an update anyway.
*
* If there are no relations changed by the callback this call will do nothing. */
- DEG_graph_relations_update(depsgraph, bmain, scene, view_layer);
+ DEG_graph_relations_update(depsgraph);
}
/* Inform editors about possible changes. */
DEG_ids_check_recalc(bmain, depsgraph, scene, view_layer, false);
@@ -1541,10 +1551,11 @@ void BKE_scene_graph_evaluated_ensure(Depsgraph *depsgraph, Main *bmain)
}
/* applies changes right away, does all sets too */
-void BKE_scene_graph_update_for_newframe(Depsgraph *depsgraph, Main *bmain)
+void BKE_scene_graph_update_for_newframe(Depsgraph *depsgraph)
{
Scene *scene = DEG_get_input_scene(depsgraph);
ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph);
+ Main *bmain = DEG_get_bmain(depsgraph);
/* Keep this first. */
BKE_callback_exec_id(bmain, &scene->id, BKE_CB_EVT_FRAME_CHANGE_PRE);
@@ -1555,7 +1566,7 @@ void BKE_scene_graph_update_for_newframe(Depsgraph *depsgraph, Main *bmain)
*/
BKE_image_editors_update_frame(bmain, scene->r.cfra);
BKE_sound_set_cfra(scene->r.cfra);
- DEG_graph_relations_update(depsgraph, bmain, scene, view_layer);
+ DEG_graph_relations_update(depsgraph);
/* Update all objects: drivers, matrices, displists, etc. flags set
* by depgraph or manual, no layer check here, gets correct flushed.
*
@@ -1564,10 +1575,10 @@ void BKE_scene_graph_update_for_newframe(Depsgraph *depsgraph, Main *bmain)
* loose any possible unkeyed changes made by the handler. */
if (pass == 0) {
const float ctime = BKE_scene_frame_get(scene);
- DEG_evaluate_on_framechange(bmain, depsgraph, ctime);
+ DEG_evaluate_on_framechange(depsgraph, ctime);
}
else {
- DEG_evaluate_on_refresh(bmain, depsgraph);
+ DEG_evaluate_on_refresh(depsgraph);
}
/* Update sound system animation. */
BKE_scene_update_sound(depsgraph, bmain);
@@ -1578,7 +1589,7 @@ void BKE_scene_graph_update_for_newframe(Depsgraph *depsgraph, Main *bmain)
/* NOTE: Similar to this case in scene_graph_update_tagged(). Need to ensure that
* DEG_ids_clear_recalc() doesn't access freed memory of possibly removed ID. */
- DEG_graph_relations_update(depsgraph, bmain, scene, view_layer);
+ DEG_graph_relations_update(depsgraph);
}
/* Inform editors about possible changes. */
@@ -1603,7 +1614,7 @@ void BKE_scene_graph_update_for_newframe(Depsgraph *depsgraph, Main *bmain)
*/
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);
}
@@ -2237,14 +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_has_view_layer(scene, view_layer));
+
/* Make sure hash itself exists. */
if (allocate_ghash_entry) {
BKE_scene_ensure_depsgraph_hash(scene);
@@ -2252,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;
}
-Depsgraph *BKE_scene_get_depsgraph(Main *bmain, Scene *scene, ViewLayer *view_layer, bool allocate)
+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(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;
}
@@ -2353,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);
@@ -2538,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)
@@ -2555,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) {
@@ -2593,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/seqcache.c b/source/blender/blenkernel/intern/seqcache.c
index 3d1f33190dc..7d2858050be 100644
--- a/source/blender/blenkernel/intern/seqcache.c
+++ b/source/blender/blenkernel/intern/seqcache.c
@@ -1326,6 +1326,7 @@ void BKE_sequencer_cache_put(const SeqRenderData *context,
context = BKE_sequencer_prefetch_get_original_context(context);
scene = context->scene;
seq = BKE_sequencer_prefetch_get_original_sequence(seq, scene);
+ BLI_assert(seq != NULL);
}
/* Prevent reinserting, it breaks cache key linking. */
diff --git a/source/blender/blenkernel/intern/seqeffects.c b/source/blender/blenkernel/intern/seqeffects.c
index afec9b835a5..aba9b255f40 100644
--- a/source/blender/blenkernel/intern/seqeffects.c
+++ b/source/blender/blenkernel/intern/seqeffects.c
@@ -2387,6 +2387,8 @@ static void copy_transform_effect(Sequence *dst, Sequence *src, const int UNUSED
static void transform_image(int x,
int y,
+ int start_line,
+ int total_lines,
ImBuf *ibuf1,
ImBuf *out,
float scale_x,
@@ -2396,34 +2398,27 @@ static void transform_image(int x,
float rotate,
int interpolation)
{
- int xo, yo, xi, yi;
- float xt, yt, xr, yr;
- float s, c;
-
- xo = x;
- yo = y;
-
/* Rotate */
- s = sinf(rotate);
- c = cosf(rotate);
+ float s = sinf(rotate);
+ float c = cosf(rotate);
- for (yi = 0; yi < yo; yi++) {
- for (xi = 0; xi < xo; xi++) {
+ for (int yi = start_line; yi < start_line + total_lines; yi++) {
+ for (int xi = 0; xi < x; xi++) {
/* translate point */
- xt = xi - translate_x;
- yt = yi - translate_y;
+ float xt = xi - translate_x;
+ float yt = yi - translate_y;
/* rotate point with center ref */
- xr = c * xt + s * yt;
- yr = -s * xt + c * yt;
+ float xr = c * xt + s * yt;
+ float yr = -s * xt + c * yt;
/* scale point with center ref */
xt = xr / scale_x;
yt = yr / scale_y;
/* undo reference center point */
- xt += (xo / 2.0f);
- yt += (yo / 2.0f);
+ xt += (x / 2.0f);
+ yt += (y / 2.0f);
/* interpolate */
switch (interpolation) {
@@ -2441,9 +2436,19 @@ static void transform_image(int x,
}
}
-static void do_transform(
- Scene *scene, Sequence *seq, float UNUSED(facf0), int x, int y, ImBuf *ibuf1, ImBuf *out)
+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)
{
+ Scene *scene = context->scene;
TransformVars *transform = (TransformVars *)seq->effectdata;
float scale_x, scale_y, translate_x, translate_y, rotate_radians;
@@ -2456,6 +2461,9 @@ static void do_transform(
scale_y = transform->ScaleyIni;
}
+ int x = context->rectx;
+ int y = context->recty;
+
/* Translate */
if (!transform->percent) {
float rd_s = (scene->r.size / 100.0f);
@@ -2473,6 +2481,8 @@ static void do_transform(
transform_image(x,
y,
+ start_line,
+ total_lines,
ibuf1,
out,
scale_x,
@@ -2483,22 +2493,6 @@ static void do_transform(
transform->interpolation);
}
-static ImBuf *do_transform_effect(const SeqRenderData *context,
- Sequence *seq,
- float UNUSED(cfra),
- float facf0,
- float UNUSED(facf1),
- ImBuf *ibuf1,
- ImBuf *ibuf2,
- ImBuf *ibuf3)
-{
- ImBuf *out = prepare_effect_imbufs(context, ibuf1, ibuf2, ibuf3);
-
- do_transform(context->scene, seq, facf0, context->rectx, context->recty, ibuf1, out);
-
- return out;
-}
-
/*********************** Glow *************************/
static void RVBlurBitmap2_float(float *map, int width, int height, float blur, int quality)
@@ -4183,11 +4177,12 @@ static struct SeqEffectHandle get_sequence_effect_impl(int seq_type)
rval.execute = do_glow_effect;
break;
case SEQ_TYPE_TRANSFORM:
+ rval.multithreaded = true;
rval.init = init_transform_effect;
rval.num_inputs = num_inputs_transform;
rval.free = free_transform_effect;
rval.copy = copy_transform_effect;
- rval.execute = do_transform_effect;
+ rval.execute_slice = do_transform_effect;
break;
case SEQ_TYPE_SPEED:
rval.init = init_speed_effect;
diff --git a/source/blender/blenkernel/intern/seqprefetch.c b/source/blender/blenkernel/intern/seqprefetch.c
index 795086fffa4..3a7e4af490a 100644
--- a/source/blender/blenkernel/intern/seqprefetch.c
+++ b/source/blender/blenkernel/intern/seqprefetch.c
@@ -207,7 +207,7 @@ static void seq_prefetch_free_depsgraph(PrefetchJob *pfjob)
static void seq_prefetch_update_depsgraph(PrefetchJob *pfjob)
{
- DEG_evaluate_on_framechange(pfjob->bmain_eval, pfjob->depsgraph, seq_prefetch_cfra(pfjob));
+ DEG_evaluate_on_framechange(pfjob->depsgraph, seq_prefetch_cfra(pfjob));
}
static void seq_prefetch_init_depsgraph(PrefetchJob *pfjob)
@@ -220,7 +220,7 @@ static void seq_prefetch_init_depsgraph(PrefetchJob *pfjob)
DEG_debug_name_set(pfjob->depsgraph, "SEQUENCER PREFETCH");
/* Make sure there is a correct evaluated scene pointer. */
- DEG_graph_build_for_render_pipeline(pfjob->depsgraph, bmain, scene, view_layer);
+ DEG_graph_build_for_render_pipeline(pfjob->depsgraph);
/* Update immediately so we have proper evaluated scene. */
seq_prefetch_update_depsgraph(pfjob);
diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c
index a2a45ae56b3..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,8 +3595,8 @@ static ImBuf *seq_render_scene_strip(const SeqRenderData *context,
}
/* opengl offscreen render */
- depsgraph = BKE_scene_get_depsgraph(context->bmain, scene, view_layer, true);
- BKE_scene_graph_update_for_newframe(depsgraph, context->bmain);
+ 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) */
depsgraph,
@@ -3695,7 +3698,7 @@ finally:
scene->r.subframe = orig_data.subframe;
if (is_frame_update && (depsgraph != NULL)) {
- BKE_scene_graph_update_for_newframe(depsgraph, context->bmain);
+ BKE_scene_graph_update_for_newframe(depsgraph);
}
#ifdef DURIAN_CAMERA_SWITCH
@@ -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 1ab9766a7ec..4d3e8b55404 100644
--- a/source/blender/blenkernel/intern/softbody.c
+++ b/source/blender/blenkernel/intern/softbody.c
@@ -955,7 +955,7 @@ static void free_softbody_intern(SoftBody *sb)
* (only needs the current particle position)
*
* it actually checks if the particle intrudes a short range force field generated
- * by the faces of the target object and returns a force to drive the particel out
+ * by the faces of the target object and returns a force to drive the particle out
* the strength of the field grows exponentially if the particle is on the 'wrong' side of the face
* 'wrong' side : projection to the face normal is negative (all referred to a vertex in the face)
*
@@ -2965,6 +2965,9 @@ static void lattice_to_softbody(Scene *scene, Object *ob)
if (ob->softflag & OB_SB_EDGES) {
makelatticesprings(lt, ob->soft->bspring, ob->softflag & OB_SB_QUADS, ob);
build_bps_springlist(ob); /* link bps to springs */
+ if (ob->softflag & OB_SB_SELF) {
+ calculate_collision_balls(ob);
+ }
}
}
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/unit.c b/source/blender/blenkernel/intern/unit.c
index 8414f93ddaa..ce4ed05b10e 100644
--- a/source/blender/blenkernel/intern/unit.c
+++ b/source/blender/blenkernel/intern/unit.c
@@ -37,7 +37,7 @@
# include "BLI_winstuff.h"
#endif
-/* no BKE or DNA includes! */
+/* No BKE or DNA includes! */
/* Keep alignment. */
/* clang-format off */
@@ -66,7 +66,7 @@
#define UN_SC_IN 0.0254f
#define UN_SC_MIL 0.0000254f
-#define UN_SC_MTON 1000.0f /* metric ton */
+#define UN_SC_MTON 1000.0f /* Metric ton. */
#define UN_SC_QL 100.0f
#define UN_SC_KG 1.0f
#define UN_SC_HG 0.1f
@@ -74,7 +74,7 @@
#define UN_SC_G 0.001f
#define UN_SC_MG 0.000001f
-#define UN_SC_ITON 907.18474f /* imperial ton */
+#define UN_SC_ITON 907.18474f /* Imperial ton. */
#define UN_SC_CWT 45.359237f
#define UN_SC_ST 6.35029318f
#define UN_SC_LB 0.45359237f
@@ -82,20 +82,22 @@
/* clang-format on */
-/* define a single unit */
+/* Define a single unit. */
typedef struct bUnitDef {
const char *name;
- /** abused a bit for the display name */
+ /** Abused a bit for the display name. */
const char *name_plural;
- /** this is used for display*/
+ /** This is used for display. */
const char *name_short;
- /** keyboard-friendly ASCII-only version of name_short, can be NULL */
+ /**
+ * Keyboard-friendly ASCII-only version of name_short, can be NULL.
+ * If name_short has non-ASCII chars, name_alt should be present.
+ */
const char *name_alt;
- /* if name_short has non-ASCII chars, name_alt should be present */
- /** can be NULL */
+ /** Can be NULL. */
const char *name_display;
- /** when NULL, a transformed version of the name will be taken */
+ /** When NULL, a transformed version of the name will be taken in some cases. */
const char *identifier;
double scalar;
@@ -106,28 +108,28 @@ typedef struct bUnitDef {
enum {
B_UNIT_DEF_NONE = 0,
- /** Use for units that are not used enough to be translated into for common use */
+ /** Use for units that are not used enough to be translated into for common use. */
B_UNIT_DEF_SUPPRESS = 1,
- /** Display a unit even if its value is 0.1, eg 0.1mm instead of 100um */
+ /** Display a unit even if its value is 0.1, eg 0.1mm instead of 100um. */
B_UNIT_DEF_TENTH = 2,
- /** Short unit name is case sensitive, for example to distinguish mW and MW */
+ /** Short unit name is case sensitive, for example to distinguish mW and MW. */
B_UNIT_DEF_CASE_SENSITIVE = 4,
- /** Short unit name does not have space between it and preceding number */
+ /** Short unit name does not have space between it and preceding number. */
B_UNIT_DEF_NO_SPACE = 8,
};
-/* define a single unit */
+/* Define a single unit system. */
typedef struct bUnitCollection {
const struct bUnitDef *units;
- /** basic unit index (when user doesn't specify unit explicitly) */
+ /** Basic unit index (when user doesn't specify unit explicitly). */
int base_unit;
- /** options for this system */
+ /** Options for this system. */
int flag;
- /** to quickly find the last item */
+ /** To quickly find the last item. */
int length;
} bUnitCollection;
-/* Keep table lignment. */
+/* Keep table Alignment. */
/* clang-format off */
#define UNIT_COLLECTION_LENGTH(def) (ARRAY_SIZE(def) - 1)
@@ -142,14 +144,14 @@ static struct bUnitDef buMetricLenDef[] = {
{"kilometer", "kilometers", "km", NULL, "Kilometers", "KILOMETERS", UN_SC_KM, 0.0, B_UNIT_DEF_NONE},
{"hectometer", "hectometers", "hm", NULL, "100 Meters", "HECTOMETERS", UN_SC_HM, 0.0, B_UNIT_DEF_SUPPRESS},
{"dekameter", "dekameters", "dam", NULL, "10 Meters", "DEKAMETERS", UN_SC_DAM, 0.0, B_UNIT_DEF_SUPPRESS},
- {"meter", "meters", "m", NULL, "Meters", "METERS", UN_SC_M, 0.0, B_UNIT_DEF_NONE}, /* base unit */
+ {"meter", "meters", "m", NULL, "Meters", "METERS", UN_SC_M, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */
{"decimeter", "decimeters", "dm", NULL, "10 Centimeters", "DECIMETERS", UN_SC_DM, 0.0, B_UNIT_DEF_SUPPRESS},
{"centimeter", "centimeters", "cm", NULL, "Centimeters", "CENTIMETERS", UN_SC_CM, 0.0, B_UNIT_DEF_NONE},
{"millimeter", "millimeters", "mm", NULL, "Millimeters", "MILLIMETERS", UN_SC_MM, 0.0, B_UNIT_DEF_NONE | B_UNIT_DEF_TENTH},
{"micrometer", "micrometers", "µm", "um", "Micrometers", "MICROMETERS", UN_SC_UM, 0.0, B_UNIT_DEF_NONE},
/* These get displayed because of float precision problems in the transform header,
- * could work around, but for now probably people wont use these */
+ * could work around, but for now probably people wont use these. */
#if 0
{"nanometer", "Nanometers", "nm", NULL, 0.000000001, 0.0, B_UNIT_DEF_NONE},
{"picometer", "Picometers", "pm", NULL, 0.000000000001, 0.0, B_UNIT_DEF_NONE},
@@ -163,19 +165,19 @@ static struct bUnitDef buImperialLenDef[] = {
{"furlong", "furlongs", "fur", NULL, "Furlongs", "FURLONGS", UN_SC_FUR, 0.0, B_UNIT_DEF_SUPPRESS},
{"chain", "chains", "ch", NULL, "Chains", "CHAINS", UN_SC_CH, 0.0, B_UNIT_DEF_SUPPRESS},
{"yard", "yards", "yd", NULL, "Yards", "YARDS", UN_SC_YD, 0.0, B_UNIT_DEF_SUPPRESS},
- {"foot", "feet", "'", "ft", "Feet", "FEET", UN_SC_FT, 0.0, B_UNIT_DEF_NONE | B_UNIT_DEF_NO_SPACE}, /* base unit */
+ {"foot", "feet", "'", "ft", "Feet", "FEET", UN_SC_FT, 0.0, B_UNIT_DEF_NONE | B_UNIT_DEF_NO_SPACE}, /* Base unit. */
{"inch", "inches", "\"", "in", "Inches", "INCHES", UN_SC_IN, 0.0, B_UNIT_DEF_NONE | B_UNIT_DEF_NO_SPACE},
- {"thou", "thou", "thou", "mil", "Thou", "THOU", UN_SC_MIL, 0.0, B_UNIT_DEF_NONE}, /* plural for thou has no 's' */
+ {"thou", "thou", "thou", "mil", "Thou", "THOU", UN_SC_MIL, 0.0, B_UNIT_DEF_NONE}, /* Plural for "thou" has no 's'. */
NULL_UNIT,
};
static struct bUnitCollection buImperialLenCollection = {buImperialLenDef, 4, 0, UNIT_COLLECTION_LENGTH(buImperialLenDef)};
-/* Areas */
+/* Areas. */
static struct bUnitDef buMetricAreaDef[] = {
{"square kilometer", "square kilometers", "km²", "km2", "Square Kilometers", NULL, UN_SC_KM * UN_SC_KM, 0.0, B_UNIT_DEF_NONE},
- {"square hectometer", "square hectometers", "hm²", "hm2", "Square Hectometers", NULL, UN_SC_HM * UN_SC_HM, 0.0, B_UNIT_DEF_SUPPRESS}, /* hectare */
+ {"square hectometer", "square hectometers", "hm²", "hm2", "Square Hectometers", NULL, UN_SC_HM * UN_SC_HM, 0.0, B_UNIT_DEF_SUPPRESS}, /* Hectare. */
{"square dekameter", "square dekameters", "dam²", "dam2", "Square Dekameters", NULL, UN_SC_DAM * UN_SC_DAM, 0.0, B_UNIT_DEF_SUPPRESS}, /* are */
- {"square meter", "square meters", "m²", "m2", "Square Meters", NULL, UN_SC_M * UN_SC_M, 0.0, B_UNIT_DEF_NONE}, /* base unit */
+ {"square meter", "square meters", "m²", "m2", "Square Meters", NULL, UN_SC_M * UN_SC_M, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */
{"square decimeter", "square decimetees", "dm²", "dm2", "Square Decimeters", NULL, UN_SC_DM * UN_SC_DM, 0.0, B_UNIT_DEF_SUPPRESS},
{"square centimeter", "square centimeters", "cm²", "cm2", "Square Centimeters", NULL, UN_SC_CM * UN_SC_CM, 0.0, B_UNIT_DEF_NONE},
{"square millimeter", "square millimeters", "mm²", "mm2", "Square Millimeters", NULL, UN_SC_MM * UN_SC_MM, 0.0, B_UNIT_DEF_NONE | B_UNIT_DEF_TENTH},
@@ -189,19 +191,19 @@ static struct bUnitDef buImperialAreaDef[] = {
{"square furlong", "square furlongs", "sq fur", NULL, "Square Furlongs", NULL, UN_SC_FUR * UN_SC_FUR, 0.0, B_UNIT_DEF_SUPPRESS},
{"square chain", "square chains", "sq ch", NULL, "Square Chains", NULL, UN_SC_CH * UN_SC_CH, 0.0, B_UNIT_DEF_SUPPRESS},
{"square yard", "square yards", "sq yd", NULL, "Square Yards", NULL, UN_SC_YD * UN_SC_YD, 0.0, B_UNIT_DEF_NONE},
- {"square foot", "square feet", "sq ft", NULL, "Square Feet", NULL, UN_SC_FT * UN_SC_FT, 0.0, B_UNIT_DEF_NONE}, /* base unit */
+ {"square foot", "square feet", "sq ft", NULL, "Square Feet", NULL, UN_SC_FT * UN_SC_FT, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */
{"square inch", "square inches", "sq in", NULL, "Square Inches", NULL, UN_SC_IN * UN_SC_IN, 0.0, B_UNIT_DEF_NONE},
{"square thou", "square thou", "sq mil", NULL, "Square Thou", NULL, UN_SC_MIL * UN_SC_MIL, 0.0, B_UNIT_DEF_NONE},
NULL_UNIT,
};
static struct bUnitCollection buImperialAreaCollection = {buImperialAreaDef, 4, 0, UNIT_COLLECTION_LENGTH(buImperialAreaDef)};
-/* Volumes */
+/* Volumes. */
static struct bUnitDef buMetricVolDef[] = {
{"cubic kilometer", "cubic kilometers", "km³", "km3", "Cubic Kilometers", NULL, UN_SC_KM * UN_SC_KM * UN_SC_KM, 0.0, B_UNIT_DEF_NONE},
{"cubic hectometer", "cubic hectometers", "hm³", "hm3", "Cubic Hectometers", NULL, UN_SC_HM * UN_SC_HM * UN_SC_HM, 0.0, B_UNIT_DEF_SUPPRESS},
{"cubic dekameter", "cubic dekameters", "dam³", "dam3", "Cubic Dekameters", NULL, UN_SC_DAM * UN_SC_DAM * UN_SC_DAM, 0.0, B_UNIT_DEF_SUPPRESS},
- {"cubic meter", "cubic meters", "m³", "m3", "Cubic Meters", NULL, UN_SC_M * UN_SC_M * UN_SC_M, 0.0, B_UNIT_DEF_NONE}, /* base unit */
+ {"cubic meter", "cubic meters", "m³", "m3", "Cubic Meters", NULL, UN_SC_M * UN_SC_M * UN_SC_M, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */
{"cubic decimeter", "cubic decimeters", "dm³", "dm3", "Cubic Decimeters", NULL, UN_SC_DM * UN_SC_DM * UN_SC_DM, 0.0, B_UNIT_DEF_SUPPRESS},
{"cubic centimeter", "cubic centimeters", "cm³", "cm3", "Cubic Centimeters", NULL, UN_SC_CM * UN_SC_CM * UN_SC_CM, 0.0, B_UNIT_DEF_NONE},
{"cubic millimeter", "cubic millimeters", "mm³", "mm3", "Cubic Millimeters", NULL, UN_SC_MM * UN_SC_MM * UN_SC_MM, 0.0, B_UNIT_DEF_NONE | B_UNIT_DEF_TENTH},
@@ -215,18 +217,18 @@ static struct bUnitDef buImperialVolDef[] = {
{"cubic furlong", "cubic furlongs", "cu fur", NULL, "Cubic Furlongs", NULL, UN_SC_FUR * UN_SC_FUR * UN_SC_FUR, 0.0, B_UNIT_DEF_SUPPRESS},
{"cubic chain", "cubic chains", "cu ch", NULL, "Cubic Chains", NULL, UN_SC_CH * UN_SC_CH * UN_SC_CH, 0.0, B_UNIT_DEF_SUPPRESS},
{"cubic yard", "cubic yards", "cu yd", NULL, "Cubic Yards", NULL, UN_SC_YD * UN_SC_YD * UN_SC_YD, 0.0, B_UNIT_DEF_NONE},
- {"cubic foot", "cubic feet", "cu ft", NULL, "Cubic Feet", NULL, UN_SC_FT * UN_SC_FT * UN_SC_FT, 0.0, B_UNIT_DEF_NONE}, /* base unit */
+ {"cubic foot", "cubic feet", "cu ft", NULL, "Cubic Feet", NULL, UN_SC_FT * UN_SC_FT * UN_SC_FT, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */
{"cubic inch", "cubic inches", "cu in", NULL, "Cubic Inches", NULL, UN_SC_IN * UN_SC_IN * UN_SC_IN, 0.0, B_UNIT_DEF_NONE},
{"cubic thou", "cubic thou", "cu mil", NULL, "Cubic Thou", NULL, UN_SC_MIL * UN_SC_MIL * UN_SC_MIL, 0.0, B_UNIT_DEF_NONE},
NULL_UNIT,
};
static struct bUnitCollection buImperialVolCollection = {buImperialVolDef, 4, 0, UNIT_COLLECTION_LENGTH(buImperialVolDef)};
-/* Mass */
+/* Mass. */
static struct bUnitDef buMetricMassDef[] = {
{"ton", "tonnes", "ton", "t", "Tonnes", "TONNES", UN_SC_MTON, 0.0, B_UNIT_DEF_NONE},
{"quintal", "quintals", "ql", "q", "100 Kilograms", "QUINTALS", UN_SC_QL, 0.0, B_UNIT_DEF_SUPPRESS},
- {"kilogram", "kilograms", "kg", NULL, "Kilograms", "KILOGRAMS", UN_SC_KG, 0.0, B_UNIT_DEF_NONE}, /* base unit */
+ {"kilogram", "kilograms", "kg", NULL, "Kilograms", "KILOGRAMS", UN_SC_KG, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */
{"hectogram", "hectograms", "hg", NULL, "Hectograms", "HECTOGRAMS", UN_SC_HG, 0.0, B_UNIT_DEF_SUPPRESS},
{"dekagram", "dekagrams", "dag", NULL, "10 Grams", "DEKAGRAMS", UN_SC_DAG, 0.0, B_UNIT_DEF_SUPPRESS},
{"gram", "grams", "g", NULL, "Grams", "GRAMS", UN_SC_G, 0.0, B_UNIT_DEF_NONE},
@@ -239,50 +241,50 @@ static struct bUnitDef buImperialMassDef[] = {
{"ton", "tonnes", "ton", "t", "Tonnes", "TONNES", UN_SC_ITON, 0.0, B_UNIT_DEF_NONE},
{"centum weight", "centum weights", "cwt", NULL, "Centum weights", "CENTUM_WEIGHTS", UN_SC_CWT, 0.0, B_UNIT_DEF_NONE},
{"stone", "stones", "st", NULL, "Stones", "STONES", UN_SC_ST, 0.0, B_UNIT_DEF_NONE},
- {"pound", "pounds", "lb", NULL, "Pounds", "POUNDS", UN_SC_LB, 0.0, B_UNIT_DEF_NONE}, /* base unit */
+ {"pound", "pounds", "lb", NULL, "Pounds", "POUNDS", UN_SC_LB, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */
{"ounce", "ounces", "oz", NULL, "Ounces", "OUNCES", UN_SC_OZ, 0.0, B_UNIT_DEF_NONE},
NULL_UNIT,
};
static struct bUnitCollection buImperialMassCollection = {buImperialMassDef, 3, 0, UNIT_COLLECTION_LENGTH(buImperialMassDef)};
/* Even if user scales the system to a point where km^3 is used, velocity and
- * acceleration aren't scaled: that's why we have so few units for them */
+ * acceleration aren't scaled: that's why we have so few units for them. */
-/* Velocity */
+/* Velocity. */
static struct bUnitDef buMetricVelDef[] = {
- {"meter per second", "meters per second", "m/s", NULL, "Meters per second", NULL, UN_SC_M, 0.0, B_UNIT_DEF_NONE}, /* base unit */
+ {"meter per second", "meters per second", "m/s", NULL, "Meters per second", NULL, UN_SC_M, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */
{"kilometer per hour", "kilometers per hour", "km/h", NULL, "Kilometers per hour", NULL, UN_SC_KM / 3600.0f, 0.0, B_UNIT_DEF_SUPPRESS},
NULL_UNIT,
};
static struct bUnitCollection buMetricVelCollection = {buMetricVelDef, 0, 0, UNIT_COLLECTION_LENGTH(buMetricVelDef)};
static struct bUnitDef buImperialVelDef[] = {
- {"foot per second", "feet per second", "ft/s", "fps", "Feet per second", NULL, UN_SC_FT, 0.0, B_UNIT_DEF_NONE}, /* base unit */
+ {"foot per second", "feet per second", "ft/s", "fps", "Feet per second", NULL, UN_SC_FT, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */
{"mile per hour", "miles per hour", "mph", NULL, "Miles per hour", NULL, UN_SC_MI / 3600.0f, 0.0, B_UNIT_DEF_SUPPRESS},
NULL_UNIT,
};
static struct bUnitCollection buImperialVelCollection = {buImperialVelDef, 0, 0, UNIT_COLLECTION_LENGTH(buImperialVelDef)};
-/* Acceleration */
+/* Acceleration. */
static struct bUnitDef buMetricAclDef[] = {
- {"meter per second squared", "meters per second squared", "m/s²", "m/s2", "Meters per second squared", NULL, UN_SC_M, 0.0, B_UNIT_DEF_NONE}, /* base unit */
+ {"meter per second squared", "meters per second squared", "m/s²", "m/s2", "Meters per second squared", NULL, UN_SC_M, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */
NULL_UNIT,
};
static struct bUnitCollection buMetricAclCollection = {buMetricAclDef, 0, 0, UNIT_COLLECTION_LENGTH(buMetricAclDef)};
static struct bUnitDef buImperialAclDef[] = {
- {"foot per second squared", "feet per second squared", "ft/s²", "ft/s2", "Feet per second squared", NULL, UN_SC_FT, 0.0, B_UNIT_DEF_NONE}, /* base unit */
+ {"foot per second squared", "feet per second squared", "ft/s²", "ft/s2", "Feet per second squared", NULL, UN_SC_FT, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */
NULL_UNIT,
};
static struct bUnitCollection buImperialAclCollection = {buImperialAclDef, 0, 0, UNIT_COLLECTION_LENGTH(buImperialAclDef)};
-/* Time */
+/* Time. */
static struct bUnitDef buNaturalTimeDef[] = {
- /* weeks? - probably not needed for blender */
+ /* Weeks? - probably not needed for Blender. */
{"day", "days", "d", NULL, "Days", "DAYS", 90000.0, 0.0, B_UNIT_DEF_NONE},
{"hour", "hours", "hr", "h", "Hours", "HOURS", 3600.0, 0.0, B_UNIT_DEF_NONE},
{"minute", "minutes", "min", "m", "Minutes", "MINUTES", 60.0, 0.0, B_UNIT_DEF_NONE},
- {"second", "seconds", "sec", "s", "Seconds", "SECONDS", 1.0, 0.0, B_UNIT_DEF_NONE}, /* base unit */
+ {"second", "seconds", "sec", "s", "Seconds", "SECONDS", 1.0, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */
{"millisecond", "milliseconds", "ms", NULL, "Milliseconds", "MILLISECONDS", 0.001, 0.0, B_UNIT_DEF_NONE},
{"microsecond", "microseconds", "µs", "us", "Microseconds", "MICROSECONDS", 0.000001, 0.0, B_UNIT_DEF_NONE},
NULL_UNIT,
@@ -296,28 +298,30 @@ static struct bUnitDef buNaturalRotDef[] = {
{"arcminute", "arcminutes", "'", NULL, "Arcminutes", "ARCMINUTES", (M_PI / 180.0) / 60.0, 0.0, B_UNIT_DEF_SUPPRESS | B_UNIT_DEF_NO_SPACE},
{"arcsecond", "arcseconds", "\"", NULL, "Arcseconds", "ARCSECONDS", (M_PI / 180.0) / 3600.0, 0.0, B_UNIT_DEF_SUPPRESS | B_UNIT_DEF_NO_SPACE},
{"radian", "radians", "r", NULL, "Radians", "RADIANS", 1.0, 0.0, B_UNIT_DEF_NONE},
-// {"turn", "turns", "t", NULL, "Turns", NULL, 1.0 / (M_PI * 2.0), 0.0, B_UNIT_DEF_NONE},
+#if 0
+ {"turn", "turns", "t", NULL, "Turns", NULL, 1.0 / (M_PI * 2.0), 0.0, B_UNIT_DEF_NONE},
+#endif
NULL_UNIT,
};
static struct bUnitCollection buNaturalRotCollection = {buNaturalRotDef, 0, 0, UNIT_COLLECTION_LENGTH(buNaturalRotDef)};
-/* Camera Lengths */
+/* Camera Lengths. */
static struct bUnitDef buCameraLenDef[] = {
- {"meter", "meters", "m", NULL, "Meters", NULL, UN_SC_KM, 0.0, B_UNIT_DEF_NONE}, /* base unit */
+ {"meter", "meters", "m", NULL, "Meters", NULL, UN_SC_KM, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */
{"decimeter", "decimeters", "dm", NULL, "10 Centimeters", NULL, UN_SC_HM, 0.0, B_UNIT_DEF_SUPPRESS},
{"centimeter", "centimeters", "cm", NULL, "Centimeters", NULL, UN_SC_DAM, 0.0, B_UNIT_DEF_SUPPRESS},
{"millimeter", "millimeters", "mm", NULL, "Millimeters", NULL, UN_SC_M, 0.0, B_UNIT_DEF_NONE},
- {"micrometer", "micrometers", "µm", "um", "Micrometers", NULL, UN_SC_MM, 0.0, B_UNIT_DEF_SUPPRESS},
+ {"micrometer", "micrometers", "µm", "um", "Micrometers", NULL, UN_SC_MM, 0.0, B_UNIT_DEF_SUPPRESS},
NULL_UNIT,
};
static struct bUnitCollection buCameraLenCollection = {buCameraLenDef, 3, 0, UNIT_COLLECTION_LENGTH(buCameraLenDef)};
-/* (Light) Power */
+/* (Light) Power. */
static struct bUnitDef buPowerDef[] = {
{"gigawatt", "gigawatts", "GW", NULL, "Gigawatts", NULL, 1e9f, 0.0, B_UNIT_DEF_NONE},
{"megawatt", "megawatts", "MW", NULL, "Megawatts", NULL, 1e6f, 0.0, B_UNIT_DEF_CASE_SENSITIVE},
{"kilowatt", "kilowatts", "kW", NULL, "Kilowatts", NULL, 1e3f, 0.0, B_UNIT_DEF_SUPPRESS},
- {"watt", "watts", "W", NULL, "Watts", NULL, 1.0f, 0.0, B_UNIT_DEF_NONE}, /* base unit */
+ {"watt", "watts", "W", NULL, "Watts", NULL, 1.0f, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */
{"milliwatt", "milliwatts", "mW", NULL, "Milliwatts", NULL, 1e-3f, 0.0, B_UNIT_DEF_CASE_SENSITIVE},
{"microwatt", "microwatts", "µW", "uW", "Microwatts", NULL, 1e-6f, 0.0, B_UNIT_DEF_NONE},
{"nanowatt", "nanowatts", "nW", NULL, "Nanowatts", NULL, 1e-9f, 0.0, B_UNIT_DEF_NONE},
@@ -325,17 +329,49 @@ static struct bUnitDef buPowerDef[] = {
};
static struct bUnitCollection buPowerCollection = {buPowerDef, 3, 0, UNIT_COLLECTION_LENGTH(buPowerDef)};
+/* clang-format on */
#define UNIT_SYSTEM_TOT (((sizeof(bUnitSystems) / B_UNIT_TYPE_TOT) / sizeof(void *)) - 1)
static const struct bUnitCollection *bUnitSystems[][B_UNIT_TYPE_TOT] = {
- {NULL, NULL, NULL, NULL, NULL, &buNaturalRotCollection, &buNaturalTimeCollection, NULL, NULL, NULL, NULL},
- {NULL, &buMetricLenCollection, &buMetricAreaCollection, &buMetricVolCollection, &buMetricMassCollection, &buNaturalRotCollection, &buNaturalTimeCollection, &buMetricVelCollection, &buMetricAclCollection, &buCameraLenCollection, &buPowerCollection}, /* metric */
- {NULL, &buImperialLenCollection, &buImperialAreaCollection, &buImperialVolCollection, &buImperialMassCollection, &buNaturalRotCollection, &buNaturalTimeCollection, &buImperialVelCollection, &buImperialAclCollection, &buCameraLenCollection, &buPowerCollection}, /* imperial */
- {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+ /* Natural. */
+ {NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &buNaturalRotCollection,
+ &buNaturalTimeCollection,
+ NULL,
+ NULL,
+ NULL,
+ NULL},
+ /* Metric. */
+ {NULL,
+ &buMetricLenCollection,
+ &buMetricAreaCollection,
+ &buMetricVolCollection,
+ &buMetricMassCollection,
+ &buNaturalRotCollection,
+ &buNaturalTimeCollection,
+ &buMetricVelCollection,
+ &buMetricAclCollection,
+ &buCameraLenCollection,
+ &buPowerCollection},
+ /* Imperial. */
+ {NULL,
+ &buImperialLenCollection,
+ &buImperialAreaCollection,
+ &buImperialVolCollection,
+ &buImperialMassCollection,
+ &buNaturalRotCollection,
+ &buNaturalTimeCollection,
+ &buImperialVelCollection,
+ &buImperialAclCollection,
+ &buCameraLenCollection,
+ &buPowerCollection},
+ {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
};
-/* clang-format on */
-
/* internal, has some option not exposed */
static const bUnitCollection *unit_get_system(int system, int type)
{
@@ -362,7 +398,7 @@ static const bUnitDef *unit_best_fit(double value,
continue;
}
- /* scale down scalar so 1cm doesn't convert to 10mm because of float error */
+ /* Scale down scalar so 1cm doesn't convert to 10mm because of float error. */
if (UNLIKELY(unit->flag & B_UNIT_DEF_TENTH)) {
if (value_abs >= unit->scalar * (0.1 - EPS)) {
return unit;
@@ -378,7 +414,7 @@ static const bUnitDef *unit_best_fit(double value,
return unit_default(usys);
}
-/* convert into 2 units and 2 values for "2ft, 3inch" syntax */
+/* Convert into 2 units and 2 values for "2ft, 3inch" syntax. */
static void unit_dual_convert(double value,
const bUnitCollection *usys,
bUnitDef const **r_unit_a,
@@ -407,7 +443,7 @@ static size_t unit_as_string(char *str,
double value,
int prec,
const bUnitCollection *usys,
- /* non exposed options */
+ /* Non exposed options. */
const bUnitDef *unit,
char pad)
{
@@ -415,10 +451,10 @@ static size_t unit_as_string(char *str,
size_t len, i;
if (unit) {
- /* use unit without finding the best one */
+ /* Use unit without finding the best one. */
}
else if (value == 0.0) {
- /* use the default units since there is no way to convert */
+ /* Use the default units since there is no way to convert. */
unit = unit_default(usys);
}
else {
@@ -428,19 +464,19 @@ static size_t unit_as_string(char *str,
value_conv = value / unit->scalar;
/* Adjust precision to expected number of significant digits.
- * Note that here, we shall not have to worry about very big/small numbers, units are expected to
- * replace 'scientific notation' in those cases. */
+ * Note that here, we shall not have to worry about very big/small numbers, units are expected
+ * to replace 'scientific notation' in those cases. */
prec -= integer_digits_d(value_conv);
CLAMP(prec, 0, 6);
- /* Convert to a string */
+ /* Convert to a string. */
len = BLI_snprintf_rlen(str, len_max, "%.*f", prec, value_conv);
- /* Add unit prefix and strip zeros */
+ /* Add unit prefix and strip zeros. */
- /* replace trailing zero's with spaces
- * so the number is less complicated but alignment in a button wont
- * jump about while dragging */
+ /* Replace trailing zero's with spaces so the number
+ * is less complicated but alignment in a button won't
+ * jump about while dragging. */
i = len - 1;
if (prec > 0) {
@@ -453,12 +489,12 @@ static size_t unit_as_string(char *str,
}
}
- /* Now add a space for all units except foot, inch, degree, arcminute, arcsecond */
+ /* Now add a space for all units except foot, inch, degree, arcminute, arcsecond. */
if (!(unit->flag & B_UNIT_DEF_NO_SPACE)) {
str[++i] = ' ';
}
- /* Now add the suffix */
+ /* Now add the suffix. */
if (i < len_max) {
int j = 0;
i++;
@@ -467,7 +503,7 @@ static size_t unit_as_string(char *str,
}
}
- /* terminate no matter what's done with padding above */
+ /* Terminate no matter what's done with padding above. */
if (i >= len_max) {
i = len_max - 1;
}
@@ -484,7 +520,7 @@ static bool unit_should_be_split(int type)
typedef struct {
int system;
int rotation;
- /* USER_UNIT_ADAPTIVE means none, otherwise the value is the index in the collection */
+ /* USER_UNIT_ADAPTIVE means none, otherwise the value is the index in the collection. */
int length;
int mass;
int time;
@@ -513,7 +549,7 @@ static size_t unit_as_string_split_pair(char *str,
unit_dual_convert(value, usys, &unit_a, &unit_b, &value_a, &value_b, main_unit);
- /* check the 2 is a smaller unit */
+ /* Check the 2 is a smaller unit. */
if (unit_b > unit_a) {
size_t i;
i = unit_as_string(str, len_max, value_a, prec, usys, unit_a, '\0');
@@ -522,11 +558,11 @@ static size_t unit_as_string_split_pair(char *str,
integer_digits_d(value_b / unit_b->scalar);
prec = max_ii(prec, 0);
- /* is there enough space for at least 1 char of the next unit? */
+ /* Is there enough space for at least 1 char of the next unit? */
if (i + 2 < len_max) {
str[i++] = ' ';
- /* use low precision since this is a smaller unit */
+ /* Use low precision since this is a smaller unit. */
i += unit_as_string(str + i, len_max - i, value_b, prec, usys, unit_b, '\0');
}
return i;
@@ -603,7 +639,7 @@ static size_t unit_as_string_main(char *str,
if (split && unit_should_be_split(type)) {
int length = unit_as_string_split_pair(str, len_max, value, prec, usys, main_unit);
- /* failed when length is negative, fallback to no split */
+ /* Failed when length is negative, fallback to no split. */
if (length >= 0) {
return length;
}
@@ -658,10 +694,10 @@ static const char *unit_find_str(const char *str, const char *substr, bool case_
if (str_found) {
/* Previous char cannot be a letter. */
if (str_found == str ||
- /* weak unicode support!, so "µm" won't match up be replaced by "m"
+ /* Weak unicode support!, so "µm" won't match up be replaced by "m"
* since non ascii utf8 values will NEVER return true */
isalpha_or_utf8(*BLI_str_prev_char_utf8(str_found)) == 0) {
- /* next char cannot be alphanum */
+ /* Next char cannot be alphanum. */
int len_name = strlen(substr);
if (!isalpha_or_utf8(*(str_found + len_name))) {
@@ -670,7 +706,7 @@ static const char *unit_find_str(const char *str, const char *substr, bool case_
}
/* If str_found is not a valid unit, we have to check further in the string... */
for (str_found++; isalpha_or_utf8(*str_found); str_found++) {
- /* pass */
+ /* Pass. */
}
str = str_found;
}
@@ -682,15 +718,15 @@ static const char *unit_find_str(const char *str, const char *substr, bool case_
return NULL;
}
-/* Note that numbers are added within brackets
- * ") " - is used to detect numbers we added so we can detect if commas need to be added
+/* Note that numbers are added within brackets.
+ * ") " - is used to detect numbers we added so we can detect if commas need to be added.
*
- * "1m1cm+2mm" - Original value
- * "1*1#1*0.01#+2*0.001#" - Replace numbers
- * "1*1+1*0.01 +2*0.001 " - Add add signs if ( + - * / | & ~ < > ^ ! = % ) not found in between
+ * "1m1cm+2mm" - Original value.
+ * "1*1#1*0.01#+2*0.001#" - Replace numbers.
+ * "1*1+1*0.01 +2*0.001 " - Add add signs if ( + - * / | & ~ < > ^ ! = % ) not found in between.
*/
-/* not too strict, (+ - * /) are most common */
+/* Not too strict, (+ - * /) are most common. */
static bool ch_is_op(char op)
{
switch (op) {
@@ -757,7 +793,6 @@ static char *find_next_op(const char *str, char *remaining_str, int len_max)
if (ch_is_op(remaining_str[i])) {
if (scientific_notation) {
scientific_notation = false;
- continue;
}
/* Make sure we don't look backwards before the start of the string. */
@@ -831,7 +866,7 @@ static int unit_scale_str(char *str,
char *str_found;
if ((len_max > 0) && (str_found = (char *)unit_find_str(str, replace_str, case_sensitive))) {
- /* XXX - investigate, does not respect len_max properly */
+ /* XXX - investigate, does not respect len_max properly. */
int len, len_num, len_name, len_move, found_ofs;
@@ -840,9 +875,9 @@ static int unit_scale_str(char *str,
len = strlen(str);
len_name = strlen(replace_str);
- len_move = (len - (found_ofs + len_name)) + 1; /* 1+ to copy the string terminator */
+ len_move = (len - (found_ofs + len_name)) + 1; /* 1+ to copy the string terminator. */
- /* # removed later */
+ /* "#" Removed later */
len_num = BLI_snprintf(str_tmp, TEMP_STR_SIZE, "*%.9g" SEP_STR, unit->scalar / scale_pref);
if (len_num > len_max) {
@@ -850,29 +885,28 @@ static int unit_scale_str(char *str,
}
if (found_ofs + len_num + len_move > len_max) {
- /* can't move the whole string, move just as much as will fit */
+ /* Can't move the whole string, move just as much as will fit. */
len_move -= (found_ofs + len_num + len_move) - len_max;
}
if (len_move > 0) {
- /* resize the last part of the string */
-
- /* May grow or shrink the string. */
+ /* Resize the last part of the string.
+ * May grow or shrink the string. */
memmove(str_found + len_num, str_found + len_name, len_move);
}
if (found_ofs + len_num > len_max) {
- /* not even the number will fit into the string, only copy part of it */
+ /* Not even the number will fit into the string, only copy part of it. */
len_num -= (found_ofs + len_num) - len_max;
}
if (len_num > 0) {
- /* its possible none of the number could be copied in */
- memcpy(str_found, str_tmp, len_num); /* without the string terminator */
+ /* It's possible none of the number could be copied in. */
+ memcpy(str_found, str_tmp, len_num); /* Without the string terminator. */
}
- /* since the null terminator wont be moved if the stringlen_max
- * was not long enough to fit everything in it */
+ /* Since the null terminator wont be moved if the stringlen_max
+ * was not long enough to fit everything in it. */
str[len_max - 1] = '\0';
return found_ofs + len_num;
}
@@ -923,7 +957,7 @@ static const bUnitDef *unit_detect_from_str(const bUnitCollection *usys,
* everything. */
const bUnitDef *unit = NULL;
- /* see which units the new value has */
+ /* see which units the new value has. */
for (unit = usys->units; unit->name; unit++) {
if (unit_find(str, unit)) {
break;
@@ -931,7 +965,7 @@ static const bUnitDef *unit_detect_from_str(const bUnitCollection *usys,
}
/* Else, try to infer the default unit from the previous string. */
if (str_prev && (unit == NULL || unit->name == NULL)) {
- /* see which units the original value had */
+ /* See which units the original value had. */
for (unit = usys->units; unit->name; unit++) {
if (unit_find(str_prev, unit)) {
break;
@@ -974,20 +1008,21 @@ double bUnit_PreferredInputUnitScalar(const struct UnitSettings *settings, int t
return bUnit_BaseScalar(units.system, type);
}
-/* make a copy of the string that replaces the units with numbers
- * this is used before parsing
+/**
+ * Make a copy of the string that replaces the units with numbers.
+ *
* This is only used when evaluating user input and can afford to be a bit slower
*
* This is to be used before python evaluation so..
* 10.1km -> 10.1*1000.0
* ...will be resolved by python.
*
- * values will be split by an add sign
+ * Values will be split by an add sign.
* 5'2" -> 5*0.3048 + 2*0.0254
*
- * str_prev is optional, when valid it is used to get a base unit when none is set.
+ * \param str_prev is optional, when valid it is used to get a base unit when none is set.
*
- * return true of a change was made.
+ * \return True of a change was made.
*/
bool bUnit_ReplaceString(
char *str, int len_max, const char *str_prev, double scale_pref, int system, int type)
@@ -1008,8 +1043,8 @@ bool bUnit_ReplaceString(
/* Try to find a default unit from current or previous string. */
default_unit = unit_detect_from_str(usys, str, str_prev);
- /* We apply the default unit to the whole expression (default unit is now the reference '1.0'
- * one). */
+ /* We apply the default unit to the whole expression (default unit is now the reference
+ * '1.0' one). */
scale_pref_base *= default_unit->scalar;
/* Apply the default unit on the whole expression, this allows to handle nasty cases like
@@ -1019,13 +1054,13 @@ bool bUnit_ReplaceString(
strncpy(str, str_tmp, len_max);
}
else {
- /* BLI_snprintf would not fit into str_tmp, cant do much in this case
- * check for this because otherwise bUnit_ReplaceString could call its self forever */
+ /* BLI_snprintf would not fit into str_tmp, cant do much in this case.
+ * Check for this because otherwise bUnit_ReplaceString could call its self forever. */
return changed;
}
for (unit = usys->units; unit->name; unit++) {
- /* in case there are multiple instances */
+ /* In case there are multiple instances. */
while (unit_replace(str, len_max, str_tmp, scale_pref_base, unit)) {
changed = true;
}
@@ -1033,7 +1068,7 @@ bool bUnit_ReplaceString(
unit = NULL;
{
- /* try other unit systems now, so we can evaluate imperial when metric is set for eg. */
+ /* Try other unit systems now, so we can evaluate imperial when metric is set for eg. */
/* Note that checking other systems at that point means we do not support their units as
* 'default' one. In other words, when in metrics, typing '2+2in' will give 2 meters 2 inches,
* not 4 inches. I do think this is the desired behavior!
@@ -1047,7 +1082,7 @@ bool bUnit_ReplaceString(
if (usys_iter) {
for (unit = usys_iter->units; unit->name; unit++) {
int ofs = 0;
- /* in case there are multiple instances */
+ /* In case there are multiple instances. */
while (
(ofs = unit_replace(str + ofs, len_max - ofs, str_tmp, scale_pref_base, unit))) {
changed = true;
@@ -1059,7 +1094,7 @@ bool bUnit_ReplaceString(
}
unit = NULL;
- /* replace # with add sign when there is no operator between it and the next number
+ /* Replace # with add sign when there is no operator between it and the next number.
*
* "1*1# 3*100# * 3" -> "1*1+ 3*100 * 3"
*
@@ -1071,7 +1106,7 @@ bool bUnit_ReplaceString(
while ((str_found = strchr(str_found, SEP_CHR))) {
bool op_found = false;
- /* any operators after this? */
+ /* Any operators after this? */
for (ch = str_found + 1; *ch != '\0'; ch++) {
if (*ch == ' ' || *ch == '\t') {
continue;
@@ -1095,7 +1130,7 @@ void bUnit_ToUnitAltName(char *str, int len_max, const char *orig_str, int syste
const bUnitDef *unit;
- /* find and substitute all units */
+ /* Find and substitute all units. */
for (unit = usys->units; unit->name; unit++) {
if (len_max > 0 && unit->name_alt) {
const bool case_sensitive = (unit->flag & B_UNIT_DEF_CASE_SENSITIVE) != 0;
@@ -1104,7 +1139,7 @@ void bUnit_ToUnitAltName(char *str, int len_max, const char *orig_str, int syste
int offset = (int)(found - orig_str);
int len_name = 0;
- /* copy everything before the unit */
+ /* Copy everything before the unit. */
offset = (offset < len_max ? offset : len_max);
strncpy(str, orig_str, offset);
@@ -1112,7 +1147,7 @@ void bUnit_ToUnitAltName(char *str, int len_max, const char *orig_str, int syste
orig_str += offset + strlen(unit->name_short);
len_max -= offset;
- /* print the alt_name */
+ /* Print the alt_name. */
if (unit->name_alt) {
len_name = BLI_strncpy_rlen(str, unit->name_alt, len_max);
}
@@ -1127,7 +1162,7 @@ void bUnit_ToUnitAltName(char *str, int len_max, const char *orig_str, int syste
}
}
- /* finally copy the rest of the string */
+ /* Finally copy the rest of the string. */
strncpy(str, orig_str, len_max);
}
@@ -1158,7 +1193,7 @@ double bUnit_BaseScalar(int system, int type)
return 1.0;
}
-/* external access */
+/* External access. */
bool bUnit_IsValid(int system, int type)
{
return !(system < 0 || system > UNIT_SYSTEM_TOT || type < 0 || type > B_UNIT_TYPE_TOT);
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 9b307dc6a04..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
@@ -288,7 +297,6 @@ class Array {
{
return data_;
}
-
const T *end() const
{
return data_ + size_;
@@ -298,12 +306,29 @@ class Array {
{
return data_;
}
-
T *end()
{
return data_ + size_;
}
+ std::reverse_iterator<T *> rbegin()
+ {
+ return std::reverse_iterator<T *>(this->end());
+ }
+ std::reverse_iterator<T *> rend()
+ {
+ return std::reverse_iterator<T *>(this->begin());
+ }
+
+ std::reverse_iterator<const T *> rbegin() const
+ {
+ return std::reverse_iterator<T *>(this->end());
+ }
+ std::reverse_iterator<const T *> rend() const
+ {
+ return std::reverse_iterator<T *>(this->begin());
+ }
+
/**
* Get an index range containing all valid indices for this array.
*/
@@ -338,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)
{
@@ -355,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_compiler_compat.h b/source/blender/blenlib/BLI_compiler_compat.h
index 0870d01872a..bd8f84cedd6 100644
--- a/source/blender/blenlib/BLI_compiler_compat.h
+++ b/source/blender/blenlib/BLI_compiler_compat.h
@@ -55,4 +55,3 @@ template<typename T> static inline T decltype_helper(T x)
#else
# define BLI_NOINLINE
#endif
-
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_listbase.h b/source/blender/blenlib/BLI_listbase.h
index fa7cf7a1847..aff80a2bd86 100644
--- a/source/blender/blenlib/BLI_listbase.h
+++ b/source/blender/blenlib/BLI_listbase.h
@@ -171,6 +171,15 @@ struct LinkData *BLI_genericNodeN(void *data);
#define LISTBASE_FOREACH(type, var, list) \
for (type var = (type)((list)->first); var != NULL; var = (type)(((Link *)(var))->next))
+/**
+ * A version of #LISTBASE_FOREACH that supports incrementing an index variable at every step.
+ * Including this in the macro helps prevent mistakes where "continue" mistakenly skips the
+ * incrementation.
+ */
+#define LISTBASE_FOREACH_INDEX(type, var, list, index_var) \
+ for (type var = (((void)(index_var = 0)), (type)((list)->first)); var != NULL; \
+ var = (type)(((Link *)(var))->next), index_var++)
+
#define LISTBASE_FOREACH_BACKWARD(type, var, list) \
for (type var = (type)((list)->last); var != NULL; var = (type)(((Link *)(var))->prev))
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_color.h b/source/blender/blenlib/BLI_math_color.h
index 943f0fc764f..7b48b62b6e7 100644
--- a/source/blender/blenlib/BLI_math_color.h
+++ b/source/blender/blenlib/BLI_math_color.h
@@ -47,7 +47,7 @@ void hsv_to_rgb(float h, float s, float v, float *r_r, float *r_g, float *r_b);
void hsv_to_rgb_v(const float hsv[3], float r_rgb[3]);
void hsl_to_rgb(float h, float c, float l, float *r_r, float *r_g, float *r_b);
void hsl_to_rgb_v(const float hcl[3], float r_rgb[3]);
-void hex_to_rgb(char *hexcol, float *r_r, float *r_g, float *r_b);
+void hex_to_rgb(const char *hexcol, float *r_r, float *r_g, float *r_b);
void yuv_to_rgb(float y, float u, float v, float *r_r, float *r_g, float *r_b, int colorspace);
void ycc_to_rgb(float y, float cb, float cr, float *r_r, float *r_g, float *r_b, int colorspace);
void cpack_to_rgb(unsigned int col, float *r_r, float *r_g, float *r_b);
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..3399287dd46 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 *******************************/
@@ -333,6 +338,7 @@ MINLINE bool equals_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RE
MINLINE bool equals_v4v4(const float a[4], const float b[4]) ATTR_WARN_UNUSED_RESULT;
MINLINE bool equals_v2v2_int(const int v1[2], const int v2[2]) ATTR_WARN_UNUSED_RESULT;
+MINLINE bool equals_v4v4_int(const int v1[4], const int v2[4]) ATTR_WARN_UNUSED_RESULT;
MINLINE bool compare_v2v2(const float a[2],
const float b[2],
@@ -402,6 +408,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 +417,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..cb6fc203dc7
--- /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 \a 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 argument 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 argument isn't `const` because we may populate its verts (for debugging).
+ * Same goes for the pm_triangulated argument.
+ * 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_span.hh b/source/blender/blenlib/BLI_span.hh
index 165814cf23c..5b4d2769f57 100644
--- a/source/blender/blenlib/BLI_span.hh
+++ b/source/blender/blenlib/BLI_span.hh
@@ -213,12 +213,20 @@ template<typename T> class Span {
{
return data_;
}
-
const T *end() const
{
return data_ + size_;
}
+ std::reverse_iterator<const T *> rbegin() const
+ {
+ return std::reverse_iterator<const T *>(this->end());
+ }
+ std::reverse_iterator<const T *> rend() const
+ {
+ return std::reverse_iterator<const T *>(this->begin());
+ }
+
/**
* Access an element in the array. This invokes undefined behavior when the index is out of
* bounds.
@@ -502,12 +510,20 @@ template<typename T> class MutableSpan {
{
return data_;
}
-
T *end() const
{
return data_ + size_;
}
+ std::reverse_iterator<T *> rbegin() const
+ {
+ return std::reverse_iterator<T *>(this->end());
+ }
+ std::reverse_iterator<T *> rend() const
+ {
+ return std::reverse_iterator<T *>(this->begin());
+ }
+
T &operator[](const int64_t index) const
{
BLI_assert(index < this->size());
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_utildefines.h b/source/blender/blenlib/BLI_utildefines.h
index 2699f2498ac..acfa77ecf31 100644
--- a/source/blender/blenlib/BLI_utildefines.h
+++ b/source/blender/blenlib/BLI_utildefines.h
@@ -765,15 +765,15 @@ extern bool BLI_memory_is_zero(const void *arr, const size_t arr_size);
# define ENUM_OPERATORS(_enum_type) \
inline constexpr _enum_type operator|(_enum_type a, _enum_type b) \
{ \
- return a = static_cast<_enum_type>(static_cast<int>(a) | b); \
+ return static_cast<_enum_type>(static_cast<int>(a) | b); \
} \
inline constexpr _enum_type operator&(_enum_type a, _enum_type b) \
{ \
- return a = static_cast<_enum_type>(static_cast<int>(a) & b); \
+ return static_cast<_enum_type>(static_cast<int>(a) & b); \
} \
inline constexpr _enum_type operator~(_enum_type a) \
{ \
- return a = static_cast<_enum_type>(~static_cast<int>(a)); \
+ return static_cast<_enum_type>(~static_cast<int>(a)); \
} \
inline _enum_type &operator|=(_enum_type &a, _enum_type b) \
{ \
diff --git a/source/blender/blenlib/BLI_vector.hh b/source/blender/blenlib/BLI_vector.hh
index 48110ef2814..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);
}
/**
@@ -178,17 +184,16 @@ class Vector {
{
}
- /**
- * Create a vector from any container. It must be possible to use the container in a
- * range-for loop.
- */
- template<typename ContainerT> static Vector FromContainer(const ContainerT &container)
+ template<typename InputIt,
+ /* 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(NoExceptConstructor(), allocator)
{
- Vector vector;
- for (const auto &value : container) {
- vector.append(value);
+ for (InputIt current = first; current != last; ++current) {
+ this->append(*current);
}
- return vector;
}
/**
@@ -198,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);
@@ -228,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 {
@@ -275,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));
}
/**
@@ -476,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)
+ template<typename ForwardT> void append_unchecked(ForwardT &&value)
{
BLI_assert(end_ < capacity_end_);
- new (end_) T(value);
- end_++;
- UPDATE_VECTOR_SIZE(this);
- }
- void append_unchecked(T &&value)
- {
- BLI_assert(end_ < capacity_end_);
- new (end_) T(std::move(value));
+ new (end_) T(std::forward<ForwardT>(value));
end_++;
UPDATE_VECTOR_SIZE(this);
}
@@ -499,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);
}
@@ -509,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;
@@ -555,11 +536,101 @@ 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);
}
+ template<typename InputIt> void extend(InputIt first, InputIt last)
+ {
+ this->insert(this->end(), first, last);
+ }
+
+ /**
+ * Insert elements into the vector at the specified position. This has a running time of O(n)
+ * where n is the number of values that have to be moved. Undefined behavior is invoked when the
+ * insert position is out of bounds.
+ */
+ void insert(const int64_t insert_index, const T &value)
+ {
+ this->insert(insert_index, Span<T>(&value, 1));
+ }
+ void insert(const int64_t insert_index, T &&value)
+ {
+ this->insert(
+ insert_index, std::make_move_iterator(&value), std::make_move_iterator(&value + 1));
+ }
+ void insert(const int64_t insert_index, Span<T> array)
+ {
+ this->insert(begin_ + insert_index, array.begin(), array.end());
+ }
+ template<typename InputIt> void insert(const T *insert_position, InputIt first, InputIt last)
+ {
+ const int64_t insert_index = insert_position - begin_;
+ this->insert(insert_index, first, last);
+ }
+ template<typename InputIt> void insert(const int64_t insert_index, InputIt first, InputIt last)
+ {
+ BLI_assert(insert_index >= 0);
+ BLI_assert(insert_index <= this->size());
+
+ const int64_t insert_amount = std::distance(first, last);
+ const int64_t old_size = this->size();
+ const int64_t new_size = old_size + insert_amount;
+ const int64_t move_amount = old_size - insert_index;
+
+ this->reserve(new_size);
+ 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;
+ 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();
+ }
+
+ 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);
+ }
+
+ /**
+ * Insert values at the beginning of the vector. The has to move all the other elements, so it
+ * has a linear running time.
+ */
+ void prepend(const T &&value)
+ {
+ this->insert(0, value);
+ }
+ void prepend(T &&value)
+ {
+ this->insert(0, std::move(value));
+ }
+ void prepend(Span<T> values)
+ {
+ this->insert(0, values);
+ }
+ template<typename InputIt> void prepend(InputIt first, InputIt last)
+ {
+ this->insert(0, first, last);
+ }
+
/**
* Return a reference to the last element in the vector.
* This invokes undefined behavior when the vector is empty.
@@ -616,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;
@@ -632,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);
}
@@ -671,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.
*/
@@ -746,6 +838,24 @@ class Vector {
return end_;
}
+ std::reverse_iterator<T *> rbegin()
+ {
+ return std::reverse_iterator<T *>(this->end());
+ }
+ std::reverse_iterator<T *> rend()
+ {
+ return std::reverse_iterator<T *>(this->begin());
+ }
+
+ std::reverse_iterator<const T *> rbegin() const
+ {
+ return std::reverse_iterator<T *>(this->end());
+ }
+ std::reverse_iterator<const T *> rend() const
+ {
+ return std::reverse_iterator<T *>(this->begin());
+ }
+
/**
* Get the current capacity of the vector, i.e. the maximum number of elements the vector can
* hold, before it has to reallocate.
@@ -813,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/BLI_kdopbvh.c b/source/blender/blenlib/intern/BLI_kdopbvh.c
index f63a523ca60..f030a733752 100644
--- a/source/blender/blenlib/intern/BLI_kdopbvh.c
+++ b/source/blender/blenlib/intern/BLI_kdopbvh.c
@@ -201,6 +201,23 @@ const float bvhtree_kdop_axes[13][3] = {
{0, 1.0, -1.0},
};
+/* Used to correct the epsilon and thus match the overlap distance. */
+const float bvhtree_kdop_axes_length[13] = {
+ 1.0f,
+ 1.0f,
+ 1.0f,
+ 1.7320508075688772f,
+ 1.7320508075688772f,
+ 1.7320508075688772f,
+ 1.7320508075688772f,
+ 1.4142135623730951f,
+ 1.4142135623730951f,
+ 1.4142135623730951f,
+ 1.4142135623730951f,
+ 1.4142135623730951f,
+ 1.4142135623730951f,
+};
+
/* -------------------------------------------------------------------- */
/** \name Utility Functions
* \{ */
@@ -970,9 +987,18 @@ void BLI_bvhtree_balance(BVHTree *tree)
#endif
}
-void BLI_bvhtree_insert(BVHTree *tree, int index, const float co[3], int numpoints)
+static void bvhtree_node_inflate(const BVHTree *tree, BVHNode *node, const float dist)
{
axis_t axis_iter;
+ for (axis_iter = tree->start_axis; axis_iter < tree->stop_axis; axis_iter++) {
+ float dist_corrected = dist * bvhtree_kdop_axes_length[axis_iter];
+ node->bv[(2 * axis_iter)] -= dist_corrected; /* minimum */
+ node->bv[(2 * axis_iter) + 1] += dist_corrected; /* maximum */
+ }
+}
+
+void BLI_bvhtree_insert(BVHTree *tree, int index, const float co[3], int numpoints)
+{
BVHNode *node = NULL;
/* insert should only possible as long as tree->totbranch is 0 */
@@ -986,10 +1012,7 @@ void BLI_bvhtree_insert(BVHTree *tree, int index, const float co[3], int numpoin
node->index = index;
/* inflate the bv with some epsilon */
- for (axis_iter = tree->start_axis; axis_iter < tree->stop_axis; axis_iter++) {
- node->bv[(2 * axis_iter)] -= tree->epsilon; /* minimum */
- node->bv[(2 * axis_iter) + 1] += tree->epsilon; /* maximum */
- }
+ bvhtree_node_inflate(tree, node, tree->epsilon);
}
/* call before BLI_bvhtree_update_tree() */
@@ -997,7 +1020,6 @@ bool BLI_bvhtree_update_node(
BVHTree *tree, int index, const float co[3], const float co_moving[3], int numpoints)
{
BVHNode *node = NULL;
- axis_t axis_iter;
/* check if index exists */
if (index > tree->totleaf) {
@@ -1013,10 +1035,7 @@ bool BLI_bvhtree_update_node(
}
/* inflate the bv with some epsilon */
- for (axis_iter = tree->start_axis; axis_iter < tree->stop_axis; axis_iter++) {
- node->bv[(2 * axis_iter)] -= tree->epsilon; /* minimum */
- node->bv[(2 * axis_iter) + 1] += tree->epsilon; /* maximum */
- }
+ bvhtree_node_inflate(tree, node, tree->epsilon);
return true;
}
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/freetypefont.c b/source/blender/blenlib/intern/freetypefont.c
index 34886054cb0..301d9dc2296 100644
--- a/source/blender/blenlib/intern/freetypefont.c
+++ b/source/blender/blenlib/intern/freetypefont.c
@@ -15,7 +15,8 @@
*
* The Original Code is written by Rob Haarsma (phase)
* All rights reserved.
- * This code parses the Freetype font outline data to chains of Blender's beziertriples.
+ *
+ * This code parses the Freetype font outline data to chains of Blender's bezier-triples.
* Additional information can be found at the bottom of this file.
*
* Code that uses exotic character maps is present but commented out.
diff --git a/source/blender/blenlib/intern/math_boolean.cc b/source/blender/blenlib/intern/math_boolean.cc
new file mode 100644
index 00000000000..0c3b4ab8395
--- /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
+ * significant 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. Non-robust.
+ * 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_color.c b/source/blender/blenlib/intern/math_color.c
index 09bb7ea5711..4b62d6b9b5b 100644
--- a/source/blender/blenlib/intern/math_color.c
+++ b/source/blender/blenlib/intern/math_color.c
@@ -198,7 +198,7 @@ void ycc_to_rgb(float y, float cb, float cr, float *r_r, float *r_g, float *r_b,
*r_b = b / 255.0f;
}
-void hex_to_rgb(char *hexcol, float *r_r, float *r_g, float *r_b)
+void hex_to_rgb(const char *hexcol, float *r_r, float *r_g, float *r_b)
{
unsigned int ri, gi, bi;
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..13a87b9ec6a 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)
@@ -1274,6 +1313,11 @@ MINLINE bool equals_v2v2_int(const int v1[2], const int v2[2])
return ((v1[0] == v2[0]) && (v1[1] == v2[1]));
}
+MINLINE bool equals_v4v4_int(const int v1[4], const int v2[4])
+{
+ return ((v1[0] == v2[0]) && (v1[1] == v2[1]) && (v1[2] == v2[2]) && (v1[3] == v2[3]));
+}
+
MINLINE bool compare_v2v2(const float v1[2], const float v2[2], const float limit)
{
return (compare_ff(v1[0], v2[0], limit) && compare_ff(v1[1], v2[1], limit));
diff --git a/source/blender/blenlib/intern/mesh_boolean.cc b/source/blender/blenlib/intern/mesh_boolean.cc
new file mode 100644
index 00000000000..387d879c5af
--- /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 closestp 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..5bd25404674
--- /dev/null
+++ b/source/blender/blenlib/intern/mesh_intersect.cc
@@ -0,0 +1,3322 @@
+/*
+ * 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()
+{
+ 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_.combine(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.
+ * In addition, it must not be the case that all of the points of one triangle
+ * are totally to one side of one edge of the other triangle, 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;
+ }
+ int s = orients[ab][0][i] + orients[ab][1][i] + orients[ab][2][i];
+ if (s == 3 || s == -3) {
+ 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 Map<std::pair<int, int>, ITT_value> &itt_map)
+{
+ 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) {
+ if (!itt_map.contains(std::pair<int, int>(t, cl_t)) &&
+ !itt_map.contains(std::pair<int, int>(cl_t, t))) {
+ continue;
+ }
+ 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);
+}
+
+/* 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;
+
+/**
+ * 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;
+
+/* Not using this at the moment. Leaving it for a bit in case we want it again. */
+# if 0
+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);
+}
+
+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;
+}
+# endif
+
+/**
+ * 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;
+}
+
+/* Not using this at the moment. Leave it here for a while in case we want it again. */
+# if 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;
+}
+# endif
+
+/*
+ * 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};
+ Array<int> first_overlap_;
+ 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) {
+ overlap_ = BLI_bvhtree_overlap(tree_, tree_, &overlap_tot_, NULL, NULL);
+ }
+ 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";
+ }
+ }
+ first_overlap_ = Array<int>(tm.face_size(), -1);
+ for (int i = 0; i < static_cast<int>(overlap_tot_); ++i) {
+ int t = overlap_[i].indexA;
+ if (first_overlap_[t] == -1) {
+ first_overlap_[t] = i;
+ }
+ }
+ }
+
+ ~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_);
+ }
+
+ int first_overlap_index(int t) const
+ {
+ return first_overlap_[t];
+ }
+
+ private:
+ 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.
+ */
+static void calc_overlap_itts(Map<std::pair<int, int>, ITT_value> &itt_map,
+ const IMesh &tm,
+ 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)) {
+ 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);
+}
+
+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 = ov.first_overlap_index(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,
+ const Map<std::pair<int, int>, ITT_value> &itt_map)
+{
+ constexpr int dbg_level = 0;
+ if (dbg_level > 0) {
+ std::cout << "FIND_CLUSTERS\n";
+ }
+ CoplanarClusterInfo ans(tm.face_size());
+ /* Use a VectorSet to get stable order from run to run. */
+ VectorSet<int> maybe_coplanar_tris;
+ maybe_coplanar_tris.reserve(2 * itt_map.size());
+ for (auto item : itt_map.items()) {
+ if (item.value.kind == ICOPLANAR) {
+ int t1 = item.key.first;
+ int t2 = item.key.second;
+ maybe_coplanar_tris.add_multiple({t1, t2});
+ }
+ }
+ if (dbg_level > 0) {
+ std::cout << "found " << maybe_coplanar_tris.size() << " possible coplanar tris\n";
+ }
+ if (maybe_coplanar_tris.size() == 0) {
+ if (dbg_level > 0) {
+ std::cout << "No possible coplanar tris, so no clusters\n";
+ }
+ return ans;
+ }
+ /* 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(maybe_coplanar_tris.size());
+ for (int t : maybe_coplanar_tris) {
+ /* 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 (dbg_level > 1) {
+ std::cout << "consider intersecting with cluster " << cl << "\n";
+ }
+ if (bbs_might_intersect(tri_bb[t], cl.bounding_box()) &&
+ non_trivially_coplanar_intersects(tm, t, cl, proj_axis, itt_map)) {
+ 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";
+ }
+ write_obj_mesh(const_cast<IMesh &>(tm_in), "trimesh_input");
+ }
+ }
+# 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;
+ }
+ }
+# 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
+ for (int t : tm_clean->face_index_range()) {
+ if (tri_ov.first_overlap_index(t) != -1) {
+ tm_clean->face(t)->populate_plane(true);
+ }
+ }
+# ifdef PERFDEBUG
+ double plane_populate = PIL_check_seconds_timer();
+ std::cout << "planes populated, time = " << plane_populate - overlap_time << "\n";
+# 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, tri_ov, arena);
+# ifdef PERFDEBUG
+ double itt_time = PIL_check_seconds_timer();
+ std::cout << "itts found, time = " << itt_time - plane_populate << "\n";
+# endif
+ CoplanarClusterInfo clinfo = find_clusters(*tm_clean, tri_bb, itt_map);
+ 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 - itt_time << "\n";
+ doperfmax(0, tm_in.face_size());
+ doperfmax(1, clinfo.tot_cluster());
+ doperfmax(2, tri_ov.overlap().size());
+# endif
+ Array<IMesh> tri_subdivided(tm_clean->face_size());
+ calc_subdivided_tris(tri_subdivided, *tm_clean, itt_map, clinfo, tri_ov, arena);
+# 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 7348a6f93f3..7d967eca87e 100644
--- a/source/blender/blenlib/tests/BLI_array_test.cc
+++ b/source/blender/blenlib/tests/BLI_array_test.cc
@@ -1,7 +1,9 @@
/* 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"
namespace blender::tests {
@@ -173,4 +175,79 @@ TEST(array, Fill)
EXPECT_EQ(array[4], 3);
}
+TEST(array, ReverseIterator)
+{
+ Array<int> array = {3, 4, 5, 6};
+ Vector<int> reversed_vec;
+
+ for (auto it = array.rbegin(); it != array.rend(); ++it) {
+ reversed_vec.append(*it);
+ *it += 10;
+ }
+
+ EXPECT_EQ_ARRAY(reversed_vec.data(), Span({6, 5, 4, 3}).data(), 4);
+ 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..237905a1bf6
--- /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(512, 0.5, false);
+}
+
+TEST(mesh_intersect_perf, SphereGrid)
+{
+ spheregrid_test(512, 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_span_test.cc b/source/blender/blenlib/tests/BLI_span_test.cc
index 6ad2a5633ad..9a8d9df7873 100644
--- a/source/blender/blenlib/tests/BLI_span_test.cc
+++ b/source/blender/blenlib/tests/BLI_span_test.cc
@@ -237,7 +237,8 @@ TEST(span, ContainsPtr)
EXPECT_TRUE(a_span.contains_ptr(&a[0] + 1));
EXPECT_TRUE(a_span.contains_ptr(&a[0] + 2));
EXPECT_FALSE(a_span.contains_ptr(&a[0] + 3));
- EXPECT_FALSE(a_span.contains_ptr(&a[0] - 1));
+ int *ptr_before = reinterpret_cast<int *>(reinterpret_cast<uintptr_t>(a.data()) - 1);
+ EXPECT_FALSE(a_span.contains_ptr(ptr_before));
EXPECT_FALSE(a_span.contains_ptr(&other));
}
@@ -308,4 +309,32 @@ TEST(span, CopyFrom)
EXPECT_EQ(dst[3], 8);
}
+TEST(span, ReverseIterator)
+{
+ std::array<int, 4> src = {4, 5, 6, 7};
+ Span<int> span = src;
+ Vector<int> reversed_vec;
+
+ for (auto it = span.rbegin(); it != span.rend(); ++it) {
+ reversed_vec.append(*it);
+ }
+ EXPECT_EQ(reversed_vec.size(), 4);
+ EXPECT_EQ_ARRAY(reversed_vec.data(), Span({7, 6, 5, 4}).data(), 4);
+}
+
+TEST(span, MutableReverseIterator)
+{
+ std::array<int, 4> src = {4, 5, 6, 7};
+ MutableSpan<int> span = src;
+ Vector<int> reversed_vec;
+
+ for (auto it = span.rbegin(); it != span.rend(); ++it) {
+ reversed_vec.append(*it);
+ *it += 10;
+ }
+ EXPECT_EQ(reversed_vec.size(), 4);
+ EXPECT_EQ_ARRAY(reversed_vec.data(), Span({7, 6, 5, 4}).data(), 4);
+ EXPECT_EQ_ARRAY(src.data(), Span({14, 15, 16, 17}).data(), 4);
+}
+
} // namespace blender::tests
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 f72dfc5deb8..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"
@@ -98,14 +99,14 @@ TEST(vector, ListBaseConstructor)
delete value3;
}
-TEST(vector, ContainerConstructor)
+TEST(vector, IteratorConstructor)
{
std::forward_list<int> list;
list.push_front(3);
list.push_front(1);
list.push_front(5);
- Vector<int> vec = Vector<int>::FromContainer(list);
+ Vector<int> vec = Vector<int>(list.begin(), list.end());
EXPECT_EQ(vec.size(), 3);
EXPECT_EQ(vec[0], 5);
EXPECT_EQ(vec[1], 1);
@@ -279,6 +280,15 @@ TEST(vector, ExtendNonDuplicates)
EXPECT_EQ(vec.size(), 5);
}
+TEST(vector, ExtendIterator)
+{
+ Vector<int> vec = {3, 4, 5};
+ std::forward_list<int> list = {8, 9};
+ vec.extend(list.begin(), list.end());
+ EXPECT_EQ(vec.size(), 5);
+ EXPECT_EQ_ARRAY(vec.data(), Span({3, 4, 5, 8, 9}).data(), 5);
+}
+
TEST(vector, Iterator)
{
Vector<int> vec({1, 4, 9, 16});
@@ -636,4 +646,183 @@ TEST(vector, Fill)
EXPECT_EQ(vec[4], 3);
}
+TEST(vector, InsertAtBeginning)
+{
+ Vector<int> vec = {1, 2, 3};
+ vec.insert(0, {6, 7});
+ EXPECT_EQ(vec.size(), 5);
+ EXPECT_EQ_ARRAY(vec.data(), Span({6, 7, 1, 2, 3}).data(), 5);
+}
+
+TEST(vector, InsertAtEnd)
+{
+ Vector<int> vec = {1, 2, 3};
+ vec.insert(3, {6, 7});
+ EXPECT_EQ(vec.size(), 5);
+ EXPECT_EQ_ARRAY(vec.data(), Span({1, 2, 3, 6, 7}).data(), 5);
+}
+
+TEST(vector, InsertInMiddle)
+{
+ Vector<int> vec = {1, 2, 3};
+ vec.insert(1, {6, 7});
+ EXPECT_EQ(vec.size(), 5);
+ EXPECT_EQ_ARRAY(vec.data(), Span({1, 6, 7, 2, 3}).data(), 5);
+}
+
+TEST(vector, InsertAtIterator)
+{
+ Vector<std::string> vec = {"1", "2", "3"};
+ Vector<std::string> other_vec = {"hello", "world"};
+ vec.insert(vec.begin() + 1, other_vec.begin(), other_vec.end());
+ EXPECT_EQ(vec.size(), 5);
+ EXPECT_EQ_ARRAY(vec.data(), Span<std::string>({"1", "hello", "world", "2", "3"}).data(), 5);
+}
+
+TEST(vector, InsertMoveOnlyType)
+{
+ Vector<std::unique_ptr<int>> vec;
+ vec.append(std::make_unique<int>(1));
+ vec.append(std::make_unique<int>(2));
+ vec.insert(1, std::make_unique<int>(30));
+ EXPECT_EQ(vec.size(), 3);
+ EXPECT_EQ(*vec[0], 1);
+ EXPECT_EQ(*vec[1], 30);
+ EXPECT_EQ(*vec[2], 2);
+}
+
+TEST(vector, Prepend)
+{
+ Vector<int> vec = {1, 2, 3};
+ vec.prepend({7, 8});
+ EXPECT_EQ(vec.size(), 5);
+ EXPECT_EQ_ARRAY(vec.data(), Span({7, 8, 1, 2, 3}).data(), 5);
+}
+
+TEST(vector, ReverseIterator)
+{
+ Vector<int> vec = {4, 5, 6, 7};
+ Vector<int> reversed_vec;
+ for (auto it = vec.rbegin(); it != vec.rend(); ++it) {
+ reversed_vec.append(*it);
+ }
+ EXPECT_EQ(reversed_vec.size(), 4);
+ 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/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h
index 97c77ed2e19..580c833d8dc 100644
--- a/source/blender/blenloader/BLO_readfile.h
+++ b/source/blender/blenloader/BLO_readfile.h
@@ -186,6 +186,9 @@ void BLO_update_defaults_workspace(struct WorkSpace *workspace, const char *app_
/* Version patch user preferences. */
void BLO_version_defaults_userpref_blend(struct Main *mainvar, struct UserDef *userdef);
+/* Disable unwanted experimental feature settings on startup. */
+void BLO_sanitize_experimental_features_userpref_blend(struct UserDef *userdef);
+
struct BlendThumbnail *BLO_thumbnail_from_file(const char *filepath);
/* datafiles (generated theme) */
diff --git a/source/blender/blenloader/CMakeLists.txt b/source/blender/blenloader/CMakeLists.txt
index c56e0b5ad2e..f5c7223a37c 100644
--- a/source/blender/blenloader/CMakeLists.txt
+++ b/source/blender/blenloader/CMakeLists.txt
@@ -101,8 +101,8 @@ add_dependencies(bf_blenloader bf_dna)
if(WITH_GTESTS)
set(TEST_SRC
- tests/blendfile_loading_base_test.cc
tests/blendfile_load_test.cc
+ tests/blendfile_loading_base_test.cc
)
set(TEST_INC
)
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index 42b97550db1..84cb898a426 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"
@@ -263,8 +266,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;
@@ -1826,9 +1827,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) {
@@ -1852,9 +1851,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;
}
@@ -1872,47 +1869,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);
}
@@ -1923,44 +1912,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);
}
}
@@ -1968,14 +1949,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));
}
}
@@ -2003,7 +1982,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);
@@ -2018,11 +1997,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;
@@ -2049,7 +2025,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;
@@ -2269,185 +2245,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
* \{ */
@@ -2456,8 +2253,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]);
}
@@ -2503,11 +2299,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) {
@@ -2634,7 +2430,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;
@@ -2662,7 +2458,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;
@@ -2675,7 +2471,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;
}
@@ -2835,12 +2631,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. */
@@ -2871,11 +2667,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);
@@ -2885,14 +2679,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);
}
}
@@ -2901,11 +2692,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);
}
}
@@ -2913,9 +2702,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);
}
}
@@ -2926,153 +2713,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 <<<
@@ -3082,7 +2722,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) {
@@ -3093,102 +2733,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);
}
}
@@ -3197,71 +2769,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);
-}
-
/** \} */
/* -------------------------------------------------------------------- */
@@ -3281,7 +2800,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);
}
/** \} */
@@ -3329,7 +2848,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);
}
@@ -3338,7 +2857,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;
@@ -3363,7 +2882,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: {
@@ -3410,7 +2929,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);
@@ -3429,7 +2948,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);
}
@@ -3445,7 +2964,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;
@@ -3458,9 +2977,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;
@@ -3471,20 +2987,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);
@@ -3563,14 +3079,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);
}
}
@@ -3578,14 +3094,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);
@@ -3622,10 +3138,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) {
@@ -3649,10 +3164,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) {
@@ -3662,7 +3175,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: {
@@ -3715,7 +3228,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;
}
@@ -3741,7 +3254,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)) {
@@ -3763,7 +3276,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);
@@ -3779,11 +3292,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);
@@ -3792,15 +3303,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;
@@ -3808,9 +3317,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);
}
@@ -3842,7 +3351,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);
@@ -3866,7 +3375,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) {
@@ -3884,10 +3393,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++;
}
}
@@ -3902,13 +3409,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;
-
- elemsize = key->elemsize;
- data = kb->data;
+ int elemsize = key->elemsize;
+ char *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;
@@ -3916,11 +3420,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;
@@ -3931,16 +3436,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)) {
@@ -3967,7 +3470,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);
@@ -3996,7 +3499,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);
@@ -4031,8 +3534,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;
@@ -4049,7 +3550,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;
@@ -4083,12 +3584,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;
}
@@ -4097,7 +3596,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;
@@ -4150,11 +3649,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);
@@ -4171,7 +3667,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);
@@ -4192,7 +3688,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);
@@ -4223,7 +3719,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);
@@ -4258,7 +3754,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;
@@ -4289,9 +3785,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 */
@@ -4308,7 +3802,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);
}
}
@@ -4336,9 +3830,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;
@@ -4401,11 +3894,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: {
@@ -4441,13 +3931,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);
@@ -4475,16 +3963,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]);
}
@@ -4497,16 +3984,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);
}
@@ -4535,11 +4017,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) {
@@ -4628,255 +4109,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);
-}
-
/** \} */
/* -------------------------------------------------------------------- */
@@ -4899,7 +4131,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;
}
}
@@ -4911,8 +4143,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;
}
}
@@ -4924,7 +4155,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;
}
}
@@ -4933,7 +4164,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);
@@ -5012,7 +4242,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]);
}
@@ -5093,17 +4323,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");
}
@@ -5127,8 +4346,6 @@ static void direct_link_motionpath(BlendDataReader *reader, bMotionPath *mpath)
static void direct_link_pose(BlendDataReader *reader, bPose *pose)
{
- bPoseChannel *pchan;
-
if (!pose) {
return;
}
@@ -5139,7 +4356,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);
@@ -5154,7 +4371,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) {
@@ -5291,11 +4508,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;
@@ -5452,10 +4667,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);
@@ -5484,11 +4698,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 */
@@ -5565,11 +4777,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 */
@@ -5593,15 +4803,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);
@@ -5690,8 +4901,7 @@ static void direct_link_object(BlendDataReader *reader, Object *ob)
/* 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]);
}
}
@@ -5773,14 +4983,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);
}
@@ -5827,7 +5034,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));
@@ -5847,9 +5054,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);
}
}
@@ -5880,15 +5086,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);
}
/** \} */
@@ -5949,8 +5153,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) {
@@ -5958,7 +5161,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);
}
}
@@ -5987,9 +5190,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;
}
@@ -6014,9 +5216,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);
}
@@ -6068,7 +5268,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);
}
}
@@ -6167,10 +5367,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) {
@@ -6187,8 +5384,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(
@@ -6229,7 +5426,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) {
@@ -6293,7 +5490,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)) {
@@ -6310,11 +5507,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);
}
@@ -6359,11 +5554,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);
}
@@ -6383,13 +5576,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;
@@ -6404,7 +5590,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);
@@ -6463,7 +5649,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;
@@ -6472,7 +5658,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);
@@ -6503,7 +5690,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) {
@@ -6535,7 +5722,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 */
@@ -6565,7 +5752,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) {
@@ -6597,7 +5784,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));
@@ -6605,9 +5792,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));
}
@@ -6615,7 +5802,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);
@@ -6669,11 +5856,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 {
@@ -6688,7 +5875,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);
}
/** \} */
@@ -6716,8 +5903,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;
@@ -6725,7 +5910,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) {
@@ -6745,7 +5930,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);
}
}
@@ -6780,7 +5965,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);
}
}
}
@@ -6808,19 +5993,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);
@@ -6873,9 +6056,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));
@@ -6900,7 +6080,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);
}
@@ -6916,7 +6096,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
@@ -6925,7 +6105,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);
}
@@ -6980,8 +6160,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];
}
@@ -7063,7 +6242,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);
@@ -7074,8 +6252,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 */
@@ -7237,7 +6414,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);
@@ -7251,6 +6427,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 */
@@ -7266,6 +6443,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;
@@ -7348,12 +6526,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;
@@ -7888,8 +7064,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);
}
@@ -7944,9 +7119,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;
@@ -8051,10 +7224,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
@@ -8066,7 +7238,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. */
@@ -8092,7 +7264,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);
}
/** \} */
@@ -8109,7 +7281,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);
@@ -8135,7 +7307,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;
}
@@ -8169,26 +7341,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]);
}
@@ -8199,7 +7365,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);
@@ -8227,7 +7392,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);
@@ -8236,9 +7401,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);
}
}
@@ -8247,9 +7410,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);
}
}
@@ -8277,28 +7438,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) {
@@ -8315,8 +7470,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) {
@@ -8345,9 +7499,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);
@@ -8368,9 +7520,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 =
@@ -8380,7 +7530,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 =
@@ -8390,7 +7540,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 =
@@ -8576,28 +7726,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]);
}
}
@@ -8618,11 +7765,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 */
@@ -8645,10 +7792,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 */
@@ -8676,7 +7823,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;
@@ -8701,7 +7848,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) {
@@ -8709,7 +7856,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);
}
}
@@ -8747,7 +7894,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);
@@ -8856,6 +8003,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. */
@@ -8874,9 +8024,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;
@@ -8907,9 +8054,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;
@@ -8982,6 +8126,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. */
@@ -9458,11 +8606,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);
}
@@ -9577,6 +8723,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
@@ -9642,18 +8793,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;
@@ -9707,6 +8852,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;
@@ -9743,7 +8892,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;
}
@@ -9751,11 +8900,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 */
@@ -9775,7 +8919,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;
@@ -9783,7 +8927,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);
@@ -9795,14 +8939,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) {
@@ -9811,14 +8955,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
@@ -10277,8 +9421,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);
}
@@ -10288,115 +9431,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);
@@ -10421,7 +9460,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);
@@ -10430,7 +9469,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);
@@ -10438,17 +9477,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) {
@@ -10459,12 +9496,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);
}
}
@@ -10472,15 +9506,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);
@@ -10501,11 +9533,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);
@@ -10547,7 +9576,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) {
@@ -10589,18 +9618,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);
@@ -10643,12 +9670,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
@@ -10656,18 +9677,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]);
}
@@ -10682,18 +9699,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,
@@ -10706,12 +9711,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
}
@@ -10720,22 +9723,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);
@@ -10760,11 +9761,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() */
@@ -10794,18 +9790,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);
}
@@ -10821,7 +9817,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);
}
@@ -10842,13 +9838,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
@@ -10866,10 +9855,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);
}
@@ -10882,14 +9867,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);
}
@@ -10898,15 +9883,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);
}
@@ -10921,8 +9906,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);
@@ -10945,7 +9930,7 @@ static void expand_scene(BlendExpander *expander, Scene *sce)
BLO_expand(expander, data->text_font);
}
}
- SEQ_END;
+ SEQ_ALL_END;
}
if (sce->rigidbody_world) {
@@ -11017,17 +10002,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);
}
@@ -11038,27 +10016,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);
}
@@ -11088,10 +10063,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)
@@ -11099,10 +10070,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)
@@ -11110,17 +10077,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);
}
@@ -11163,13 +10123,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;
@@ -11188,9 +10150,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;
@@ -11281,9 +10240,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;
}
@@ -11294,9 +10251,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;
}
@@ -11320,7 +10275,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) {
@@ -11387,8 +10342,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);
@@ -11431,8 +10385,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)) {
@@ -11446,8 +10399,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) {
@@ -11673,9 +10625,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);
@@ -12121,8 +11071,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. */
@@ -12139,6 +11089,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);
@@ -12294,6 +11249,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 fced95ee629..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;
}
}
@@ -4953,8 +4953,6 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
const View3D *v3d_default = DNA_struct_default_get(View3D);
wm->xr.session_settings.shading = v3d_default->shading;
- /* Don't rotate light with the viewer by default, make it fixed. */
- wm->xr.session_settings.shading.flag |= V3D_SHADING_WORLD_ORIENTATION;
wm->xr.session_settings.draw_flags = (V3D_OFSDRAW_SHOW_GRIDFLOOR |
V3D_OFSDRAW_SHOW_ANNOTATION);
wm->xr.session_settings.clip_start = v3d_default->clip_start;
@@ -5112,6 +5110,12 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
+ for (wmWindowManager *wm = bmain->wm.first; wm; wm = wm->id.next) {
+ /* Don't rotate light with the viewer by default, make it fixed. Shading settings can't be
+ * edited and this flag should always be set. So we can always execute this. */
+ wm->xr.session_settings.shading.flag |= V3D_SHADING_WORLD_ORIENTATION;
+ }
+
/* Keep this block, even when empty. */
}
}
diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c
index 12b5a297df5..3c326565557 100644
--- a/source/blender/blenloader/intern/versioning_290.c
+++ b/source/blender/blenloader/intern/versioning_290.c
@@ -190,22 +190,35 @@ void do_versions_after_linking_290(Main *bmain, ReportList *UNUSED(reports))
/* Patch first frame for old files. */
Scene *scene = bmain->scenes.first;
- LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
- if (ob->type != OB_GPENCIL) {
- continue;
- }
- bGPdata *gpd = ob->data;
- LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
- bGPDframe *gpf = gpl->frames.first;
- if (gpf && gpf->framenum > scene->r.sfra) {
- bGPDframe *gpf_dup = BKE_gpencil_frame_duplicate(gpf);
- gpf_dup->framenum = scene->r.sfra;
- BLI_addhead(&gpl->frames, gpf_dup);
+ if (scene != NULL) {
+ LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
+ if (ob->type != OB_GPENCIL) {
+ continue;
+ }
+ bGPdata *gpd = ob->data;
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ bGPDframe *gpf = gpl->frames.first;
+ if (gpf && gpf->framenum > scene->r.sfra) {
+ bGPDframe *gpf_dup = BKE_gpencil_frame_duplicate(gpf);
+ gpf_dup->framenum = scene->r.sfra;
+ BLI_addhead(&gpl->frames, gpf_dup);
+ }
}
}
}
}
+ 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.
*
@@ -217,18 +230,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);
@@ -407,17 +428,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")) {
@@ -482,5 +493,41 @@ 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;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * 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_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c
index df0b2b380fa..b4bee9a3c7e 100644
--- a/source/blender/blenloader/intern/versioning_defaults.c
+++ b/source/blender/blenloader/intern/versioning_defaults.c
@@ -174,7 +174,7 @@ static void blo_update_defaults_screen(bScreen *screen,
}
else if (area->spacetype == SPACE_SEQ) {
SpaceSeq *seq = area->spacedata.first;
- seq->flag |= SEQ_SHOW_MARKERS | SEQ_SHOW_FCURVES;
+ seq->flag |= SEQ_SHOW_MARKERS | SEQ_SHOW_FCURVES | SEQ_ZOOM_TO_FIT;
}
else if (area->spacetype == SPACE_TEXT) {
/* Show syntax and line numbers in Script workspace text editor. */
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 d1bd518baca..bfbf985b08f 100644
--- a/source/blender/blenloader/intern/versioning_userdef.c
+++ b/source/blender/blenloader/intern/versioning_userdef.c
@@ -38,6 +38,7 @@
#include "DNA_windowmanager_types.h"
#include "BKE_addon.h"
+#include "BKE_blender_version.h"
#include "BKE_colorband.h"
#include "BKE_idprop.h"
#include "BKE_keyconfig.h"
@@ -227,6 +228,12 @@ 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);
+
FROM_DEFAULT_V4_UCHAR(space_graph.vertex_active);
}
@@ -758,6 +765,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 +782,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) {
@@ -785,4 +794,23 @@ void BLO_version_defaults_userpref_blend(Main *bmain, UserDef *userdef)
#undef USER_VERSION_ATLEAST
}
+void BLO_sanitize_experimental_features_userpref_blend(UserDef *userdef)
+{
+ /* User preference experimental settings are only supported in alpha builds.
+ * This prevents users corrupting data and relying on API that may change.
+ *
+ * If user preferences are saved this will be stored in disk as expected.
+ * This only starts to take effect when there is a release branch (on beta).
+ *
+ * At that time master already has its version bumped so its user preferences
+ * are not touched by these settings. */
+
+ if (BKE_blender_version_is_alpha()) {
+ return;
+ }
+ userdef->experimental.use_new_particle_system = false;
+ userdef->experimental.use_new_hair_type = false;
+ userdef->experimental.use_sculpt_vertex_colors = false;
+}
+
#undef USER_LMOUSESELECT
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c
index ed4f997a856..a9c92719a33 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 */
@@ -1902,7 +1621,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);
}
@@ -1917,7 +1635,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) {
@@ -1933,10 +1651,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 */
@@ -1954,10 +1672,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) {
@@ -1979,12 +1697,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) {
@@ -2003,12 +1721,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) {
@@ -2040,206 +1758,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)) {
@@ -2254,7 +1772,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);
@@ -2285,10 +1803,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 */
@@ -2315,10 +1833,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 */
@@ -2344,10 +1862,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 */
@@ -2365,10 +1883,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) {
@@ -2410,7 +1928,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);
}
@@ -2418,9 +1936,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) {
@@ -2453,7 +1969,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);
}
}
@@ -2480,7 +1996,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) {
@@ -2538,10 +2054,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);
@@ -2607,15 +2123,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 */
@@ -2675,12 +2191,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) {
@@ -2698,7 +2214,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 */
@@ -2772,10 +2288,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);
@@ -2793,7 +2309,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);
}
}
}
@@ -2842,7 +2358,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);
}
}
@@ -2988,18 +2504,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);
@@ -3048,7 +2562,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) {
@@ -3083,7 +2597,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);
@@ -3103,7 +2617,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 */
@@ -3123,10 +2637,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 */
@@ -3148,7 +2662,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);
@@ -3171,10 +2685,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);
}
}
}
@@ -3190,7 +2704,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;
@@ -3205,10 +2719,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);
}
}
}
@@ -3227,7 +2741,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);
}
@@ -3237,7 +2751,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);
@@ -3285,7 +2799,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);
@@ -3297,7 +2811,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);
}
@@ -3353,10 +2867,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);
@@ -3382,10 +2896,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) {
@@ -3692,10 +3206,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);
@@ -3726,7 +3240,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);
}
}
}
@@ -3734,14 +3248,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);
}
}
}
@@ -3749,29 +3263,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);
}
}
}
@@ -3785,14 +3286,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. */
@@ -3810,12 +3311,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) {
@@ -3830,10 +3331,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 */
@@ -3850,21 +3351,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 *)
@@ -3920,7 +3411,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;
@@ -4125,12 +3616,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);
@@ -4165,9 +3661,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;
@@ -4207,9 +3700,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;
@@ -4246,6 +3736,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);
@@ -4404,7 +3898,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/blenloader/tests/blendfile_loading_base_test.cc b/source/blender/blenloader/tests/blendfile_loading_base_test.cc
index d74bab4b31c..c743e6bcd3f 100644
--- a/source/blender/blenloader/tests/blendfile_loading_base_test.cc
+++ b/source/blender/blenloader/tests/blendfile_loading_base_test.cc
@@ -148,7 +148,7 @@ void BlendfileLoadingBaseTest::depsgraph_create(eEvaluationMode depsgraph_evalua
{
depsgraph = DEG_graph_new(
bfile->main, bfile->curscene, bfile->cur_view_layer, depsgraph_evaluation_mode);
- DEG_graph_build_from_view_layer(depsgraph, bfile->main, bfile->curscene, bfile->cur_view_layer);
+ DEG_graph_build_from_view_layer(depsgraph);
BKE_scene_graph_update_tagged(depsgraph, bfile->main);
}
diff --git a/source/blender/blenloader/tests/blendfile_loading_base_test.h b/source/blender/blenloader/tests/blendfile_loading_base_test.h
index a5e75ef6df8..f90e07218fb 100644
--- a/source/blender/blenloader/tests/blendfile_loading_base_test.h
+++ b/source/blender/blenloader/tests/blendfile_loading_base_test.h
@@ -56,9 +56,9 @@ class BlendfileLoadingBaseTest : public testing::Test {
void blendfile_free();
/* Create a depsgraph. Assumes a blend file has been loaded to this->bfile. */
- void depsgraph_create(eEvaluationMode depsgraph_evaluation_mode);
+ virtual void depsgraph_create(eEvaluationMode depsgraph_evaluation_mode);
/* Free the depsgraph if it's not nullptr. */
- void depsgraph_free();
+ virtual void depsgraph_free();
};
#endif /* __BLENDFILE_LOADING_BASE_TEST_H__ */
diff --git a/source/blender/blentranslation/intern/blt_lang.c b/source/blender/blentranslation/intern/blt_lang.c
index bcbffe56636..078ded7e5c2 100644
--- a/source/blender/blentranslation/intern/blt_lang.c
+++ b/source/blender/blentranslation/intern/blt_lang.c
@@ -205,11 +205,11 @@ void BLT_lang_init(void)
const char *const messagepath = BKE_appdir_folder_id(BLENDER_DATAFILES, "locale");
#endif
- /* Make sure LANG is correct and wouldn't cause std::rumtime_error. */
+ /* Make sure LANG is correct and wouldn't cause #std::rumtime_error. */
#ifndef _WIN32
/* TODO(sergey): This code only ensures LANG is set properly, so later when
- * Cycles will try to use file system API from boost there'll be no runtime
- * exception generated by std::locale() which _requires_ having proper LANG
+ * Cycles will try to use file system API from boost there will be no runtime
+ * exception generated by #std::locale() which _requires_ having proper LANG
* set in the environment.
*
* Ideally we also need to ensure LC_ALL, LC_MESSAGES and others are also
@@ -221,7 +221,7 @@ void BLT_lang_init(void)
const char *lang = BLI_getenv("LANG");
if (lang != NULL) {
char *old_locale = setlocale(LC_ALL, NULL);
- /* Make a copy so subsequenct setlocale() doesn't interfere. */
+ /* Make a copy so subsequent #setlocale() doesn't interfere. */
old_locale = BLI_strdup(old_locale);
if (setlocale(LC_ALL, lang) == NULL) {
setenv("LANG", "C", 1);
@@ -267,7 +267,7 @@ void BLT_lang_set(const char *str)
/* We want to avoid locales like '.UTF-8'! */
if (short_locale[0]) {
- /* Hurrey! encoding needs to be placed *before* variant! */
+ /* Hooray! Encoding needs to be placed *before* variant! */
char *variant = strchr(short_locale, '@');
if (variant) {
char *locale = BLI_strdupn(short_locale, variant - short_locale);
@@ -311,12 +311,14 @@ const char *BLT_lang_get(void)
#undef LOCALE
#undef ULANGUAGE
-/* Get locale's elements (if relevant pointer is not NULL and element actually exists, e.g.
+/**
+ * Get locale's elements (if relevant pointer is not NULL and element actually exists, e.g.
* if there is no variant,
* *variant and *language_variant will always be NULL).
* Non-null elements are always MEM_mallocN'ed, it's the caller's responsibility to free them.
- * NOTE: Keep that one always available, you never know,
- * may become useful even in no-WITH_INTERNATIONAL context...
+ *
+ * \note Keep that one always available, you never know,
+ * may become useful even in no #WITH_INTERNATIONAL context.
*/
void BLT_lang_locale_explode(const char *locale,
char **language,
@@ -378,7 +380,8 @@ void BLT_lang_locale_explode(const char *locale,
}
}
-/* Test if the translation context allows IME input - used to
+/**
+ * Test if the translation context allows IME input - used to
* avoid weird character drawing if IME inputs non-ascii chars.
*/
static void blt_lang_check_ime_supported(void)
diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt
index b97b5cc95f2..d2b747aa68f 100644
--- a/source/blender/bmesh/CMakeLists.txt
+++ b/source/blender/bmesh/CMakeLists.txt
@@ -23,6 +23,7 @@ set(INC
../blenkernel
../blenlib
../blentranslation
+ ../depsgraph
../makesdna
../../../intern/atomic
../../../intern/eigen
@@ -137,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
@@ -153,10 +156,10 @@ set(SRC
tools/bmesh_path.h
tools/bmesh_path_region.c
tools/bmesh_path_region.h
- tools/bmesh_path_uv.c
- tools/bmesh_path_uv.h
tools/bmesh_path_region_uv.c
tools/bmesh_path_region_uv.h
+ tools/bmesh_path_uv.c
+ tools/bmesh_path_uv.h
tools/bmesh_region_match.c
tools/bmesh_region_match.h
tools/bmesh_separate.c
@@ -203,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/intern/bmesh_mesh_convert.c b/source/blender/bmesh/intern/bmesh_mesh_convert.c
index 8db125970fd..4671df90d53 100644
--- a/source/blender/bmesh/intern/bmesh_mesh_convert.c
+++ b/source/blender/bmesh/intern/bmesh_mesh_convert.c
@@ -90,6 +90,8 @@
#include "BKE_key.h"
#include "BKE_main.h"
+#include "DEG_depsgraph_query.h"
+
#include "bmesh.h"
#include "intern/bmesh_private.h" /* For element checking. */
@@ -231,7 +233,13 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
/* -------------------------------------------------------------------- */
/* Shape Key */
- int tot_shape_keys = me->key ? BLI_listbase_count(&me->key->block) : 0;
+ int tot_shape_keys = 0;
+ if (me->key != NULL && DEG_is_original_id(&me->id)) {
+ /* Evaluated meshes can be topologically inconsistent with their shape keys.
+ * Shape keys are also already integrated into the state of the evaluated
+ * mesh, so considering them here would kind of apply them twice. */
+ tot_shape_keys = BLI_listbase_count(&me->key->block);
+ }
if (is_new == false) {
tot_shape_keys = min_ii(tot_shape_keys, CustomData_number_of_layers(&bm->vdata, CD_SHAPEKEY));
}
@@ -239,7 +247,7 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
BLI_array_alloca(shape_key_table, tot_shape_keys) :
NULL;
- if ((params->active_shapekey != 0) && (me->key != NULL)) {
+ if ((params->active_shapekey != 0) && tot_shape_keys > 0) {
actkey = BLI_findlink(&me->key->block, params->active_shapekey - 1);
}
else {
@@ -298,7 +306,8 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
const int cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT);
const int cd_edge_bweight_offset = CustomData_get_offset(&bm->edata, CD_BWEIGHT);
const int cd_edge_crease_offset = CustomData_get_offset(&bm->edata, CD_CREASE);
- const int cd_shape_key_offset = me->key ? CustomData_get_offset(&bm->vdata, CD_SHAPEKEY) : -1;
+ const int cd_shape_key_offset = tot_shape_keys ? CustomData_get_offset(&bm->vdata, CD_SHAPEKEY) :
+ -1;
const int cd_shape_keyindex_offset = is_new && (tot_shape_keys || params->add_key_index) ?
CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX) :
-1;
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/gpu/intern/gpu_shader_private.h b/source/blender/bmesh/tools/bmesh_boolean.h
index 0f89fbda737..d1b4fb2509c 100644
--- a/source/blender/gpu/intern/gpu_shader_private.h
+++ b/source/blender/bmesh/tools/bmesh_boolean.h
@@ -14,40 +14,31 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-/** \file
- * \ingroup gpu
- */
-
#pragma once
-#include "GPU_shader_interface.h"
+/** \file
+ * \ingroup bmesh
+ */
#ifdef __cplusplus
extern "C" {
#endif
-struct GPUShader {
- /** Handle for full program (links shader stages below). */
- GLuint program;
-
- /** Handle for vertex shader. */
- GLuint vertex;
- /** Handle for geometry shader. */
- GLuint geometry;
- /** Handle for fragment shader. */
- GLuint fragment;
-
- /** Cached uniform & attribute interface for shader. */
- GPUShaderInterface *interface;
-
- int feedback_transform_type;
-#ifndef NDEBUG
- char name[64];
-#endif
-};
-
-/* XXX do not use it. Special hack to use OCIO with batch API. */
-GPUShader *immGetShader(void);
+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
}
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/compositor/COM_compositor.h b/source/blender/compositor/COM_compositor.h
index b200fa8d266..79c1ebcfe9f 100644
--- a/source/blender/compositor/COM_compositor.h
+++ b/source/blender/compositor/COM_compositor.h
@@ -362,4 +362,3 @@ void COM_deinitialize(void);
#ifdef __cplusplus
}
#endif
-
diff --git a/source/blender/depsgraph/CMakeLists.txt b/source/blender/depsgraph/CMakeLists.txt
index 417aaa2c4c0..e0916491edb 100644
--- a/source/blender/depsgraph/CMakeLists.txt
+++ b/source/blender/depsgraph/CMakeLists.txt
@@ -55,6 +55,7 @@ set(SRC
intern/builder/deg_builder_rna.cc
intern/builder/deg_builder_transitive.cc
intern/builder/pipeline.cc
+ intern/builder/pipeline_all_objects.cc
intern/builder/pipeline_compositor.cc
intern/builder/pipeline_from_ids.cc
intern/builder/pipeline_render.cc
@@ -106,16 +107,17 @@ set(SRC
intern/builder/deg_builder.h
intern/builder/deg_builder_cache.h
intern/builder/deg_builder_cycle.h
- intern/builder/deg_builder_relations_drivers.h
intern/builder/deg_builder_map.h
intern/builder/deg_builder_nodes.h
intern/builder/deg_builder_pchanmap.h
intern/builder/deg_builder_relations.h
+ intern/builder/deg_builder_relations_drivers.h
intern/builder/deg_builder_relations_impl.h
intern/builder/deg_builder_remove_noop.h
intern/builder/deg_builder_rna.h
intern/builder/deg_builder_transitive.h
intern/builder/pipeline.h
+ intern/builder/pipeline_all_objects.h
intern/builder/pipeline_compositor.h
intern/builder/pipeline_from_ids.h
intern/builder/pipeline_render.h
diff --git a/source/blender/depsgraph/DEG_depsgraph.h b/source/blender/depsgraph/DEG_depsgraph.h
index b3636743101..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. */
@@ -149,18 +155,11 @@ void DEG_ids_check_recalc(struct Main *bmain,
/* Graph Evaluation ----------------------------- */
-/* Frame changed recalculation entry point
- * < context_type: context to perform evaluation for
- * < ctime: (frame) new frame to evaluate values on
- */
-void DEG_evaluate_on_framechange(struct Main *bmain, Depsgraph *graph, float ctime);
-
-/* Data changed recalculation entry point.
- * < context_type: context to perform evaluation for
- */
-void DEG_evaluate_on_refresh(struct Main *bmain, Depsgraph *graph);
+/* Frame changed recalculation entry point. */
+void DEG_evaluate_on_framechange(Depsgraph *graph, float ctime);
-bool DEG_needs_eval(Depsgraph *graph);
+/* Data changed recalculation entry point. */
+void DEG_evaluate_on_refresh(Depsgraph *graph);
/* Editors Integration -------------------------- */
diff --git a/source/blender/depsgraph/DEG_depsgraph_build.h b/source/blender/depsgraph/DEG_depsgraph_build.h
index 50f22b00028..2147a584765 100644
--- a/source/blender/depsgraph/DEG_depsgraph_build.h
+++ b/source/blender/depsgraph/DEG_depsgraph_build.h
@@ -51,44 +51,29 @@ extern "C" {
/* Graph Building -------------------------------- */
/* Build depsgraph for the given scene, and dump results in given graph container. */
-void DEG_graph_build_from_view_layer(struct Depsgraph *graph,
- struct Main *bmain,
- struct Scene *scene,
- struct ViewLayer *view_layer);
+void DEG_graph_build_from_view_layer(struct Depsgraph *graph);
+
+/* Build depsgraph for all objects (so also invisible ones) in the given view layer. */
+void DEG_graph_build_for_all_objects(struct Depsgraph *graph);
/* Special version of builder which produces dependency graph suitable for the render pipeline.
* It will contain sequencer and compositor (if needed) and all their dependencies. */
-void DEG_graph_build_for_render_pipeline(struct Depsgraph *graph,
- struct Main *bmain,
- struct Scene *scene,
- struct ViewLayer *view_layer);
+void DEG_graph_build_for_render_pipeline(struct Depsgraph *graph);
/* Builds minimal dependency graph for compositor preview.
*
* Note that compositor editor might have pinned node tree, which is different from scene's node
* tree.
*/
-void DEG_graph_build_for_compositor_preview(struct Depsgraph *graph,
- struct Main *bmain,
- struct Scene *scene,
- struct ViewLayer *view_layer,
- struct bNodeTree *nodetree);
-
-void DEG_graph_build_from_ids(struct Depsgraph *graph,
- struct Main *bmain,
- struct Scene *scene,
- struct ViewLayer *view_layer,
- struct ID **ids,
- const int num_ids);
+void DEG_graph_build_for_compositor_preview(struct Depsgraph *graph, struct bNodeTree *nodetree);
+
+void DEG_graph_build_from_ids(struct Depsgraph *graph, struct ID **ids, const int num_ids);
/* Tag relations from the given graph for update. */
void DEG_graph_tag_relations_update(struct Depsgraph *graph);
/* Create or update relations in the specified graph. */
-void DEG_graph_relations_update(struct Depsgraph *graph,
- struct Main *bmain,
- struct Scene *scene,
- struct ViewLayer *view_layer);
+void DEG_graph_relations_update(struct Depsgraph *graph);
/* Tag all relations in the database for update.*/
void DEG_relations_tag_update(struct Main *bmain);
diff --git a/source/blender/depsgraph/DEG_depsgraph_query.h b/source/blender/depsgraph/DEG_depsgraph_query.h
index e0166a13d69..7eb5f1ccec1 100644
--- a/source/blender/depsgraph/DEG_depsgraph_query.h
+++ b/source/blender/depsgraph/DEG_depsgraph_query.h
@@ -55,6 +55,9 @@ struct Scene *DEG_get_input_scene(const Depsgraph *graph);
/* Get view layer that depsgraph was built for. */
struct ViewLayer *DEG_get_input_view_layer(const Depsgraph *graph);
+/* Get bmain that depsgraph was built for. */
+struct Main *DEG_get_bmain(const Depsgraph *graph);
+
/* Get evaluation mode that depsgraph was built for. */
eEvaluationMode DEG_get_mode(const Depsgraph *graph);
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
index e262c880421..6776f4b7b83 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) {
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 37b23833e00..14f9db767a9 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
@@ -978,13 +978,13 @@ void DepsgraphRelationBuilder::build_object_parent(Object *object)
break;
}
}
- /* Metaballs are the odd balls here (no pun intended): they will request
+ /* Meta-balls are the odd balls here (no pun intended): they will request
* instance-list (formerly known as dupli-list) during evaluation. This is
* their way of interacting with all instanced surfaces, making a nice
* effect when is used form particle system. */
if (object->type == OB_MBALL && parent->transflag & OB_DUPLI) {
ComponentKey parent_geometry_key(parent_id, NodeType::GEOMETRY);
- /* NOTE: Metaballs are evaluating geometry only after their transform,
+ /* NOTE: Meta-balls are evaluating geometry only after their transform,
* so we only hook up to transform channel here. */
add_relation(parent_geometry_key, object_transform_key, "Parent");
}
@@ -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,29 @@ 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 (object->rigidbody_object->type == RBO_TYPE_ACTIVE) {
+ /* 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).
+ * But we can't update the depgraph when the animated property in changed during playback.
+ * So always assume that active bodies needs updating.
+ */
+ 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 +2663,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 +2689,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/builder/pipeline.cc b/source/blender/depsgraph/intern/builder/pipeline.cc
index d6893ba11d8..b13077e4792 100644
--- a/source/blender/depsgraph/intern/builder/pipeline.cc
+++ b/source/blender/depsgraph/intern/builder/pipeline.cc
@@ -33,14 +33,11 @@
namespace blender {
namespace deg {
-AbstractBuilderPipeline::AbstractBuilderPipeline(::Depsgraph *graph,
- Main *bmain,
- Scene *scene,
- ViewLayer *view_layer)
+AbstractBuilderPipeline::AbstractBuilderPipeline(::Depsgraph *graph)
: deg_graph_(reinterpret_cast<Depsgraph *>(graph)),
- bmain_(bmain),
- scene_(scene),
- view_layer_(view_layer),
+ bmain_(deg_graph_->bmain),
+ scene_(deg_graph_->scene),
+ view_layer_(deg_graph_->view_layer),
builder_cache_()
{
}
diff --git a/source/blender/depsgraph/intern/builder/pipeline.h b/source/blender/depsgraph/intern/builder/pipeline.h
index 2c9c78bb2cb..d98d834932c 100644
--- a/source/blender/depsgraph/intern/builder/pipeline.h
+++ b/source/blender/depsgraph/intern/builder/pipeline.h
@@ -49,7 +49,7 @@ class DepsgraphRelationBuilder;
*/
class AbstractBuilderPipeline {
public:
- AbstractBuilderPipeline(::Depsgraph *graph, Main *bmain, Scene *scene, ViewLayer *view_layer);
+ AbstractBuilderPipeline(::Depsgraph *graph);
virtual ~AbstractBuilderPipeline();
void build();
diff --git a/source/blender/depsgraph/intern/builder/pipeline_all_objects.cc b/source/blender/depsgraph/intern/builder/pipeline_all_objects.cc
new file mode 100644
index 00000000000..81d239239be
--- /dev/null
+++ b/source/blender/depsgraph/intern/builder/pipeline_all_objects.cc
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+#include "pipeline_all_objects.h"
+
+#include "intern/builder/deg_builder_nodes.h"
+#include "intern/builder/deg_builder_relations.h"
+#include "intern/depsgraph.h"
+
+#include "DNA_layer_types.h"
+
+namespace blender {
+namespace deg {
+
+namespace {
+
+class AllObjectsNodeBuilder : public DepsgraphNodeBuilder {
+ public:
+ AllObjectsNodeBuilder(Main *bmain, Depsgraph *graph, DepsgraphBuilderCache *cache)
+ : DepsgraphNodeBuilder(bmain, graph, cache)
+ {
+ }
+
+ virtual bool need_pull_base_into_graph(Base * /*base*/) override
+ {
+ return true;
+ }
+};
+
+class AllObjectsRelationBuilder : public DepsgraphRelationBuilder {
+ public:
+ AllObjectsRelationBuilder(Main *bmain, Depsgraph *graph, DepsgraphBuilderCache *cache)
+ : DepsgraphRelationBuilder(bmain, graph, cache)
+ {
+ }
+
+ virtual bool need_pull_base_into_graph(Base * /*base*/) override
+ {
+ return true;
+ }
+};
+
+} // namespace
+
+AllObjectsBuilderPipeline::AllObjectsBuilderPipeline(::Depsgraph *graph)
+ : ViewLayerBuilderPipeline(graph)
+{
+}
+
+unique_ptr<DepsgraphNodeBuilder> AllObjectsBuilderPipeline::construct_node_builder()
+{
+ return std::make_unique<AllObjectsNodeBuilder>(bmain_, deg_graph_, &builder_cache_);
+}
+
+unique_ptr<DepsgraphRelationBuilder> AllObjectsBuilderPipeline::construct_relation_builder()
+{
+ return std::make_unique<AllObjectsRelationBuilder>(bmain_, deg_graph_, &builder_cache_);
+}
+
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/builder/pipeline_all_objects.h b/source/blender/depsgraph/intern/builder/pipeline_all_objects.h
new file mode 100644
index 00000000000..11ca2314331
--- /dev/null
+++ b/source/blender/depsgraph/intern/builder/pipeline_all_objects.h
@@ -0,0 +1,44 @@
+/*
+ * 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 depsgraph
+ */
+
+#pragma once
+
+#include "pipeline_view_layer.h"
+
+namespace blender {
+namespace deg {
+
+/* Builds a dependency graph that contains all objects in the view layer.
+ * This is contrary to the regular ViewLayerBuilderPipeline, which is limited to visible objects
+ * (and their dependencies). */
+class AllObjectsBuilderPipeline : public ViewLayerBuilderPipeline {
+ public:
+ AllObjectsBuilderPipeline(::Depsgraph *graph);
+
+ protected:
+ virtual unique_ptr<DepsgraphNodeBuilder> construct_node_builder() override;
+ virtual unique_ptr<DepsgraphRelationBuilder> construct_relation_builder() override;
+};
+
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/builder/pipeline_compositor.cc b/source/blender/depsgraph/intern/builder/pipeline_compositor.cc
index 3e56f17fc7e..2b9922851c1 100644
--- a/source/blender/depsgraph/intern/builder/pipeline_compositor.cc
+++ b/source/blender/depsgraph/intern/builder/pipeline_compositor.cc
@@ -26,9 +26,8 @@
namespace blender {
namespace deg {
-CompositorBuilderPipeline::CompositorBuilderPipeline(
- ::Depsgraph *graph, Main *bmain, Scene *scene, ViewLayer *view_layer, bNodeTree *nodetree)
- : AbstractBuilderPipeline(graph, bmain, scene, view_layer), nodetree_(nodetree)
+CompositorBuilderPipeline::CompositorBuilderPipeline(::Depsgraph *graph, bNodeTree *nodetree)
+ : AbstractBuilderPipeline(graph), nodetree_(nodetree)
{
deg_graph_->is_render_pipeline_depsgraph = true;
}
diff --git a/source/blender/depsgraph/intern/builder/pipeline_compositor.h b/source/blender/depsgraph/intern/builder/pipeline_compositor.h
index 892ece7c2a4..46f1e3694d3 100644
--- a/source/blender/depsgraph/intern/builder/pipeline_compositor.h
+++ b/source/blender/depsgraph/intern/builder/pipeline_compositor.h
@@ -32,8 +32,7 @@ namespace deg {
class CompositorBuilderPipeline : public AbstractBuilderPipeline {
public:
- CompositorBuilderPipeline(
- ::Depsgraph *graph, Main *bmain, Scene *scene, ViewLayer *view_layer, bNodeTree *nodetree);
+ CompositorBuilderPipeline(::Depsgraph *graph, bNodeTree *nodetree);
protected:
virtual void build_nodes(DepsgraphNodeBuilder &node_builder) override;
diff --git a/source/blender/depsgraph/intern/builder/pipeline_from_ids.cc b/source/blender/depsgraph/intern/builder/pipeline_from_ids.cc
index e44f554f197..87cfeb46693 100644
--- a/source/blender/depsgraph/intern/builder/pipeline_from_ids.cc
+++ b/source/blender/depsgraph/intern/builder/pipeline_from_ids.cc
@@ -32,11 +32,9 @@ namespace {
class DepsgraphFromIDsFilter {
public:
- DepsgraphFromIDsFilter(ID **ids, const int num_ids)
+ DepsgraphFromIDsFilter(Span<ID *> ids)
{
- for (int i = 0; i < num_ids; ++i) {
- ids_.add(ids[i]);
- }
+ ids_.add_multiple(ids);
}
bool contains(ID *id)
@@ -50,9 +48,11 @@ class DepsgraphFromIDsFilter {
class DepsgraphFromIDsNodeBuilder : public DepsgraphNodeBuilder {
public:
- DepsgraphFromIDsNodeBuilder(
- Main *bmain, Depsgraph *graph, DepsgraphBuilderCache *cache, ID **ids, const int num_ids)
- : DepsgraphNodeBuilder(bmain, graph, cache), filter_(ids, num_ids)
+ DepsgraphFromIDsNodeBuilder(Main *bmain,
+ Depsgraph *graph,
+ DepsgraphBuilderCache *cache,
+ Span<ID *> ids)
+ : DepsgraphNodeBuilder(bmain, graph, cache), filter_(ids)
{
}
@@ -81,9 +81,11 @@ class DepsgraphFromIDsNodeBuilder : public DepsgraphNodeBuilder {
class DepsgraphFromIDsRelationBuilder : public DepsgraphRelationBuilder {
public:
- DepsgraphFromIDsRelationBuilder(
- Main *bmain, Depsgraph *graph, DepsgraphBuilderCache *cache, ID **ids, const int num_ids)
- : DepsgraphRelationBuilder(bmain, graph, cache), filter_(ids, num_ids)
+ DepsgraphFromIDsRelationBuilder(Main *bmain,
+ Depsgraph *graph,
+ DepsgraphBuilderCache *cache,
+ Span<ID *> ids)
+ : DepsgraphRelationBuilder(bmain, graph, cache), filter_(ids)
{
}
@@ -112,41 +114,35 @@ class DepsgraphFromIDsRelationBuilder : public DepsgraphRelationBuilder {
} // namespace
-FromIDsBuilderPipeline::FromIDsBuilderPipeline(::Depsgraph *graph,
- Main *bmain,
- Scene *scene,
- ViewLayer *view_layer,
- ID **ids,
- const int num_ids)
- : AbstractBuilderPipeline(graph, bmain, scene, view_layer), ids_(ids), num_ids_(num_ids)
+FromIDsBuilderPipeline::FromIDsBuilderPipeline(::Depsgraph *graph, Span<ID *> ids)
+ : AbstractBuilderPipeline(graph), ids_(ids)
{
}
unique_ptr<DepsgraphNodeBuilder> FromIDsBuilderPipeline::construct_node_builder()
{
- return std::make_unique<DepsgraphFromIDsNodeBuilder>(
- bmain_, deg_graph_, &builder_cache_, ids_, num_ids_);
+ return std::make_unique<DepsgraphFromIDsNodeBuilder>(bmain_, deg_graph_, &builder_cache_, ids_);
}
unique_ptr<DepsgraphRelationBuilder> FromIDsBuilderPipeline::construct_relation_builder()
{
return std::make_unique<DepsgraphFromIDsRelationBuilder>(
- bmain_, deg_graph_, &builder_cache_, ids_, num_ids_);
+ bmain_, deg_graph_, &builder_cache_, ids_);
}
void FromIDsBuilderPipeline::build_nodes(DepsgraphNodeBuilder &node_builder)
{
node_builder.build_view_layer(scene_, view_layer_, DEG_ID_LINKED_DIRECTLY);
- for (int i = 0; i < num_ids_; ++i) {
- node_builder.build_id(ids_[i]);
+ for (ID *id : ids_) {
+ node_builder.build_id(id);
}
}
void FromIDsBuilderPipeline::build_relations(DepsgraphRelationBuilder &relation_builder)
{
relation_builder.build_view_layer(scene_, view_layer_, DEG_ID_LINKED_DIRECTLY);
- for (int i = 0; i < num_ids_; ++i) {
- relation_builder.build_id(ids_[i]);
+ for (ID *id : ids_) {
+ relation_builder.build_id(id);
}
}
diff --git a/source/blender/depsgraph/intern/builder/pipeline_from_ids.h b/source/blender/depsgraph/intern/builder/pipeline_from_ids.h
index 4a507f2c728..79fcfc52446 100644
--- a/source/blender/depsgraph/intern/builder/pipeline_from_ids.h
+++ b/source/blender/depsgraph/intern/builder/pipeline_from_ids.h
@@ -43,8 +43,7 @@ namespace deg {
class FromIDsBuilderPipeline : public AbstractBuilderPipeline {
public:
- FromIDsBuilderPipeline(
- ::Depsgraph *graph, Main *bmain, Scene *scene, ViewLayer *view_layer, ID **ids, int num_ids);
+ FromIDsBuilderPipeline(::Depsgraph *graph, Span<ID *> ids);
protected:
virtual unique_ptr<DepsgraphNodeBuilder> construct_node_builder() override;
@@ -54,8 +53,7 @@ class FromIDsBuilderPipeline : public AbstractBuilderPipeline {
virtual void build_relations(DepsgraphRelationBuilder &relation_builder) override;
private:
- ID **ids_;
- const int num_ids_;
+ Span<ID *> ids_;
};
} // namespace deg
diff --git a/source/blender/depsgraph/intern/builder/pipeline_render.cc b/source/blender/depsgraph/intern/builder/pipeline_render.cc
index 50a37d0d3e4..3b065f7f1bc 100644
--- a/source/blender/depsgraph/intern/builder/pipeline_render.cc
+++ b/source/blender/depsgraph/intern/builder/pipeline_render.cc
@@ -26,11 +26,7 @@
namespace blender {
namespace deg {
-RenderBuilderPipeline::RenderBuilderPipeline(::Depsgraph *graph,
- Main *bmain,
- Scene *scene,
- ViewLayer *view_layer)
- : AbstractBuilderPipeline(graph, bmain, scene, view_layer)
+RenderBuilderPipeline::RenderBuilderPipeline(::Depsgraph *graph) : AbstractBuilderPipeline(graph)
{
deg_graph_->is_render_pipeline_depsgraph = true;
}
diff --git a/source/blender/depsgraph/intern/builder/pipeline_render.h b/source/blender/depsgraph/intern/builder/pipeline_render.h
index df7f9e0de68..91a4be137b6 100644
--- a/source/blender/depsgraph/intern/builder/pipeline_render.h
+++ b/source/blender/depsgraph/intern/builder/pipeline_render.h
@@ -30,7 +30,7 @@ namespace deg {
class RenderBuilderPipeline : public AbstractBuilderPipeline {
public:
- RenderBuilderPipeline(::Depsgraph *graph, Main *bmain, Scene *scene, ViewLayer *view_layer);
+ RenderBuilderPipeline(::Depsgraph *graph);
protected:
virtual void build_nodes(DepsgraphNodeBuilder &node_builder) override;
diff --git a/source/blender/depsgraph/intern/builder/pipeline_view_layer.cc b/source/blender/depsgraph/intern/builder/pipeline_view_layer.cc
index 3223f17f349..f1852a40a10 100644
--- a/source/blender/depsgraph/intern/builder/pipeline_view_layer.cc
+++ b/source/blender/depsgraph/intern/builder/pipeline_view_layer.cc
@@ -26,11 +26,8 @@
namespace blender {
namespace deg {
-ViewLayerBuilderPipeline::ViewLayerBuilderPipeline(::Depsgraph *graph,
- Main *bmain,
- Scene *scene,
- ViewLayer *view_layer)
- : AbstractBuilderPipeline(graph, bmain, scene, view_layer)
+ViewLayerBuilderPipeline::ViewLayerBuilderPipeline(::Depsgraph *graph)
+ : AbstractBuilderPipeline(graph)
{
}
diff --git a/source/blender/depsgraph/intern/builder/pipeline_view_layer.h b/source/blender/depsgraph/intern/builder/pipeline_view_layer.h
index fbd7b98acad..aa85dd7a47b 100644
--- a/source/blender/depsgraph/intern/builder/pipeline_view_layer.h
+++ b/source/blender/depsgraph/intern/builder/pipeline_view_layer.h
@@ -30,7 +30,7 @@ namespace deg {
class ViewLayerBuilderPipeline : public AbstractBuilderPipeline {
public:
- ViewLayerBuilderPipeline(::Depsgraph *graph, Main *bmain, Scene *scene, ViewLayer *view_layer);
+ ViewLayerBuilderPipeline(::Depsgraph *graph);
protected:
virtual void build_nodes(DepsgraphNodeBuilder &node_builder) override;
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_build.cc b/source/blender/depsgraph/intern/depsgraph_build.cc
index fb933cb38f3..96c17ae4dc5 100644
--- a/source/blender/depsgraph/intern/depsgraph_build.cc
+++ b/source/blender/depsgraph/intern/depsgraph_build.cc
@@ -44,6 +44,7 @@
#include "DEG_depsgraph_debug.h"
#include "builder/deg_builder_relations.h"
+#include "builder/pipeline_all_objects.h"
#include "builder/pipeline_compositor.h"
#include "builder/pipeline_from_ids.h"
#include "builder/pipeline_render.h"
@@ -209,39 +210,33 @@ struct Depsgraph *DEG_get_graph_from_handle(struct DepsNodeHandle *node_handle)
/* Graph Building API's */
/* Build depsgraph for the given scene layer, and dump results in given graph container. */
-void DEG_graph_build_from_view_layer(Depsgraph *graph,
- Main *bmain,
- Scene *scene,
- ViewLayer *view_layer)
+void DEG_graph_build_from_view_layer(Depsgraph *graph)
{
- deg::ViewLayerBuilderPipeline builder(graph, bmain, scene, view_layer);
+ deg::ViewLayerBuilderPipeline builder(graph);
builder.build();
}
-void DEG_graph_build_for_render_pipeline(Depsgraph *graph,
- Main *bmain,
- Scene *scene,
- ViewLayer *view_layer)
+void DEG_graph_build_for_all_objects(struct Depsgraph *graph)
{
- deg::RenderBuilderPipeline builder(graph, bmain, scene, view_layer);
+ deg::AllObjectsBuilderPipeline builder(graph);
builder.build();
}
-void DEG_graph_build_for_compositor_preview(
- Depsgraph *graph, Main *bmain, Scene *scene, struct ViewLayer *view_layer, bNodeTree *nodetree)
+void DEG_graph_build_for_render_pipeline(Depsgraph *graph)
{
- deg::CompositorBuilderPipeline builder(graph, bmain, scene, view_layer, nodetree);
+ deg::RenderBuilderPipeline builder(graph);
builder.build();
}
-void DEG_graph_build_from_ids(Depsgraph *graph,
- Main *bmain,
- Scene *scene,
- ViewLayer *view_layer,
- ID **ids,
- const int num_ids)
+void DEG_graph_build_for_compositor_preview(Depsgraph *graph, bNodeTree *nodetree)
{
- deg::FromIDsBuilderPipeline builder(graph, bmain, scene, view_layer, ids, num_ids);
+ deg::CompositorBuilderPipeline builder(graph, nodetree);
+ builder.build();
+}
+
+void DEG_graph_build_from_ids(Depsgraph *graph, ID **ids, const int num_ids)
+{
+ deg::FromIDsBuilderPipeline builder(graph, blender::Span(ids, num_ids));
builder.build();
}
@@ -264,14 +259,14 @@ void DEG_graph_tag_relations_update(Depsgraph *graph)
}
/* Create or update relations in the specified graph. */
-void DEG_graph_relations_update(Depsgraph *graph, Main *bmain, Scene *scene, ViewLayer *view_layer)
+void DEG_graph_relations_update(Depsgraph *graph)
{
deg::Depsgraph *deg_graph = (deg::Depsgraph *)graph;
if (!deg_graph->need_update) {
/* Graph is up to date, nothing to do. */
return;
}
- DEG_graph_build_from_view_layer(graph, bmain, scene, view_layer);
+ DEG_graph_build_from_view_layer(graph);
}
/* Tag all relations for update. */
diff --git a/source/blender/depsgraph/intern/depsgraph_debug.cc b/source/blender/depsgraph/intern/depsgraph_debug.cc
index 0763738ff59..c5e306f3148 100644
--- a/source/blender/depsgraph/intern/depsgraph_debug.cc
+++ b/source/blender/depsgraph/intern/depsgraph_debug.cc
@@ -93,7 +93,7 @@ bool DEG_debug_graph_relations_validate(Depsgraph *graph,
{
Depsgraph *temp_depsgraph = DEG_graph_new(bmain, scene, view_layer, DEG_get_mode(graph));
bool valid = true;
- DEG_graph_build_from_view_layer(temp_depsgraph, bmain, scene, view_layer);
+ DEG_graph_build_from_view_layer(temp_depsgraph);
if (!DEG_debug_compare(temp_depsgraph, graph)) {
fprintf(stderr, "ERROR! Depsgraph wasn't tagged for update when it should have!\n");
BLI_assert(!"This should not happen!");
diff --git a/source/blender/depsgraph/intern/depsgraph_eval.cc b/source/blender/depsgraph/intern/depsgraph_eval.cc
index 8a641f23a42..1ad3fdbc9da 100644
--- a/source/blender/depsgraph/intern/depsgraph_eval.cc
+++ b/source/blender/depsgraph/intern/depsgraph_eval.cc
@@ -47,44 +47,37 @@
namespace deg = blender::deg;
-/* Evaluate all nodes tagged for updating. */
-void DEG_evaluate_on_refresh(Main *bmain, 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 on primary timesource. */
- deg::TimeSourceNode *tsrc = deg_graph->find_time_source();
- tsrc->cfra = deg_graph->ctime;
- /* 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(bmain, deg_graph);
+
+ 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(Main *bmain, 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;
- /* Update time on primary timesource. */
- deg::TimeSourceNode *tsrc = deg_graph->find_time_source();
- tsrc->cfra = ctime;
- deg_graph->need_update_time = true;
- deg::deg_graph_flush_updates(bmain, 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_query.cc b/source/blender/depsgraph/intern/depsgraph_query.cc
index 0b6014c18f1..fc9ed9a6ab9 100644
--- a/source/blender/depsgraph/intern/depsgraph_query.cc
+++ b/source/blender/depsgraph/intern/depsgraph_query.cc
@@ -61,6 +61,12 @@ struct ViewLayer *DEG_get_input_view_layer(const Depsgraph *graph)
return deg_graph->view_layer;
}
+struct Main *DEG_get_bmain(const Depsgraph *graph)
+{
+ const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph);
+ return deg_graph->bmain;
+}
+
eEvaluationMode DEG_get_mode(const Depsgraph *graph)
{
const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph);
diff --git a/source/blender/depsgraph/intern/depsgraph_query_iter.cc b/source/blender/depsgraph/intern/depsgraph_query_iter.cc
index 048c0125f53..7d47e1fb541 100644
--- a/source/blender/depsgraph/intern/depsgraph_query_iter.cc
+++ b/source/blender/depsgraph/intern/depsgraph_query_iter.cc
@@ -106,8 +106,8 @@ bool deg_object_hide_original(eEvaluationMode eval_mode, Object *ob, DupliObject
* visible otherwise. The better solution eventually would be for objects
* to specify which object they instance, instead of through parenting.
*
- * This function should not be used for metaballs. They have custom visibility rules, as hiding
- * the base metaball will also hide all the other balls in the group. */
+ * This function should not be used for meta-balls. They have custom visibility rules, as hiding
+ * the base meta-ball will also hide all the other balls in the group. */
if (eval_mode == DAG_EVAL_RENDER || dob) {
const int hide_original_types = OB_DUPLIVERTS | OB_DUPLIFACES;
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.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc
index 1ede2cf914a..2e0487bfca1 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval.cc
@@ -86,8 +86,8 @@ enum class EvaluationStage {
/* Workaround for areas which can not be evaluated in threads.
*
- * For example, metaballs, which are iterating over all bases and are requesting dupli-lists
- * to see whether there are metaballs inside. */
+ * For example, meta-balls, which are iterating over all bases and are requesting dupli-lists
+ * to see whether there are meta-balls inside. */
SINGLE_THREADED_WORKAROUND,
};
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc
index a74ec485d88..5ccdcbec858 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc
@@ -351,19 +351,15 @@ void invalidate_tagged_evaluated_data(Depsgraph *graph)
/* Flush updates from tagged nodes outwards until all affected nodes
* are tagged.
*/
-void deg_graph_flush_updates(Main *bmain, Depsgraph *graph)
+void deg_graph_flush_updates(Depsgraph *graph)
{
/* Sanity checks. */
- BLI_assert(bmain != nullptr);
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;
}
@@ -411,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_flush.h b/source/blender/depsgraph/intern/eval/deg_eval_flush.h
index c76dc9fe01d..1f58c54dbf4 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_flush.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval_flush.h
@@ -35,7 +35,7 @@ struct Depsgraph;
/* Flush updates from tagged nodes outwards until all affected nodes
* are tagged.
*/
-void deg_graph_flush_updates(struct Main *bmain, struct Depsgraph *graph);
+void deg_graph_flush_updates(struct Depsgraph *graph);
/* Clear tags from all operation nodes. */
void deg_graph_clear_tags(struct Depsgraph *graph);
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 364c214b014..79ad92f336f 100644
--- a/source/blender/depsgraph/intern/node/deg_node_time.h
+++ b/source/blender/depsgraph/intern/node/deg_node_time.h
@@ -30,16 +30,14 @@ namespace deg {
/* Time Source Node. */
struct TimeSourceNode : public Node {
- /* New "current time". */
- float cfra;
-
- /* time-offset relative to the "official" time source that this one has. */
- float offset;
+ 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_bloom.c b/source/blender/draw/engines/eevee/eevee_bloom.c
index 8fd953478d5..9b8f7ddab0c 100644
--- a/source/blender/draw/engines/eevee/eevee_bloom.c
+++ b/source/blender/draw/engines/eevee/eevee_bloom.c
@@ -30,45 +30,8 @@
#include "eevee_private.h"
-static struct {
- /* Bloom */
- struct GPUShader *bloom_blit_sh[2];
- struct GPUShader *bloom_downsample_sh[2];
- struct GPUShader *bloom_upsample_sh[2];
- struct GPUShader *bloom_resolve_sh[2];
-} e_data = {{NULL}}; /* Engine data */
-
-extern char datatoc_effect_bloom_frag_glsl[];
-
static const bool use_highres = true;
-static void eevee_create_shader_bloom(void)
-{
- e_data.bloom_blit_sh[0] = DRW_shader_create_fullscreen(datatoc_effect_bloom_frag_glsl,
- "#define STEP_BLIT\n");
- e_data.bloom_blit_sh[1] = DRW_shader_create_fullscreen(datatoc_effect_bloom_frag_glsl,
- "#define STEP_BLIT\n"
- "#define HIGH_QUALITY\n");
-
- e_data.bloom_downsample_sh[0] = DRW_shader_create_fullscreen(datatoc_effect_bloom_frag_glsl,
- "#define STEP_DOWNSAMPLE\n");
- e_data.bloom_downsample_sh[1] = DRW_shader_create_fullscreen(datatoc_effect_bloom_frag_glsl,
- "#define STEP_DOWNSAMPLE\n"
- "#define HIGH_QUALITY\n");
-
- e_data.bloom_upsample_sh[0] = DRW_shader_create_fullscreen(datatoc_effect_bloom_frag_glsl,
- "#define STEP_UPSAMPLE\n");
- e_data.bloom_upsample_sh[1] = DRW_shader_create_fullscreen(datatoc_effect_bloom_frag_glsl,
- "#define STEP_UPSAMPLE\n"
- "#define HIGH_QUALITY\n");
-
- e_data.bloom_resolve_sh[0] = DRW_shader_create_fullscreen(datatoc_effect_bloom_frag_glsl,
- "#define STEP_RESOLVE\n");
- e_data.bloom_resolve_sh[1] = DRW_shader_create_fullscreen(datatoc_effect_bloom_frag_glsl,
- "#define STEP_RESOLVE\n"
- "#define HIGH_QUALITY\n");
-}
-
int EEVEE_bloom_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
{
EEVEE_StorageList *stl = vedata->stl;
@@ -81,11 +44,6 @@ int EEVEE_bloom_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
if (scene_eval->eevee.flag & SCE_EEVEE_BLOOM_ENABLED) {
const float *viewport_size = DRW_viewport_size_get();
- /* Shaders */
- if (!e_data.bloom_blit_sh[0]) {
- eevee_create_shader_bloom();
- }
-
/* Bloom */
int blitsize[2], texsize[2];
@@ -246,26 +204,26 @@ void EEVEE_bloom_cache_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *ved
const bool use_antiflicker = true;
eevee_create_bloom_pass("Bloom Downsample First",
effects,
- e_data.bloom_downsample_sh[use_antiflicker],
+ EEVEE_shaders_bloom_downsample_get(use_antiflicker),
&psl->bloom_downsample_first,
false,
false);
eevee_create_bloom_pass("Bloom Downsample",
effects,
- e_data.bloom_downsample_sh[0],
+ EEVEE_shaders_bloom_downsample_get(false),
&psl->bloom_downsample,
false,
false);
eevee_create_bloom_pass("Bloom Upsample",
effects,
- e_data.bloom_upsample_sh[use_highres],
+ EEVEE_shaders_bloom_upsample_get(use_highres),
&psl->bloom_upsample,
true,
false);
grp = eevee_create_bloom_pass("Bloom Blit",
effects,
- e_data.bloom_blit_sh[use_antiflicker],
+ EEVEE_shaders_bloom_blit_get(use_antiflicker),
&psl->bloom_blit,
false,
false);
@@ -274,7 +232,7 @@ void EEVEE_bloom_cache_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *ved
grp = eevee_create_bloom_pass("Bloom Resolve",
effects,
- e_data.bloom_resolve_sh[use_highres],
+ EEVEE_shaders_bloom_resolve_get(use_highres),
&psl->bloom_resolve,
true,
true);
@@ -362,7 +320,7 @@ void EEVEE_bloom_output_init(EEVEE_ViewLayerData *UNUSED(sldata),
/* Create Pass and shgroup. */
DRWShadingGroup *grp = eevee_create_bloom_pass("Bloom Accumulate",
effects,
- e_data.bloom_resolve_sh[use_highres],
+ EEVEE_shaders_bloom_resolve_get(use_highres),
&psl->bloom_accum_ps,
true,
true);
@@ -383,13 +341,3 @@ void EEVEE_bloom_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Da
GPU_framebuffer_bind(fbl->main_fb);
}
}
-
-void EEVEE_bloom_free(void)
-{
- for (int i = 0; i < 2; i++) {
- DRW_SHADER_FREE_SAFE(e_data.bloom_blit_sh[i]);
- DRW_SHADER_FREE_SAFE(e_data.bloom_downsample_sh[i]);
- DRW_SHADER_FREE_SAFE(e_data.bloom_upsample_sh[i]);
- DRW_SHADER_FREE_SAFE(e_data.bloom_resolve_sh[i]);
- }
-}
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_effects.c b/source/blender/draw/engines/eevee/eevee_effects.c
index 4a03ef69d45..acda2669bb7 100644
--- a/source/blender/draw/engines/eevee/eevee_effects.c
+++ b/source/blender/draw/engines/eevee/eevee_effects.c
@@ -195,7 +195,7 @@ void EEVEE_effects_init(EEVEE_ViewLayerData *sldata,
}
if (fbl->downsample_fb == NULL) {
- fbl->downsample_fb = GPU_framebuffer_create();
+ fbl->downsample_fb = GPU_framebuffer_create("downsample_fb");
}
/**
diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c
index 72f008ea66a..d5fd11040e3 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;
@@ -571,7 +571,6 @@ static void eevee_render_to_image(void *vedata,
static void eevee_engine_free(void)
{
EEVEE_shaders_free();
- EEVEE_bloom_free();
EEVEE_depth_of_field_free();
EEVEE_effects_free();
EEVEE_lightprobes_free();
diff --git a/source/blender/draw/engines/eevee/eevee_lightcache.c b/source/blender/draw/engines/eevee/eevee_lightcache.c
index 6d2577d5b78..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);
@@ -1302,8 +1302,8 @@ void EEVEE_lightbake_job(void *custom_data, short *stop, short *do_update, float
EEVEE_LightBake *lbake = (EEVEE_LightBake *)custom_data;
Depsgraph *depsgraph = lbake->depsgraph;
- DEG_graph_relations_update(depsgraph, lbake->bmain, lbake->scene, lbake->view_layer_input);
- DEG_evaluate_on_framechange(lbake->bmain, depsgraph, lbake->frame);
+ DEG_graph_relations_update(depsgraph);
+ DEG_evaluate_on_framechange(depsgraph, lbake->frame);
lbake->view_layer = DEG_get_evaluated_view_layer(depsgraph);
lbake->stop = stop;
@@ -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..9f86958cef8 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;
@@ -1080,10 +1080,12 @@ void EEVEE_lightbake_filter_glossy(EEVEE_ViewLayerData *sldata,
log(2);
pinfo->firefly_fac = (firefly_fac > 0.0) ? firefly_fac : 1e16;
- GPU_framebuffer_ensure_config(
- &fb, {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE_MIP(light_cache->cube_tx.tex, i)});
+ GPU_framebuffer_ensure_config(&fb,
+ {
+ GPU_ATTACHMENT_NONE,
+ GPU_ATTACHMENT_TEXTURE_MIP(light_cache->cube_tx.tex, i),
+ });
GPU_framebuffer_bind(fb);
- GPU_framebuffer_viewport_set(fb, 0, 0, mipsize, mipsize);
DRW_draw_pass(psl->probe_glossy_compute);
mipsize /= 2;
@@ -1144,6 +1146,7 @@ void EEVEE_lightbake_filter_diffuse(EEVEE_ViewLayerData *sldata,
GPU_framebuffer_bind(fb);
GPU_framebuffer_viewport_set(fb, x, y, size[0], size[1]);
DRW_draw_pass(psl->probe_diffuse_compute);
+ GPU_framebuffer_viewport_reset(fb);
}
/* Filter rt_depth to light_cache->grid_tx.tex at index grid_offset */
@@ -1182,6 +1185,7 @@ void EEVEE_lightbake_filter_visibility(EEVEE_ViewLayerData *sldata,
GPU_framebuffer_bind(fb);
GPU_framebuffer_viewport_set(fb, x, y, vis_size, vis_size);
DRW_draw_pass(psl->probe_visibility_compute);
+ GPU_framebuffer_viewport_reset(fb);
}
/* Actually a simple down-sampling. */
@@ -1241,7 +1245,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 +1253,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..2ca234ad5bd 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];
@@ -331,6 +331,8 @@ void EEVEE_lookdev_draw(EEVEE_Data *vedata)
DRW_draw_pass(psl->lookdev_glossy_pass);
+ GPU_framebuffer_viewport_reset(fb);
+
DRW_stats_group_end();
DRW_view_set_active(NULL);
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_motion_blur.c b/source/blender/draw/engines/eevee/eevee_motion_blur.c
index fa517e2d5c9..f10a3f42077 100644
--- a/source/blender/draw/engines/eevee/eevee_motion_blur.c
+++ b/source/blender/draw/engines/eevee/eevee_motion_blur.c
@@ -616,6 +616,8 @@ void EEVEE_motion_blur_draw(EEVEE_Data *vedata)
DRW_draw_pass(psl->velocity_tiles_expand[buf]);
+ GPU_framebuffer_viewport_reset(fbl->velocity_tiles_fb[buf]);
+
buf = buf ? 0 : 1;
}
diff --git a/source/blender/draw/engines/eevee/eevee_occlusion.c b/source/blender/draw/engines/eevee/eevee_occlusion.c
index 9aae801197f..052fb485b19 100644
--- a/source/blender/draw/engines/eevee/eevee_occlusion.c
+++ b/source/blender/draw/engines/eevee/eevee_occlusion.c
@@ -78,7 +78,8 @@ int EEVEE_occlusion_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
e_data.dummy_horizon_tx = DRW_texture_create_2d(1, 1, GPU_RGBA8, DRW_TEX_WRAP, pixel);
}
- if (scene_eval->eevee.flag & SCE_EEVEE_GTAO_ENABLED) {
+ if (scene_eval->eevee.flag & SCE_EEVEE_GTAO_ENABLED ||
+ stl->g_data->render_passes & EEVEE_RENDER_PASS_AO) {
const float *viewport_size = DRW_viewport_size_get();
const int fs_size[2] = {(int)viewport_size[0], (int)viewport_size[1]};
@@ -101,10 +102,11 @@ int EEVEE_occlusion_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
common_data->ao_bounce_fac = (scene_eval->eevee.flag & SCE_EEVEE_GTAO_BOUNCE) ? 1.0f : 0.0f;
- effects->gtao_horizons = DRW_texture_pool_query_2d(
+ effects->gtao_horizons_renderpass = DRW_texture_pool_query_2d(
fs_size[0], fs_size[1], GPU_RGBA8, &draw_engine_eevee_type);
GPU_framebuffer_ensure_config(
- &fbl->gtao_fb, {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(effects->gtao_horizons)});
+ &fbl->gtao_fb,
+ {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(effects->gtao_horizons_renderpass)});
if (G.debug_value == 6) {
effects->gtao_horizons_debug = DRW_texture_pool_query_2d(
@@ -117,10 +119,15 @@ int EEVEE_occlusion_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
effects->gtao_horizons_debug = NULL;
}
+ effects->gtao_horizons = (scene_eval->eevee.flag & SCE_EEVEE_GTAO_ENABLED) ?
+ effects->gtao_horizons_renderpass :
+ e_data.dummy_horizon_tx;
+
return EFFECT_GTAO | EFFECT_NORMAL_BUFFER;
}
/* Cleanup */
+ effects->gtao_horizons_renderpass = e_data.dummy_horizon_tx;
effects->gtao_horizons = e_data.dummy_horizon_tx;
GPU_FRAMEBUFFER_FREE_SAFE(fbl->gtao_fb);
common_data->ao_settings = 0.0f;
@@ -136,45 +143,41 @@ void EEVEE_occlusion_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata
EEVEE_PassList *psl = vedata->psl;
EEVEE_EffectsInfo *effects = stl->effects;
- const DRWContextState *draw_ctx = DRW_context_state_get();
- const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
-
- if (scene_eval->eevee.flag & SCE_EEVEE_GTAO_ENABLED) {
- const eGPUTextureFormat texture_format = (tot_samples > 128) ? GPU_R32F : GPU_R16F;
+ const eGPUTextureFormat texture_format = (tot_samples > 128) ? GPU_R32F : GPU_R16F;
- DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
- const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
-
- /* Should be enough precision for many samples. */
- DRW_texture_ensure_fullscreen_2d(&txl->ao_accum, texture_format, 0);
+ DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
+ const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
- GPU_framebuffer_ensure_config(&fbl->ao_accum_fb,
- {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->ao_accum)});
+ /* Should be enough precision for many samples. */
+ DRW_texture_ensure_fullscreen_2d(&txl->ao_accum, texture_format, 0);
- /* Clear texture. */
- if (effects->taa_current_sample == 1) {
- GPU_framebuffer_bind(fbl->ao_accum_fb);
- GPU_framebuffer_clear_color(fbl->ao_accum_fb, clear);
- }
+ GPU_framebuffer_ensure_config(&fbl->ao_accum_fb,
+ {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->ao_accum)});
- /* Accumulation pass */
- DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD;
- DRW_PASS_CREATE(psl->ao_accum_ps, state);
- DRWShadingGroup *grp = DRW_shgroup_create(e_data.gtao_debug_sh, psl->ao_accum_ps);
- DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex());
- DRW_shgroup_uniform_texture_ref(grp, "maxzBuffer", &txl->maxzbuffer);
- DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);
- DRW_shgroup_uniform_texture_ref(grp, "normalBuffer", &effects->ssr_normal_input);
- DRW_shgroup_uniform_texture_ref(grp, "horizonBuffer", &effects->gtao_horizons);
- DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
- DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
- DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL);
+ /* Clear texture. */
+ if (effects->taa_current_sample == 1) {
+ GPU_framebuffer_bind(fbl->ao_accum_fb);
+ GPU_framebuffer_clear_color(fbl->ao_accum_fb, clear);
}
- else {
- /* Cleanup to release memory */
- DRW_TEXTURE_FREE_SAFE(txl->ao_accum);
- GPU_FRAMEBUFFER_FREE_SAFE(fbl->ao_accum_fb);
+
+ /* Clear texture. */
+ if (DRW_state_is_image_render() || effects->taa_current_sample == 1) {
+ GPU_framebuffer_bind(fbl->ao_accum_fb);
+ GPU_framebuffer_clear_color(fbl->ao_accum_fb, clear);
}
+
+ /* Accumulation pass */
+ DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD;
+ DRW_PASS_CREATE(psl->ao_accum_ps, state);
+ DRWShadingGroup *grp = DRW_shgroup_create(e_data.gtao_debug_sh, psl->ao_accum_ps);
+ DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex());
+ DRW_shgroup_uniform_texture_ref(grp, "maxzBuffer", &txl->maxzbuffer);
+ DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);
+ DRW_shgroup_uniform_texture_ref(grp, "normalBuffer", &effects->ssr_normal_input);
+ DRW_shgroup_uniform_texture_ref(grp, "horizonBuffer", &effects->gtao_horizons_renderpass);
+ DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
+ DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
+ DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL);
}
void EEVEE_occlusion_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
@@ -225,7 +228,7 @@ void EEVEE_occlusion_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
DRW_shgroup_uniform_texture_ref(grp, "maxzBuffer", &txl->maxzbuffer);
DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);
DRW_shgroup_uniform_texture_ref(grp, "normalBuffer", &effects->ssr_normal_input);
- DRW_shgroup_uniform_texture_ref(grp, "horizonBuffer", &effects->gtao_horizons);
+ DRW_shgroup_uniform_texture_ref(grp, "horizonBuffer", &effects->gtao_horizons_renderpass);
DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
DRW_shgroup_call(grp, quad, NULL);
diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h
index 1e2de521cdf..7fba0e1b8ed 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;
@@ -679,8 +683,9 @@ typedef struct EEVEE_EffectsInfo {
struct DRWView *taa_view;
/* Ambient Occlusion */
int ao_depth_layer;
- struct GPUTexture *ao_src_depth; /* pointer copy */
- struct GPUTexture *gtao_horizons; /* Textures from pool */
+ struct GPUTexture *ao_src_depth; /* pointer copy */
+ struct GPUTexture *gtao_horizons; /* Textures from pool */
+ struct GPUTexture *gtao_horizons_renderpass; /* Texture when rendering render pass */
struct GPUTexture *gtao_horizons_debug;
/* Motion Blur */
float current_ndc_to_world[4][4];
@@ -817,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;
@@ -831,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;
@@ -952,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. */
@@ -1067,6 +1072,10 @@ void EEVEE_random_rotation_m4(int sample_ofs, float scale, float r_mat[4][4]);
void EEVEE_shaders_lightprobe_shaders_init(void);
void EEVEE_shaders_material_shaders_init(void);
struct DRWShaderLibrary *EEVEE_shader_lib_get(void);
+struct GPUShader *EEVEE_shaders_bloom_blit_get(bool high_quality);
+struct GPUShader *EEVEE_shaders_bloom_downsample_get(bool high_quality);
+struct GPUShader *EEVEE_shaders_bloom_upsample_get(bool high_quality);
+struct GPUShader *EEVEE_shaders_bloom_resolve_get(bool high_quality);
struct GPUShader *EEVEE_shaders_probe_filter_glossy_sh_get(void);
struct GPUShader *EEVEE_shaders_probe_filter_diffuse_sh_get(void);
struct GPUShader *EEVEE_shaders_probe_filter_visibility_sh_get(void);
@@ -1158,7 +1167,6 @@ void EEVEE_bloom_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
void EEVEE_bloom_draw(EEVEE_Data *vedata);
void EEVEE_bloom_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, uint tot_samples);
void EEVEE_bloom_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_bloom_free(void);
/* eevee_occlusion.c */
int EEVEE_occlusion_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
@@ -1362,3 +1370,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 21a4013e309..2351b06db98 100644
--- a/source/blender/draw/engines/eevee/eevee_render.c
+++ b/source/blender/draw/engines/eevee/eevee_render.c
@@ -359,11 +359,6 @@ static void eevee_render_result_occlusion(RenderLayer *rl,
EEVEE_Data *vedata,
EEVEE_ViewLayerData *sldata)
{
- if ((vedata->stl->effects->enabled_effects & EFFECT_GTAO) == 0) {
- /* AO is not enabled. */
- return;
- }
-
if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_AO) != 0) {
EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_AO);
eevee_render_color_result(
@@ -579,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_renderpasses.c b/source/blender/draw/engines/eevee/eevee_renderpasses.c
index 089d8b7a287..55fe5882211 100644
--- a/source/blender/draw/engines/eevee/eevee_renderpasses.c
+++ b/source/blender/draw/engines/eevee/eevee_renderpasses.c
@@ -90,12 +90,8 @@ void EEVEE_renderpasses_init(EEVEE_Data *vedata)
if (v3d) {
const Scene *scene = draw_ctx->scene;
eViewLayerEEVEEPassType render_pass = v3d->shading.render_pass;
- if (render_pass == EEVEE_RENDER_PASS_AO &&
- ((scene->eevee.flag & SCE_EEVEE_GTAO_ENABLED) == 0)) {
- render_pass = EEVEE_RENDER_PASS_COMBINED;
- }
- else if (render_pass == EEVEE_RENDER_PASS_BLOOM &&
- ((scene->eevee.flag & SCE_EEVEE_BLOOM_ENABLED) == 0)) {
+ if (render_pass == EEVEE_RENDER_PASS_BLOOM &&
+ ((scene->eevee.flag & SCE_EEVEE_BLOOM_ENABLED) == 0)) {
render_pass = EEVEE_RENDER_PASS_COMBINED;
}
g_data->render_passes = render_pass;
@@ -392,8 +388,6 @@ void EEVEE_renderpasses_draw(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
((stl->g_data->render_passes & EEVEE_RENDERPASSES_LIGHT_PASS) != 0) ?
(stl->g_data->render_passes & EEVEE_RENDERPASSES_LIGHT_PASS) :
stl->g_data->render_passes;
- const DRWContextState *draw_ctx = DRW_context_state_get();
- const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
bool is_valid = (render_pass & EEVEE_RENDERPASSES_ALL) > 0;
bool needs_color_transfer = (render_pass & EEVEE_RENDERPASSES_COLOR_PASS) > 0 &&
@@ -405,12 +399,6 @@ void EEVEE_renderpasses_draw(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
is_valid = false;
}
- /* When SSS isn't available, but the pass is requested, we mark it as invalid */
- if ((render_pass & EEVEE_RENDER_PASS_AO) != 0 &&
- (scene_eval->eevee.flag & SCE_EEVEE_GTAO_ENABLED) == 0) {
- is_valid = false;
- }
-
const int current_sample = stl->effects->taa_current_sample;
const int total_samples = stl->effects->taa_total_sample;
if ((render_pass & EEVEE_RENDERPASSES_POST_PROCESS_ON_FIRST_SAMPLE) &&
@@ -462,10 +450,10 @@ void EEVEE_renderpasses_draw_debug(EEVEE_Data *vedata)
tx = txl->color_double_buffer;
break;
case 6:
- tx = effects->gtao_horizons;
+ tx = effects->gtao_horizons_renderpass;
break;
case 7:
- tx = effects->gtao_horizons;
+ tx = effects->gtao_horizons_renderpass;
break;
case 8:
tx = effects->sss_irradiance;
diff --git a/source/blender/draw/engines/eevee/eevee_shaders.c b/source/blender/draw/engines/eevee/eevee_shaders.c
index 5f125d395d3..6c90d6325a0 100644
--- a/source/blender/draw/engines/eevee/eevee_shaders.c
+++ b/source/blender/draw/engines/eevee/eevee_shaders.c
@@ -69,6 +69,12 @@ static struct {
struct GPUShader *taa_resolve_sh;
struct GPUShader *taa_resolve_reproject_sh;
+ /* Bloom */
+ struct GPUShader *bloom_blit_sh[2];
+ struct GPUShader *bloom_downsample_sh[2];
+ struct GPUShader *bloom_upsample_sh[2];
+ struct GPUShader *bloom_resolve_sh[2];
+
/* General purpose Shaders. */
struct GPUShader *lookdev_background;
struct GPUShader *update_noise_sh;
@@ -405,6 +411,62 @@ GPUShader *EEVEE_shaders_taa_resolve_sh_get(EEVEE_EffectsFlag enabled_effects)
return *sh;
}
+GPUShader *EEVEE_shaders_bloom_blit_get(bool high_quality)
+{
+ int index = high_quality ? 1 : 0;
+
+ if (e_data.bloom_blit_sh[index] == NULL) {
+ const char *define = high_quality ? "#define STEP_BLIT\n"
+ "#define HIGH_QUALITY\n" :
+ "#define STEP_BLIT\n";
+ e_data.bloom_blit_sh[index] = DRW_shader_create_fullscreen(datatoc_effect_bloom_frag_glsl,
+ define);
+ }
+ return e_data.bloom_blit_sh[index];
+}
+
+GPUShader *EEVEE_shaders_bloom_downsample_get(bool high_quality)
+{
+ int index = high_quality ? 1 : 0;
+
+ if (e_data.bloom_downsample_sh[index] == NULL) {
+ const char *define = high_quality ? "#define STEP_DOWNSAMPLE\n"
+ "#define HIGH_QUALITY\n" :
+ "#define STEP_DOWNSAMPLE\n";
+ e_data.bloom_downsample_sh[index] = DRW_shader_create_fullscreen(
+ datatoc_effect_bloom_frag_glsl, define);
+ }
+ return e_data.bloom_downsample_sh[index];
+}
+
+GPUShader *EEVEE_shaders_bloom_upsample_get(bool high_quality)
+{
+ int index = high_quality ? 1 : 0;
+
+ if (e_data.bloom_upsample_sh[index] == NULL) {
+ const char *define = high_quality ? "#define STEP_UPSAMPLE\n"
+ "#define HIGH_QUALITY\n" :
+ "#define STEP_UPSAMPLE\n";
+ e_data.bloom_upsample_sh[index] = DRW_shader_create_fullscreen(datatoc_effect_bloom_frag_glsl,
+ define);
+ }
+ return e_data.bloom_upsample_sh[index];
+}
+
+GPUShader *EEVEE_shaders_bloom_resolve_get(bool high_quality)
+{
+ int index = high_quality ? 1 : 0;
+
+ if (e_data.bloom_resolve_sh[index] == NULL) {
+ const char *define = high_quality ? "#define STEP_RESOLVE\n"
+ "#define HIGH_QUALITY\n" :
+ "#define STEP_RESOLVE\n";
+ e_data.bloom_resolve_sh[index] = DRW_shader_create_fullscreen(datatoc_effect_bloom_frag_glsl,
+ define);
+ }
+ return e_data.bloom_resolve_sh[index];
+}
+
Material *EEVEE_material_default_diffuse_get(void)
{
if (!e_data.diffuse_mat) {
@@ -771,6 +833,13 @@ void EEVEE_shaders_free(void)
DRW_SHADER_FREE_SAFE(e_data.velocity_resolve_sh);
DRW_SHADER_FREE_SAFE(e_data.taa_resolve_sh);
DRW_SHADER_FREE_SAFE(e_data.taa_resolve_reproject_sh);
+
+ for (int i = 0; i < 2; i++) {
+ DRW_SHADER_FREE_SAFE(e_data.bloom_blit_sh[i]);
+ DRW_SHADER_FREE_SAFE(e_data.bloom_downsample_sh[i]);
+ DRW_SHADER_FREE_SAFE(e_data.bloom_upsample_sh[i]);
+ DRW_SHADER_FREE_SAFE(e_data.bloom_resolve_sh[i]);
+ }
DRW_SHADER_LIB_FREE_SAFE(e_data.lib);
if (e_data.default_world) {
diff --git a/source/blender/draw/engines/eevee/eevee_shadows.c b/source/blender/draw/engines/eevee/eevee_shadows.c
index 71a4da9fcab..26bf477763e 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(
@@ -263,7 +263,7 @@ void EEVEE_shadows_update(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
}
if (sldata->shadow_fb == NULL) {
- sldata->shadow_fb = GPU_framebuffer_create();
+ sldata->shadow_fb = GPU_framebuffer_create("shadow_fb");
}
/* Gather all light own update bits. to avoid costly intersection check. */
@@ -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..e81d15d1e31 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]);
@@ -636,6 +638,7 @@ void EEVEE_volumes_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo);
DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo);
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, common_data->vol_tex_size[2]);
@@ -645,6 +648,7 @@ void EEVEE_volumes_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
DRW_shgroup_uniform_texture_ref(grp, "volumeScattering", &txl->volume_scatter);
DRW_shgroup_uniform_texture_ref(grp, "volumeExtinction", &txl->volume_transmit);
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(
@@ -656,6 +660,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 746920e38c6..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,8 +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;
+ bool show_stroke = ((gp_style->flag & GP_MATERIAL_STROKE_SHOW) != 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);
@@ -500,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);
@@ -653,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_edit_mesh.c b/source/blender/draw/engines/overlay/overlay_edit_mesh.c
index ebc8a2f97ef..728b3d510fa 100644
--- a/source/blender/draw/engines/overlay/overlay_edit_mesh.c
+++ b/source/blender/draw/engines/overlay/overlay_edit_mesh.c
@@ -75,8 +75,6 @@ void OVERLAY_edit_mesh_cache_init(OVERLAY_Data *vedata)
bool show_face_dots = (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_FACE_DOT) != 0 ||
pd->edit_mesh.do_zbufclip;
- pd->edit_mesh.ghost_ob = 0;
- pd->edit_mesh.edit_ob = 0;
pd->edit_mesh.do_faces = true;
pd->edit_mesh.do_edges = true;
@@ -312,9 +310,6 @@ void OVERLAY_edit_mesh_cache_populate(OVERLAY_Data *vedata, Object *ob)
overlay_edit_mesh_add_ob_to_pass(pd, ob, do_in_front);
}
- pd->edit_mesh.ghost_ob += (ob->dtx & OB_DRAW_IN_FRONT) ? 1 : 0;
- pd->edit_mesh.edit_ob += 1;
-
if (DRW_state_show_text() && (pd->edit_mesh.flag & OVERLAY_EDIT_TEXT)) {
const DRWContextState *draw_ctx = DRW_context_state_get();
DRW_text_edit_mesh_measure_stats(draw_ctx->region, draw_ctx->v3d, ob, &draw_ctx->scene->unit);
@@ -375,18 +370,11 @@ void OVERLAY_edit_mesh_draw(OVERLAY_Data *vedata)
DRW_draw_pass(psl->edit_mesh_verts_ps[NOT_IN_FRONT]);
}
else {
- const DRWContextState *draw_ctx = DRW_context_state_get();
- View3D *v3d = draw_ctx->v3d;
-
DRW_draw_pass(psl->edit_mesh_normals_ps);
overlay_edit_mesh_draw_components(psl, pd, false);
- if (!DRW_state_is_depth() && v3d->shading.type == OB_SOLID && pd->edit_mesh.ghost_ob == 1 &&
- pd->edit_mesh.edit_ob == 1) {
- /* In the case of single ghost object edit (common case for retopology):
- * we clear the depth buffer so that only the depth of the retopo mesh
- * is occluding the edit cage. */
- GPU_framebuffer_clear_depth(fbl->overlay_default_fb, 1.0f);
+ if (DRW_state_is_fbo()) {
+ GPU_framebuffer_bind(fbl->overlay_in_front_fb);
}
if (!DRW_pass_is_empty(psl->edit_mesh_depth_ps[IN_FRONT])) {
diff --git a/source/blender/draw/engines/overlay/overlay_private.h b/source/blender/draw/engines/overlay/overlay_private.h
index a8ac2616c02..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
@@ -306,8 +312,6 @@ typedef struct OVERLAY_PrivateData {
float overlay_color[4];
} edit_text;
struct {
- int ghost_ob;
- int edit_ob;
bool do_zbufclip;
bool do_faces;
bool do_edges;
@@ -629,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/overlay/shaders/edit_curve_handle_geom.glsl b/source/blender/draw/engines/overlay/shaders/edit_curve_handle_geom.glsl
index a400aadb052..9311542a79e 100644
--- a/source/blender/draw/engines/overlay/shaders/edit_curve_handle_geom.glsl
+++ b/source/blender/draw/engines/overlay/shaders/edit_curve_handle_geom.glsl
@@ -58,7 +58,7 @@ void main()
if ((curveHandleDisplay != CURVE_HANDLE_ALL) && (!handle_selected)) {
/* Nurbs must show the handles always. */
bool is_u_segment = (((vertFlag[1] ^ vertFlag[0]) & EVEN_U_BIT) != 0);
- if (!is_u_segment) {
+ if ((!is_u_segment) && (color_id <= 4)) {
return;
}
}
diff --git a/source/blender/draw/engines/overlay/shaders/edit_gpencil_vert.glsl b/source/blender/draw/engines/overlay/shaders/edit_gpencil_vert.glsl
index 5818d8eca52..a6161d36a07 100644
--- a/source/blender/draw/engines/overlay/shaders/edit_gpencil_vert.glsl
+++ b/source/blender/draw/engines/overlay/shaders/edit_gpencil_vert.glsl
@@ -73,7 +73,7 @@ void main()
}
#ifdef USE_POINTS
- gl_PointSize = sizeVertex * 2.0;
+ gl_PointSize = sizeVertexGpencil * 2.0;
if (is_point_dimmed) {
finalColor.rgb = clamp(colorUnselect.rgb + vec3(0.3), 0.0, 1.0);
diff --git a/source/blender/draw/engines/select/select_engine.c b/source/blender/draw/engines/select/select_engine.c
index 8cee6c4ee9f..0ce8937687a 100644
--- a/source/blender/draw/engines/select/select_engine.c
+++ b/source/blender/draw/engines/select/select_engine.c
@@ -65,7 +65,7 @@ static void select_engine_framebuffer_setup(void)
size[1] = GPU_texture_height(dtxl->depth);
if (e_data.framebuffer_select_id == NULL) {
- e_data.framebuffer_select_id = GPU_framebuffer_create();
+ e_data.framebuffer_select_id = GPU_framebuffer_create("framebuffer_select_id");
}
if ((e_data.texture_u32 != NULL) && ((GPU_texture_width(e_data.texture_u32) != size[0]) ||
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/shaders/workbench_effect_dof_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_effect_dof_frag.glsl
index 71816f6ff6e..899ada852f9 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_effect_dof_frag.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_effect_dof_frag.glsl
@@ -1,4 +1,4 @@
-
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
/**
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/engines/workbench/workbench_shader.c b/source/blender/draw/engines/workbench/workbench_shader.c
index 9cc5087bd36..af3b5d31b2b 100644
--- a/source/blender/draw/engines/workbench/workbench_shader.c
+++ b/source/blender/draw/engines/workbench/workbench_shader.c
@@ -380,24 +380,26 @@ void workbench_shader_depth_of_field_get(GPUShader **prepare_sh,
GPUShader **resolve_sh)
{
if (e_data.dof_prepare_sh == NULL) {
- char *frag = BLI_string_joinN(datatoc_common_view_lib_glsl,
- datatoc_workbench_effect_dof_frag_glsl);
- e_data.dof_prepare_sh = DRW_shader_create_fullscreen(frag, "#define PREPARE\n");
- e_data.dof_downsample_sh = DRW_shader_create_fullscreen(frag, "#define DOWNSAMPLE\n");
+ e_data.dof_prepare_sh = DRW_shader_create_fullscreen_with_shaderlib(
+ datatoc_workbench_effect_dof_frag_glsl, e_data.lib, "#define PREPARE\n");
+ e_data.dof_downsample_sh = DRW_shader_create_fullscreen_with_shaderlib(
+ datatoc_workbench_effect_dof_frag_glsl, e_data.lib, "#define DOWNSAMPLE\n");
#if 0 /* TODO(fclem) finish COC min_max optimization */
- e_data.dof_flatten_v_sh = DRW_shader_create_fullscreen(frag,
- "#define FLATTEN_VERTICAL\n");
- e_data.dof_flatten_h_sh = DRW_shader_create_fullscreen(frag,
- "#define FLATTEN_HORIZONTAL\n");
- e_data.dof_dilate_v_sh = DRW_shader_create_fullscreen(frag,
- "#define DILATE_VERTICAL\n");
- e_data.dof_dilate_h_sh = DRW_shader_create_fullscreen(frag,
- "#define DILATE_HORIZONTAL\n");
+ e_data.dof_flatten_v_sh = DRW_shader_create_fullscreen_with_shaderlib(
+ datatoc_workbench_effect_dof_frag_glsl, e_data.lib, "#define FLATTEN_VERTICAL\n");
+ e_data.dof_flatten_h_sh = DRW_shader_create_fullscreen_with_shaderlib(
+ datatoc_workbench_effect_dof_frag_glsl, e_data.lib, "#define FLATTEN_HORIZONTAL\n");
+ e_data.dof_dilate_v_sh = DRW_shader_create_fullscreen_with_shaderlib(
+ datatoc_workbench_effect_dof_frag_glsl, e_data.lib, "#define DILATE_VERTICAL\n");
+ e_data.dof_dilate_h_sh = DRW_shader_create_fullscreen_with_shaderlib(
+ datatoc_workbench_effect_dof_frag_glsl, e_data.lib, "#define DILATE_HORIZONTAL\n");
#endif
- e_data.dof_blur1_sh = DRW_shader_create_fullscreen(frag, "#define BLUR1\n");
- e_data.dof_blur2_sh = DRW_shader_create_fullscreen(frag, "#define BLUR2\n");
- e_data.dof_resolve_sh = DRW_shader_create_fullscreen(frag, "#define RESOLVE\n");
- MEM_freeN(frag);
+ e_data.dof_blur1_sh = DRW_shader_create_fullscreen_with_shaderlib(
+ datatoc_workbench_effect_dof_frag_glsl, e_data.lib, "#define BLUR1\n");
+ e_data.dof_blur2_sh = DRW_shader_create_fullscreen_with_shaderlib(
+ datatoc_workbench_effect_dof_frag_glsl, e_data.lib, "#define BLUR2\n");
+ e_data.dof_resolve_sh = DRW_shader_create_fullscreen_with_shaderlib(
+ datatoc_workbench_effect_dof_frag_glsl, e_data.lib, "#define RESOLVE\n");
}
*prepare_sh = e_data.dof_prepare_sh;
diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h
index 956bddfb357..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)
@@ -208,27 +205,44 @@ typedef void (*GPUMaterialEvalCallbackFn)(struct GPUMaterial *mat,
const char **defines);
#endif
-struct GPUShader *DRW_shader_create(const char *vert,
- const char *geom,
- const char *frag,
- const char *defines);
-struct GPUShader *DRW_shader_create_with_lib(
- const char *vert, const char *geom, const char *frag, const char *lib, const char *defines);
-struct GPUShader *DRW_shader_create_with_shaderlib(const char *vert,
- const char *geom,
- const char *frag,
- const DRWShaderLibrary *lib,
- const char *defines);
+struct GPUShader *DRW_shader_create_ex(
+ const char *vert, const char *geom, const char *frag, const char *defines, const char *func);
+struct GPUShader *DRW_shader_create_with_lib_ex(const char *vert,
+ const char *geom,
+ const char *frag,
+ const char *lib,
+ const char *defines,
+ const char *func);
+struct GPUShader *DRW_shader_create_with_shaderlib_ex(const char *vert,
+ const char *geom,
+ const char *frag,
+ const DRWShaderLibrary *lib,
+ const char *defines,
+ const char *func);
struct GPUShader *DRW_shader_create_with_transform_feedback(const char *vert,
const char *geom,
const char *defines,
const eGPUShaderTFBType prim_type,
const char **varying_names,
const int varying_count);
-struct GPUShader *DRW_shader_create_fullscreen(const char *frag, const char *defines);
-struct GPUShader *DRW_shader_create_fullscreen_with_shaderlib(const char *frag,
- const DRWShaderLibrary *lib,
- const char *defines);
+struct GPUShader *DRW_shader_create_fullscreen_ex(const char *frag,
+ const char *defines,
+ const char *func);
+struct GPUShader *DRW_shader_create_fullscreen_with_shaderlib_ex(const char *frag,
+ const DRWShaderLibrary *lib,
+ const char *defines,
+ const char *func);
+#define DRW_shader_create(vert, geom, frag, defines) \
+ DRW_shader_create_ex(vert, geom, frag, defines, __func__)
+#define DRW_shader_create_with_lib(vert, geom, frag, lib, defines) \
+ DRW_shader_create_with_lib_ex(vert, geom, frag, lib, defines, __func__)
+#define DRW_shader_create_with_shaderlib(vert, geom, frag, lib, defines) \
+ DRW_shader_create_with_shaderlib_ex(vert, geom, frag, lib, defines, __func__)
+#define DRW_shader_create_fullscreen(frag, defines) \
+ DRW_shader_create_fullscreen_ex(frag, defines, __func__)
+#define DRW_shader_create_fullscreen_with_shaderlib(frag, lib, defines) \
+ DRW_shader_create_fullscreen_with_shaderlib_ex(frag, lib, defines, __func__)
+
struct GPUMaterial *DRW_shader_find_from_world(struct World *wo,
const void *engine_type,
const int options,
@@ -344,6 +358,10 @@ typedef enum {
#define DRW_STATE_DEFAULT \
(DRW_STATE_WRITE_DEPTH | DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL)
+#define DRW_STATE_BLEND_ENABLED \
+ (DRW_STATE_BLEND_ADD | DRW_STATE_BLEND_ADD_FULL | DRW_STATE_BLEND_ALPHA | \
+ DRW_STATE_BLEND_ALPHA_PREMUL | DRW_STATE_BLEND_BACKGROUND | DRW_STATE_BLEND_OIT | \
+ DRW_STATE_BLEND_MUL | DRW_STATE_BLEND_SUB | DRW_STATE_BLEND_CUSTOM | DRW_STATE_LOGIC_INVERT)
#define DRW_STATE_RASTERIZER_ENABLED \
(DRW_STATE_WRITE_DEPTH | DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_STENCIL | \
DRW_STATE_WRITE_STENCIL_SHADOW_PASS | DRW_STATE_WRITE_STENCIL_SHADOW_FAIL)
@@ -495,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,
@@ -718,7 +736,6 @@ bool DRW_state_draw_background(void);
/* Avoid too many lookups while drawing */
typedef struct DRWContextState {
-
struct ARegion *region; /* 'CTX_wm_region(C)' */
struct RegionView3D *rv3d; /* 'CTX_wm_region_view3d(C)' */
struct View3D *v3d; /* 'CTX_wm_view3d(C)' */
diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c
index 46b7a88b2a6..4d7440a3276 100644
--- a/source/blender/draw/intern/draw_cache.c
+++ b/source/blender/draw/intern/draw_cache.c
@@ -152,18 +152,6 @@ void DRW_shape_cache_free(void)
}
}
-void DRW_shape_cache_reset(void)
-{
- uint i = sizeof(SHC) / sizeof(GPUBatch *);
- GPUBatch **batch = (GPUBatch **)&SHC;
- while (i--) {
- if (*batch) {
- GPU_batch_vao_cache_clear(*batch);
- }
- batch++;
- }
-}
-
/* -------------------------------------------------------------------- */
/** \name Procedural Batches
* \{ */
diff --git a/source/blender/draw/intern/draw_cache.h b/source/blender/draw/intern/draw_cache.h
index 2a7448ce877..7164a477d8a 100644
--- a/source/blender/draw/intern/draw_cache.h
+++ b/source/blender/draw/intern/draw_cache.h
@@ -33,7 +33,6 @@ struct VolumeGrid;
struct bGPDstroke;
void DRW_shape_cache_free(void);
-void DRW_shape_cache_reset(void);
/* 3D cursor */
struct GPUBatch *DRW_cache_cursor_get(bool crosshair_lines);
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 d6faeb16583..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;
@@ -1248,7 +1256,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
saved_elem_ranges[i] = cache->surface_per_mat[i]->elem;
/* Avoid deletion as the batch is owner. */
cache->surface_per_mat[i]->elem = NULL;
- cache->surface_per_mat[i]->owns_flag &= ~GPU_BATCH_OWNS_INDEX;
+ cache->surface_per_mat[i]->flag &= ~GPU_BATCH_OWNS_INDEX;
}
}
/* We can't discard batches at this point as they have been
@@ -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_cache_impl_metaball.c b/source/blender/draw/intern/draw_cache_impl_metaball.c
index 076d32ffe1f..5f0af06931e 100644
--- a/source/blender/draw/intern/draw_cache_impl_metaball.c
+++ b/source/blender/draw/intern/draw_cache_impl_metaball.c
@@ -155,7 +155,7 @@ static GPUVertBuf *mball_batch_cache_get_pos_and_normals(Object *ob, MetaBallBat
{
if (cache->pos_nor_in_order == NULL) {
ListBase *lb = &ob->runtime.curve_cache->disp;
- cache->pos_nor_in_order = MEM_callocN(sizeof(GPUVertBuf), __func__);
+ cache->pos_nor_in_order = GPU_vertbuf_create(GPU_USAGE_STATIC);
DRW_displist_vertbuf_create_pos_and_nor(lb, cache->pos_nor_in_order);
}
return cache->pos_nor_in_order;
@@ -165,7 +165,7 @@ static GPUIndexBuf *mball_batch_cache_get_edges_adj_lines(Object *ob, MetaBallBa
{
if (cache->edges_adj_lines == NULL) {
ListBase *lb = &ob->runtime.curve_cache->disp;
- cache->edges_adj_lines = MEM_callocN(sizeof(GPUVertBuf), __func__);
+ cache->edges_adj_lines = GPU_indexbuf_calloc();
DRW_displist_indexbuf_create_edges_adjacency_lines(
lb, cache->edges_adj_lines, &cache->is_manifold);
}
@@ -187,7 +187,7 @@ GPUBatch *DRW_metaball_batch_cache_get_triangles_with_normals(Object *ob)
if (cache->batch == NULL) {
ListBase *lb = &ob->runtime.curve_cache->disp;
- GPUIndexBuf *ibo = MEM_callocN(sizeof(GPUIndexBuf), __func__);
+ GPUIndexBuf *ibo = GPU_indexbuf_calloc();
DRW_displist_indexbuf_create_triangles_in_order(lb, ibo);
cache->batch = GPU_batch_create_ex(GPU_PRIM_TRIS,
mball_batch_cache_get_pos_and_normals(ob, cache),
@@ -234,10 +234,10 @@ GPUBatch *DRW_metaball_batch_cache_get_wireframes_face(Object *ob)
if (cache->face_wire.batch == NULL) {
ListBase *lb = &ob->runtime.curve_cache->disp;
- GPUVertBuf *vbo_wiredata = MEM_callocN(sizeof(GPUVertBuf), __func__);
+ GPUVertBuf *vbo_wiredata = GPU_vertbuf_create(GPU_USAGE_STATIC);
DRW_displist_vertbuf_create_wiredata(lb, vbo_wiredata);
- GPUIndexBuf *ibo = MEM_callocN(sizeof(GPUIndexBuf), __func__);
+ GPUIndexBuf *ibo = GPU_indexbuf_calloc();
DRW_displist_indexbuf_create_lines_in_order(lb, ibo);
cache->face_wire.batch = GPU_batch_create_ex(GPU_PRIM_LINES,
diff --git a/source/blender/draw/intern/draw_cache_impl_volume.c b/source/blender/draw/intern/draw_cache_impl_volume.c
index e07f5b33d58..825fec83cf1 100644
--- a/source/blender/draw/intern/draw_cache_impl_volume.c
+++ b/source/blender/draw/intern/draw_cache_impl_volume.c
@@ -163,7 +163,7 @@ static void drw_volume_wireframe_cb(
GPU_vertbuf_attr_fill_stride(cache->face_wire.pos_nor_in_order, nor_id, 0, &packed_normal);
/* Create wiredata. */
- GPUVertBuf *vbo_wiredata = MEM_callocN(sizeof(GPUVertBuf), __func__);
+ GPUVertBuf *vbo_wiredata = GPU_vertbuf_create(GPU_USAGE_STATIC);
DRW_vertbuf_create_wiredata(vbo_wiredata, totvert);
if (volume->display.wireframe_type == VOLUME_WIREFRAME_POINTS) {
diff --git a/source/blender/draw/intern/draw_cache_inline.h b/source/blender/draw/intern/draw_cache_inline.h
index 06d6f1afc31..0f0e1785a2a 100644
--- a/source/blender/draw/intern/draw_cache_inline.h
+++ b/source/blender/draw/intern/draw_cache_inline.h
@@ -48,7 +48,7 @@ BLI_INLINE GPUBatch *DRW_batch_request(GPUBatch **batch)
{
/* XXX TODO(fclem): We are writing to batch cache here. Need to make this thread safe. */
if (*batch == NULL) {
- *batch = GPU_batch_calloc(1);
+ *batch = GPU_batch_calloc();
}
return *batch;
}
@@ -69,11 +69,10 @@ BLI_INLINE bool DRW_batch_requested(GPUBatch *batch, int prim_type)
BLI_INLINE void DRW_ibo_request(GPUBatch *batch, GPUIndexBuf **ibo)
{
if (*ibo == NULL) {
- *ibo = MEM_callocN(sizeof(GPUIndexBuf), "GPUIndexBuf");
+ *ibo = GPU_indexbuf_calloc();
}
if (batch != NULL) {
- GPU_batch_vao_cache_clear(batch);
- batch->elem = *ibo;
+ GPU_batch_elembuf_set(batch, *ibo, false);
}
}
@@ -87,13 +86,12 @@ BLI_INLINE bool DRW_ibo_requested(GPUIndexBuf *ibo)
BLI_INLINE void DRW_vbo_request(GPUBatch *batch, GPUVertBuf **vbo)
{
if (*vbo == NULL) {
- *vbo = MEM_callocN(sizeof(GPUVertBuf), "GPUVertBuf");
+ *vbo = GPU_vertbuf_create(GPU_USAGE_STATIC);
}
if (batch != NULL) {
/* HACK set first vbo if not init. */
if (batch->verts[0] == NULL) {
- GPU_batch_vao_cache_clear(batch);
- batch->verts[0] = *vbo;
+ GPU_batch_vertbuf_add(batch, *vbo);
}
else {
/* HACK: bypass assert */
diff --git a/source/blender/draw/intern/draw_common.c b/source/blender/draw/intern/draw_common.c
index f0d73d5bb84..f80b5bd71fd 100644
--- a/source/blender/draw/intern/draw_common.c
+++ b/source/blender/draw/intern/draw_common.c
@@ -187,6 +187,7 @@ void DRW_globals_update(void)
/* M_SQRT2 to be at least the same size of the old square */
gb->sizeVertex = U.pixelsize *
(max_ff(1.0f, UI_GetThemeValuef(TH_VERTEX_SIZE) * (float)M_SQRT2 / 2.0f));
+ gb->sizeVertexGpencil = U.pixelsize * UI_GetThemeValuef(TH_GP_VERTEX_SIZE);
gb->sizeFaceDot = U.pixelsize * UI_GetThemeValuef(TH_FACEDOT_SIZE);
gb->sizeEdge = U.pixelsize * (1.0f / 2.0f); /* TODO Theme */
gb->sizeEdgeFix = U.pixelsize * (0.5f + 2.0f * (2.0f * (gb->sizeEdge * (float)M_SQRT1_2)));
@@ -213,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 6060dce47ac..645848e7fe0 100644
--- a/source/blender/draw/intern/draw_common.h
+++ b/source/blender/draw/intern/draw_common.h
@@ -150,8 +150,7 @@ typedef struct GlobalsUboStorage {
float sizeObjectCenter, sizeLightCenter, sizeLightCircle, sizeLightCircleShadow;
float sizeVertex, sizeEdge, sizeEdgeFix, sizeFaceDot;
float sizeChecker;
-
- float pad_globalsBlock;
+ float sizeVertexGpencil;
} GlobalsUboStorage;
/* Keep in sync with globalsBlock in shaders */
BLI_STATIC_ASSERT_ALIGN(GlobalsUboStorage, 16)
@@ -206,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_instance_data.c b/source/blender/draw/intern/draw_instance_data.c
index 5005f38c558..9d0727555b6 100644
--- a/source/blender/draw/intern/draw_instance_data.c
+++ b/source/blender/draw/intern/draw_instance_data.c
@@ -38,8 +38,6 @@
#include "BLI_utildefines.h"
#include "MEM_guardedalloc.h"
-#include "intern/gpu_primitive_private.h"
-
struct DRWInstanceData {
struct DRWInstanceData *next;
bool used; /* If this data is used or not. */
@@ -59,50 +57,50 @@ struct DRWInstanceDataList {
};
typedef struct DRWTempBufferHandle {
- /** Must be first for casting. */
- GPUVertBuf buf;
+ GPUVertBuf *buf;
/** Format pointer for reuse. */
GPUVertFormat *format;
/** Touched vertex length for resize. */
int *vert_len;
} DRWTempBufferHandle;
-static ListBase g_idatalists = {NULL, NULL};
+typedef struct DRWTempInstancingHandle {
+ /** Copy of geom but with the per-instance attributes. */
+ GPUBatch *batch;
+ /** Batch containing instancing attributes. */
+ GPUBatch *instancer;
+ /** Callbuffer to be used instead of instancer . */
+ GPUVertBuf *buf;
+ /** Original non-instanced batch pointer. */
+ GPUBatch *geom;
+} DRWTempInstancingHandle;
-/* -------------------------------------------------------------------- */
-/** \name Instance Buffer Management
- * \{ */
+static ListBase g_idatalists = {NULL, NULL};
-static void instance_batch_free(GPUBatch *geom, void *UNUSED(user_data))
+static void instancing_batch_references_add(GPUBatch *batch)
{
- if (geom->verts[0] == NULL) {
- /** XXX This is a false positive case.
- * The batch has been requested but not init yet
- * and there is a chance that it might become init.
- */
- return;
+ for (int i = 0; i < GPU_BATCH_VBO_MAX_LEN && batch->verts[i]; i++) {
+ GPU_vertbuf_handle_ref_add(batch->verts[i]);
+ }
+ for (int i = 0; i < GPU_BATCH_INST_VBO_MAX_LEN && batch->inst[i]; i++) {
+ GPU_vertbuf_handle_ref_add(batch->inst[i]);
}
+}
- /* Free all batches that use the same vbos before they are reused. */
- /* TODO: Make it thread safe! Batch freeing can happen from another thread. */
- /* FIXME: This is not really correct. The correct way would be to check based on
- * the vertex buffers. We assume the batch containing the VBO is being when it should. */
- /* PERF: This is doing a linear search. This can be very costly. */
- LISTBASE_FOREACH (DRWInstanceDataList *, data_list, &g_idatalists) {
- BLI_memblock *memblock = data_list->pool_instancing;
- BLI_memblock_iter iter;
- BLI_memblock_iternew(memblock, &iter);
- GPUBatch **batch_ptr;
- while ((batch_ptr = (GPUBatch **)BLI_memblock_iterstep(&iter))) {
- GPUBatch *batch = *batch_ptr;
- /* Only check verts[0] that's enough. */
- if (batch->verts[0] == geom->verts[0]) {
- GPU_batch_clear(batch);
- }
- }
+static void instancing_batch_references_remove(GPUBatch *batch)
+{
+ for (int i = 0; i < GPU_BATCH_VBO_MAX_LEN && batch->verts[i]; i++) {
+ GPU_vertbuf_handle_ref_remove(batch->verts[i]);
+ }
+ for (int i = 0; i < GPU_BATCH_INST_VBO_MAX_LEN && batch->inst[i]; i++) {
+ GPU_vertbuf_handle_ref_remove(batch->inst[i]);
}
}
+/* -------------------------------------------------------------------- */
+/** \name Instance Buffer Management
+ * \{ */
+
/**
* This manager allows to distribute existing batches for instancing
* attributes. This reduce the number of batches creation.
@@ -119,20 +117,23 @@ GPUVertBuf *DRW_temp_buffer_request(DRWInstanceDataList *idatalist,
BLI_assert(vert_len != NULL);
DRWTempBufferHandle *handle = BLI_memblock_alloc(idatalist->pool_buffers);
- GPUVertBuf *vert = &handle->buf;
- handle->vert_len = vert_len;
if (handle->format != format) {
handle->format = format;
- /* TODO/PERF: Save the allocated data from freeing to avoid reallocation. */
- GPU_vertbuf_clear(vert);
+ GPU_VERTBUF_DISCARD_SAFE(handle->buf);
+
+ GPUVertBuf *vert = GPU_vertbuf_create(GPU_USAGE_DYNAMIC);
GPU_vertbuf_init_with_format_ex(vert, format, GPU_USAGE_DYNAMIC);
GPU_vertbuf_data_alloc(vert, DRW_BUFFER_VERTS_CHUNK);
+
+ handle->buf = vert;
}
- return vert;
+ handle->vert_len = vert_len;
+ return handle->buf;
}
-/* NOTE: Does not return a valid drawable batch until DRW_instance_buffer_finish has run. */
+/* NOTE: Does not return a valid drawable batch until DRW_instance_buffer_finish has run.
+ * Initialization is delayed because instancer or geom could still not be initialized. */
GPUBatch *DRW_temp_batch_instance_request(DRWInstanceDataList *idatalist,
GPUVertBuf *buf,
GPUBatch *instancer,
@@ -143,17 +144,17 @@ GPUBatch *DRW_temp_batch_instance_request(DRWInstanceDataList *idatalist,
/* Only call with one of them. */
BLI_assert((instancer != NULL) != (buf != NULL));
- GPUBatch **batch_ptr = BLI_memblock_alloc(idatalist->pool_instancing);
- if (*batch_ptr == NULL) {
- *batch_ptr = GPU_batch_calloc(1);
+ DRWTempInstancingHandle *handle = BLI_memblock_alloc(idatalist->pool_instancing);
+ if (handle->batch == NULL) {
+ handle->batch = GPU_batch_calloc();
}
- GPUBatch *batch = *batch_ptr;
+ GPUBatch *batch = handle->batch;
bool instancer_compat = buf ? ((batch->inst[0] == buf) && (buf->vbo_id != 0)) :
- ((batch->inst[0] == instancer->inst[0]) &&
- (batch->inst[1] == instancer->inst[1]));
- bool is_compatible = (batch->gl_prim_type == geom->gl_prim_type) && instancer_compat &&
- (batch->phase == GPU_BATCH_READY_TO_DRAW) && (batch->elem == geom->elem);
+ ((batch->inst[0] == instancer->verts[0]) &&
+ (batch->inst[1] == instancer->verts[1]));
+ bool is_compatible = (batch->prim_type == geom->prim_type) && instancer_compat &&
+ (batch->flag & GPU_BATCH_BUILDING) == 0 && (batch->elem == geom->elem);
for (int i = 0; i < GPU_BATCH_VBO_MAX_LEN && is_compatible; i++) {
if (batch->verts[i] != geom->verts[i]) {
is_compatible = false;
@@ -161,15 +162,13 @@ GPUBatch *DRW_temp_batch_instance_request(DRWInstanceDataList *idatalist,
}
if (!is_compatible) {
+ instancing_batch_references_remove(batch);
GPU_batch_clear(batch);
- /* Save args and init later */
- batch->inst[0] = buf;
- batch->inst[1] = (void *)instancer; /* HACK to save the pointer without other alloc. */
- batch->phase = GPU_BATCH_READY_TO_BUILD;
- batch->verts[0] = (void *)geom; /* HACK to save the pointer without other alloc. */
-
- /* Make sure to free this batch if the instance geom gets free. */
- GPU_batch_callback_free_set(geom, &instance_batch_free, NULL);
+ /* Save args and init later. */
+ batch->flag = GPU_BATCH_BUILDING;
+ handle->buf = buf;
+ handle->instancer = instancer;
+ handle->geom = geom;
}
return batch;
}
@@ -179,14 +178,14 @@ GPUBatch *DRW_temp_batch_request(DRWInstanceDataList *idatalist,
GPUVertBuf *buf,
GPUPrimType prim_type)
{
- GPUBatch **batch_ptr = BLI_memblock_alloc(idatalist->pool_instancing);
+ GPUBatch **batch_ptr = BLI_memblock_alloc(idatalist->pool_batching);
if (*batch_ptr == NULL) {
- *batch_ptr = GPU_batch_calloc(1);
+ *batch_ptr = GPU_batch_calloc();
}
GPUBatch *batch = *batch_ptr;
bool is_compatible = (batch->verts[0] == buf) && (buf->vbo_id != 0) &&
- (batch->gl_prim_type == convert_prim_type_to_gl(prim_type));
+ (batch->prim_type == prim_type);
if (!is_compatible) {
GPU_batch_clear(batch);
GPU_batch_init(batch, prim_type, buf, NULL);
@@ -197,7 +196,13 @@ GPUBatch *DRW_temp_batch_request(DRWInstanceDataList *idatalist,
static void temp_buffer_handle_free(DRWTempBufferHandle *handle)
{
handle->format = NULL;
- GPU_vertbuf_clear(&handle->buf);
+ GPU_VERTBUF_DISCARD_SAFE(handle->buf);
+}
+
+static void temp_instancing_handle_free(DRWTempInstancingHandle *handle)
+{
+ instancing_batch_references_remove(handle->batch);
+ GPU_BATCH_DISCARD_SAFE(handle->batch);
}
static void temp_batch_free(GPUBatch **batch)
@@ -215,23 +220,22 @@ void DRW_instance_buffer_finish(DRWInstanceDataList *idatalist)
if (handle->vert_len != NULL) {
uint vert_len = *(handle->vert_len);
uint target_buf_size = ((vert_len / DRW_BUFFER_VERTS_CHUNK) + 1) * DRW_BUFFER_VERTS_CHUNK;
- if (target_buf_size < handle->buf.vertex_alloc) {
- GPU_vertbuf_data_resize(&handle->buf, target_buf_size);
+ if (target_buf_size < handle->buf->vertex_alloc) {
+ GPU_vertbuf_data_resize(handle->buf, target_buf_size);
}
- GPU_vertbuf_data_len_set(&handle->buf, vert_len);
- GPU_vertbuf_use(&handle->buf); /* Send data. */
+ GPU_vertbuf_data_len_set(handle->buf, vert_len);
+ GPU_vertbuf_use(handle->buf); /* Send data. */
}
}
/* Finish pending instancing batches. */
- GPUBatch **batch_ptr;
+ DRWTempInstancingHandle *handle_inst;
BLI_memblock_iternew(idatalist->pool_instancing, &iter);
- while ((batch_ptr = BLI_memblock_iterstep(&iter))) {
- GPUBatch *batch = *batch_ptr;
- if (batch && batch->phase == GPU_BATCH_READY_TO_BUILD) {
- GPUVertBuf *inst_buf = batch->inst[0];
- /* HACK see DRW_temp_batch_instance_request. */
- GPUBatch *inst_batch = (void *)batch->inst[1];
- GPUBatch *geom = (void *)batch->verts[0];
+ while ((handle_inst = BLI_memblock_iterstep(&iter))) {
+ GPUBatch *batch = handle_inst->batch;
+ if (batch && batch->flag == GPU_BATCH_BUILDING) {
+ GPUVertBuf *inst_buf = handle_inst->buf;
+ GPUBatch *inst_batch = handle_inst->instancer;
+ GPUBatch *geom = handle_inst->geom;
GPU_batch_copy(batch, geom);
if (inst_batch != NULL) {
for (int i = 0; i < GPU_BATCH_INST_VBO_MAX_LEN && inst_batch->verts[i]; i++) {
@@ -241,11 +245,14 @@ void DRW_instance_buffer_finish(DRWInstanceDataList *idatalist)
else {
GPU_batch_instbuf_add_ex(batch, inst_buf, false);
}
+ /* Add reference to avoid comparing pointers (in DRW_temp_batch_request) that could
+ * potentially be the same. This will delay the freeing of the GPUVertBuf itself. */
+ instancing_batch_references_add(batch);
}
}
/* Resize pools and free unused. */
BLI_memblock_clear(idatalist->pool_buffers, (MemblockValFreeFP)temp_buffer_handle_free);
- BLI_memblock_clear(idatalist->pool_instancing, (MemblockValFreeFP)temp_batch_free);
+ BLI_memblock_clear(idatalist->pool_instancing, (MemblockValFreeFP)temp_instancing_handle_free);
BLI_memblock_clear(idatalist->pool_batching, (MemblockValFreeFP)temp_batch_free);
}
@@ -318,7 +325,7 @@ DRWInstanceDataList *DRW_instance_data_list_create(void)
DRWInstanceDataList *idatalist = MEM_callocN(sizeof(DRWInstanceDataList), "DRWInstanceDataList");
idatalist->pool_batching = BLI_memblock_create(sizeof(GPUBatch *));
- idatalist->pool_instancing = BLI_memblock_create(sizeof(GPUBatch *));
+ idatalist->pool_instancing = BLI_memblock_create(sizeof(DRWTempInstancingHandle));
idatalist->pool_buffers = BLI_memblock_create(sizeof(DRWTempBufferHandle));
BLI_addtail(&g_idatalists, idatalist);
@@ -341,7 +348,7 @@ void DRW_instance_data_list_free(DRWInstanceDataList *idatalist)
}
BLI_memblock_destroy(idatalist->pool_buffers, (MemblockValFreeFP)temp_buffer_handle_free);
- BLI_memblock_destroy(idatalist->pool_instancing, (MemblockValFreeFP)temp_batch_free);
+ BLI_memblock_destroy(idatalist->pool_instancing, (MemblockValFreeFP)temp_instancing_handle_free);
BLI_memblock_destroy(idatalist->pool_batching, (MemblockValFreeFP)temp_batch_free);
BLI_remlink(&g_idatalists, idatalist);
diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c
index 4a5e07476a9..336a3d61479 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);
}
}
@@ -1461,7 +1462,7 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph,
DRW_hair_init();
/* No framebuffer allowed before drawing. */
- BLI_assert(GPU_framebuffer_active_get() == NULL);
+ BLI_assert(GPU_framebuffer_active_get() == GPU_framebuffer_back_get());
/* Init engines */
drw_engines_init();
@@ -1593,10 +1594,8 @@ 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_set_func(GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA_PREMULT);
}
GPU_matrix_identity_set();
@@ -1606,9 +1605,7 @@ void DRW_draw_render_loop_offscreen(struct Depsgraph *depsgraph,
if (draw_background) {
/* Reset default. */
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
/* Free temporary viewport. */
@@ -1706,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();
@@ -1758,8 +1755,6 @@ void DRW_render_to_image(RenderEngine *engine, struct Depsgraph *depsgraph)
BLI_rcti_init(&render_rect, 0, size[0], 0, size[1]);
}
- /* Set the default Blender draw state */
- GPU_state_init();
/* Reset state before drawing */
DRW_state_reset();
@@ -1922,7 +1917,7 @@ static struct DRWSelectBuffer {
static void draw_select_framebuffer_depth_only_setup(const int size[2])
{
if (g_select_buffer.framebuffer_depth_only == NULL) {
- g_select_buffer.framebuffer_depth_only = GPU_framebuffer_create();
+ g_select_buffer.framebuffer_depth_only = GPU_framebuffer_create("framebuffer_depth_only");
}
if ((g_select_buffer.texture_depth != NULL) &&
@@ -2443,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)) {
@@ -2475,7 +2470,7 @@ void DRW_draw_depth_object(
GPU_SHADER_CFG_DEFAULT;
GPU_batch_program_set_builtin_with_config(batch, GPU_SHADER_3D_DEPTH_ONLY, sh_cfg);
if (world_clip_planes != NULL) {
- GPU_batch_uniform_4fv_array(batch, "WorldClipPlanes", 6, world_clip_planes[0]);
+ GPU_batch_uniform_4fv_array(batch, "WorldClipPlanes", 6, world_clip_planes);
}
GPU_batch_draw(batch);
@@ -2490,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();
}
@@ -2788,8 +2783,6 @@ void DRW_opengl_context_create(void)
if (!G.background) {
immActivate();
}
- /* Set default Blender OpenGL state */
- GPU_state_init();
/* So we activate the window's one afterwards. */
wm_window_reset_drawable();
}
@@ -2824,7 +2817,6 @@ void DRW_opengl_context_enable_ex(bool restore)
if (!G.background) {
immActivate();
}
- BLF_batch_reset();
}
}
}
@@ -2888,16 +2880,16 @@ void DRW_gpu_render_context_enable(void *re_gpu_context)
BLI_assert(!BLI_thread_is_main());
GPU_context_active_set(re_gpu_context);
- DRW_shape_cache_reset(); /* XXX fix that too. */
}
/* Needs to be called BEFORE DRW_opengl_render_context_disable() */
void DRW_gpu_render_context_disable(void *UNUSED(re_gpu_context))
{
- DRW_shape_cache_reset(); /* XXX fix that too. */
GPU_context_active_set(NULL);
}
+/** \} */
+
#ifdef WITH_XR_OPENXR
/* XXX
@@ -2933,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 92a01cbbe04..c0bcb0e679f 100644
--- a/source/blender/draw/intern/draw_manager.h
+++ b/source/blender/draw/intern/draw_manager.h
@@ -35,9 +35,10 @@
#include "GPU_batch.h"
#include "GPU_context.h"
+#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"
@@ -307,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 e3860b1bfb2..1d15548e1ed 100644
--- a/source/blender/draw/intern/draw_manager_exec.c
+++ b/source/blender/draw/intern/draw_manager_exec.c
@@ -31,8 +31,8 @@
#include "GPU_extensions.h"
#include "GPU_platform.h"
-#include "intern/gpu_primitive_private.h"
-#include "intern/gpu_shader_private.h"
+#include "GPU_shader.h"
+#include "GPU_state.h"
#ifdef USE_GPU_SELECT
# include "GPU_select.h"
@@ -54,8 +54,6 @@ typedef struct DRWCommandsState {
int resource_id;
int base_inst;
int inst_count;
- int v_first;
- int v_count;
bool neg_scale;
/* Resource location. */
int obmats_loc;
@@ -80,313 +78,179 @@ 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;
}
-#define CHANGED_TO(f) \
- ((DST.state_lock & (f)) ? \
- 0 : \
- (((DST.state & (f)) ? ((state & (f)) ? 0 : -1) : ((state & (f)) ? 1 : 0))))
-
-#define CHANGED_ANY(f) (((DST.state & (f)) != (state & (f))) && ((DST.state_lock & (f)) == 0))
-
-#define CHANGED_ANY_STORE_VAR(f, enabled) \
- (((DST.state & (f)) != (enabled = (state & (f)))) && (((DST.state_lock & (f)) == 0)))
+ eGPUWriteMask write_mask = 0;
+ eGPUBlend blend = 0;
+ eGPUFaceCullTest culling_test = 0;
+ eGPUDepthTest depth_test = 0;
+ eGPUStencilTest stencil_test = 0;
+ eGPUStencilOp stencil_op = 0;
+ eGPUProvokingVertex provoking_vert = 0;
- /* Depth Write */
- {
- int test;
- if ((test = CHANGED_TO(DRW_STATE_WRITE_DEPTH))) {
- GPU_depth_mask(test == 1);
- }
+ if (state & DRW_STATE_WRITE_DEPTH) {
+ write_mask |= GPU_WRITE_DEPTH;
}
-
- /* Stencil Write */
- {
- DRWState test;
- if (CHANGED_ANY_STORE_VAR(DRW_STATE_WRITE_STENCIL_ENABLED, test)) {
- /* Stencil Write */
- if (test) {
- glStencilMask(0xFF);
- switch (test) {
- case DRW_STATE_WRITE_STENCIL:
- glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
- break;
- case DRW_STATE_WRITE_STENCIL_SHADOW_PASS:
- glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_INCR_WRAP);
- glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_DECR_WRAP);
- break;
- case DRW_STATE_WRITE_STENCIL_SHADOW_FAIL:
- glStencilOpSeparate(GL_BACK, GL_KEEP, GL_DECR_WRAP, GL_KEEP);
- glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_INCR_WRAP, GL_KEEP);
- break;
- default:
- BLI_assert(0);
- }
- }
- else {
- glStencilMask(0x00);
- glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
- }
- }
+ if (state & DRW_STATE_WRITE_COLOR) {
+ write_mask |= GPU_WRITE_COLOR;
}
-
- /* Color Write */
- {
- int test;
- if ((test = CHANGED_TO(DRW_STATE_WRITE_COLOR))) {
- if (test == 1) {
- GPU_color_mask(true, true, true, true);
- }
- else {
- GPU_color_mask(false, false, false, false);
- }
- }
+ if (state & DRW_STATE_WRITE_STENCIL_ENABLED) {
+ write_mask |= GPU_WRITE_STENCIL;
}
- /* Raster Discard */
- {
- if (CHANGED_ANY(DRW_STATE_RASTERIZER_ENABLED)) {
- if ((state & DRW_STATE_RASTERIZER_ENABLED) != 0) {
- glDisable(GL_RASTERIZER_DISCARD);
- }
- else {
- glEnable(GL_RASTERIZER_DISCARD);
- }
- }
+ switch (state & (DRW_STATE_CULL_BACK | DRW_STATE_CULL_FRONT)) {
+ case DRW_STATE_CULL_BACK:
+ culling_test = GPU_CULL_BACK;
+ break;
+ case DRW_STATE_CULL_FRONT:
+ culling_test = GPU_CULL_FRONT;
+ break;
+ default:
+ culling_test = GPU_CULL_NONE;
+ break;
}
- /* Cull */
- {
- DRWState test;
- if (CHANGED_ANY_STORE_VAR(DRW_STATE_CULL_BACK | DRW_STATE_CULL_FRONT, test)) {
- if (test) {
- glEnable(GL_CULL_FACE);
+ switch (state & DRW_STATE_DEPTH_TEST_ENABLED) {
+ case DRW_STATE_DEPTH_LESS:
+ depth_test = GPU_DEPTH_LESS;
+ break;
+ case DRW_STATE_DEPTH_LESS_EQUAL:
+ depth_test = GPU_DEPTH_LESS_EQUAL;
+ break;
+ case DRW_STATE_DEPTH_EQUAL:
+ depth_test = GPU_DEPTH_EQUAL;
+ break;
+ case DRW_STATE_DEPTH_GREATER:
+ depth_test = GPU_DEPTH_GREATER;
+ break;
+ case DRW_STATE_DEPTH_GREATER_EQUAL:
+ depth_test = GPU_DEPTH_GREATER_EQUAL;
+ break;
+ case DRW_STATE_DEPTH_ALWAYS:
+ depth_test = GPU_DEPTH_ALWAYS;
+ break;
+ default:
+ depth_test = GPU_DEPTH_NONE;
+ break;
+ }
- if ((state & DRW_STATE_CULL_BACK) != 0) {
- glCullFace(GL_BACK);
- }
- else if ((state & DRW_STATE_CULL_FRONT) != 0) {
- glCullFace(GL_FRONT);
- }
- else {
- BLI_assert(0);
- }
- }
- else {
- glDisable(GL_CULL_FACE);
- }
- }
+ switch (state & DRW_STATE_WRITE_STENCIL_ENABLED) {
+ case DRW_STATE_WRITE_STENCIL:
+ stencil_op = GPU_STENCIL_OP_REPLACE;
+ GPU_stencil_write_mask_set(0xFF);
+ break;
+ case DRW_STATE_WRITE_STENCIL_SHADOW_PASS:
+ stencil_op = GPU_STENCIL_OP_COUNT_DEPTH_PASS;
+ GPU_stencil_write_mask_set(0xFF);
+ break;
+ case DRW_STATE_WRITE_STENCIL_SHADOW_FAIL:
+ stencil_op = GPU_STENCIL_OP_COUNT_DEPTH_FAIL;
+ GPU_stencil_write_mask_set(0xFF);
+ break;
+ default:
+ stencil_op = GPU_STENCIL_OP_NONE;
+ GPU_stencil_write_mask_set(0x00);
+ break;
}
- /* Depth Test */
- {
- DRWState test;
- if (CHANGED_ANY_STORE_VAR(DRW_STATE_DEPTH_TEST_ENABLED, test)) {
- if (test) {
- glEnable(GL_DEPTH_TEST);
-
- switch (test) {
- case DRW_STATE_DEPTH_LESS:
- glDepthFunc(GL_LESS);
- break;
- case DRW_STATE_DEPTH_LESS_EQUAL:
- glDepthFunc(GL_LEQUAL);
- break;
- case DRW_STATE_DEPTH_EQUAL:
- glDepthFunc(GL_EQUAL);
- break;
- case DRW_STATE_DEPTH_GREATER:
- glDepthFunc(GL_GREATER);
- break;
- case DRW_STATE_DEPTH_GREATER_EQUAL:
- glDepthFunc(GL_GEQUAL);
- break;
- case DRW_STATE_DEPTH_ALWAYS:
- glDepthFunc(GL_ALWAYS);
- break;
- default:
- BLI_assert(0);
- }
- }
- else {
- glDisable(GL_DEPTH_TEST);
- }
- }
+ switch (state & DRW_STATE_STENCIL_TEST_ENABLED) {
+ case DRW_STATE_STENCIL_ALWAYS:
+ stencil_test = GPU_STENCIL_ALWAYS;
+ break;
+ case DRW_STATE_STENCIL_EQUAL:
+ stencil_test = GPU_STENCIL_EQUAL;
+ break;
+ case DRW_STATE_STENCIL_NEQUAL:
+ stencil_test = GPU_STENCIL_NEQUAL;
+ break;
+ default:
+ stencil_test = GPU_STENCIL_NONE;
+ break;
}
- /* Stencil Test */
- {
- int test;
- if (CHANGED_ANY_STORE_VAR(DRW_STATE_STENCIL_TEST_ENABLED, test)) {
- if (test) {
- glEnable(GL_STENCIL_TEST);
- }
- else {
- glDisable(GL_STENCIL_TEST);
- }
- }
+ switch (state & DRW_STATE_BLEND_ENABLED) {
+ case DRW_STATE_BLEND_ADD:
+ blend = GPU_BLEND_ADDITIVE;
+ break;
+ case DRW_STATE_BLEND_ADD_FULL:
+ blend = GPU_BLEND_ADDITIVE_PREMULT;
+ break;
+ case DRW_STATE_BLEND_ALPHA:
+ blend = GPU_BLEND_ALPHA;
+ break;
+ case DRW_STATE_BLEND_ALPHA_PREMUL:
+ blend = GPU_BLEND_ALPHA_PREMULT;
+ break;
+ case DRW_STATE_BLEND_BACKGROUND:
+ blend = GPU_BLEND_BACKGROUND;
+ break;
+ case DRW_STATE_BLEND_OIT:
+ blend = GPU_BLEND_OIT;
+ break;
+ case DRW_STATE_BLEND_MUL:
+ blend = GPU_BLEND_MULTIPLY;
+ break;
+ case DRW_STATE_BLEND_SUB:
+ blend = GPU_BLEND_SUBTRACT;
+ break;
+ case DRW_STATE_BLEND_CUSTOM:
+ blend = GPU_BLEND_CUSTOM;
+ break;
+ case DRW_STATE_LOGIC_INVERT:
+ blend = GPU_BLEND_INVERT;
+ break;
+ default:
+ blend = GPU_BLEND_NONE;
+ break;
}
- /* Blending (all buffer) */
- {
- int test;
- if (CHANGED_ANY_STORE_VAR(DRW_STATE_BLEND_ALPHA | DRW_STATE_BLEND_ALPHA_PREMUL |
- DRW_STATE_BLEND_ADD | DRW_STATE_BLEND_MUL |
- DRW_STATE_BLEND_ADD_FULL | DRW_STATE_BLEND_OIT |
- DRW_STATE_BLEND_BACKGROUND | DRW_STATE_BLEND_CUSTOM |
- DRW_STATE_LOGIC_INVERT | DRW_STATE_BLEND_SUB,
- test)) {
- if (test) {
- glEnable(GL_BLEND);
-
- switch (test) {
- case DRW_STATE_BLEND_ALPHA:
- glBlendFuncSeparate(GL_SRC_ALPHA,
- GL_ONE_MINUS_SRC_ALPHA, /* RGB */
- GL_ONE,
- GL_ONE_MINUS_SRC_ALPHA); /* Alpha */
- break;
- case DRW_STATE_BLEND_BACKGROUND:
- /* Special blend to add color under and multiply dst by alpha. */
- glBlendFuncSeparate(GL_ONE_MINUS_DST_ALPHA,
- GL_SRC_ALPHA, /* RGB */
- GL_ZERO,
- GL_SRC_ALPHA); /* Alpha */
- break;
- case DRW_STATE_BLEND_ALPHA_PREMUL:
- glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
- break;
- case DRW_STATE_BLEND_MUL:
- glBlendFunc(GL_DST_COLOR, GL_ZERO);
- break;
- case DRW_STATE_BLEND_OIT:
- glBlendFuncSeparate(GL_ONE,
- GL_ONE, /* RGB */
- GL_ZERO,
- GL_ONE_MINUS_SRC_ALPHA); /* Alpha */
- break;
- case DRW_STATE_BLEND_ADD:
- /* Do not let alpha accumulate but premult the source RGB by it. */
- glBlendFuncSeparate(GL_SRC_ALPHA,
- GL_ONE, /* RGB */
- GL_ZERO,
- GL_ONE); /* Alpha */
- break;
- case DRW_STATE_BLEND_ADD_FULL:
- /* Let alpha accumulate. */
- glBlendFunc(GL_ONE, GL_ONE);
- break;
- case DRW_STATE_BLEND_SUB:
- glBlendFunc(GL_ONE, GL_ONE);
- break;
- case DRW_STATE_BLEND_CUSTOM:
- /* Custom blend parameters using dual source blending.
- * Can only be used with one Draw Buffer. */
- glBlendFunc(GL_ONE, GL_SRC1_COLOR);
- break;
- case DRW_STATE_LOGIC_INVERT:
- /* Replace logic op by blend func to support floating point framebuffer. */
- glBlendFuncSeparate(GL_ONE_MINUS_DST_COLOR,
- GL_ZERO, /* RGB */
- GL_ZERO,
- GL_ONE); /* Alpha */
- break;
- default:
- BLI_assert(0);
- }
+ GPU_state_set(
+ write_mask, blend, culling_test, depth_test, stencil_test, stencil_op, provoking_vert);
- if (test == DRW_STATE_BLEND_SUB) {
- glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
- }
- else {
- glBlendEquation(GL_FUNC_ADD);
- }
- }
- else {
- glDisable(GL_BLEND);
- glBlendFunc(GL_ONE, GL_ONE); /* Don't multiply incoming color by alpha. */
- }
- }
+ if (state & DRW_STATE_SHADOW_OFFSET) {
+ GPU_shadow_offset(true);
}
-
- /* Shadow Bias */
- {
- int test;
- if ((test = CHANGED_TO(DRW_STATE_SHADOW_OFFSET))) {
- if (test == 1) {
- glEnable(GL_POLYGON_OFFSET_FILL);
- glEnable(GL_POLYGON_OFFSET_LINE);
- /* 2.0 Seems to be the lowest possible slope bias that works in every case. */
- glPolygonOffset(2.0f, 1.0f);
- }
- else {
- glDisable(GL_POLYGON_OFFSET_FILL);
- glDisable(GL_POLYGON_OFFSET_LINE);
- }
- }
+ else {
+ GPU_shadow_offset(false);
}
- /* In Front objects selection */
- {
- int test;
- if ((test = CHANGED_TO(DRW_STATE_IN_FRONT_SELECT))) {
- if (test == 1) {
- /* XXX `GPU_depth_range` is not a perfect solution
- * since very distant geometries can still be occluded.
- * Also the depth test precision of these geometries is impaired.
- * However, it solves the selection for the vast majority of cases. */
- GPU_depth_range(0.0f, 0.01f);
- }
- else {
- GPU_depth_range(0.0f, 1.0f);
- }
- }
+ /* TODO this should be part of shader state. */
+ if (state & DRW_STATE_CLIP_PLANES) {
+ GPU_clip_distances(DST.view_active->clip_planes_len);
}
-
- /* Clip Planes */
- {
- int test;
- if ((test = CHANGED_TO(DRW_STATE_CLIP_PLANES))) {
- if (test == 1) {
- GPU_clip_distances(DST.view_active->clip_planes_len);
- }
- else {
- GPU_clip_distances(0);
- }
- }
+ else {
+ GPU_clip_distances(0);
}
- /* Program Points Size */
- {
- int test;
- if ((test = CHANGED_TO(DRW_STATE_PROGRAM_POINT_SIZE))) {
- if (test == 1) {
- GPU_program_point_size(true);
- }
- else {
- GPU_program_point_size(false);
- }
- }
+ if (state & DRW_STATE_IN_FRONT_SELECT) {
+ /* XXX `GPU_depth_range` is not a perfect solution
+ * since very distant geometries can still be occluded.
+ * Also the depth test precision of these geometries is impaired.
+ * However, it solves the selection for the vast majority of cases. */
+ GPU_depth_range(0.0f, 0.01f);
+ }
+ else {
+ GPU_depth_range(0.0f, 1.0f);
}
- /* Provoking Vertex */
- {
- int test;
- if ((test = CHANGED_TO(DRW_STATE_FIRST_VERTEX_CONVENTION))) {
- if (test == 1) {
- glProvokingVertex(GL_FIRST_VERTEX_CONVENTION);
- }
- else {
- glProvokingVertex(GL_LAST_VERTEX_CONVENTION);
- }
- }
+ if (state & DRW_STATE_PROGRAM_POINT_SIZE) {
+ GPU_program_point_size(true);
+ }
+ else {
+ GPU_program_point_size(false);
}
-#undef CHANGED_TO
-#undef CHANGED_ANY
-#undef CHANGED_ANY_STORE_VAR
+ if (state & DRW_STATE_FIRST_VERTEX_CONVENTION) {
+ GPU_provoking_vertex(GPU_VERTEX_FIRST);
+ }
+ else {
+ GPU_provoking_vertex(GPU_VERTEX_LAST);
+ }
DST.state = state;
}
@@ -398,17 +262,9 @@ static void drw_stencil_state_set(uint write_mask, uint reference, uint compare_
* stencil_value being the value stored in the stencil buffer.
* - (write-mask & reference) is what gets written if the test condition is fulfilled.
**/
- glStencilMask(write_mask);
- DRWState stencil_test = DST.state & DRW_STATE_STENCIL_TEST_ENABLED;
- if (stencil_test == DRW_STATE_STENCIL_ALWAYS) {
- glStencilFunc(GL_ALWAYS, reference, compare_mask);
- }
- else if (stencil_test == DRW_STATE_STENCIL_EQUAL) {
- glStencilFunc(GL_EQUAL, reference, compare_mask);
- }
- else if (stencil_test == DRW_STATE_STENCIL_NEQUAL) {
- glStencilFunc(GL_NOTEQUAL, reference, compare_mask);
- }
+ GPU_stencil_write_mask_set(write_mask);
+ GPU_stencil_reference_set(reference);
+ GPU_stencil_compare_mask_set(compare_mask);
}
/* Reset state to not interfer with other UI drawcall */
@@ -446,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);
}
/** \} */
@@ -663,18 +516,9 @@ BLI_INLINE void draw_legacy_matrix_update(DRWShadingGroup *shgroup,
BLI_INLINE void draw_geometry_bind(DRWShadingGroup *shgroup, GPUBatch *geom)
{
- /* XXX hacking #GPUBatch. we don't want to call glUseProgram! (huge performance loss) */
- if (DST.batch) {
- DST.batch->program_in_use = false;
- }
-
DST.batch = geom;
- GPU_batch_set_shader_no_bind(geom, shgroup->shader);
-
- geom->program_in_use = true; /* XXX hacking #GPUBatch */
-
- GPU_batch_bind(geom);
+ GPU_batch_set_shader(geom, shgroup->shader);
}
BLI_INLINE void draw_geometry_execute(DRWShadingGroup *shgroup,
@@ -714,77 +558,15 @@ BLI_INLINE void draw_indirect_call(DRWShadingGroup *shgroup, DRWCommandsState *s
GPU_draw_list_submit(DST.draw_list);
draw_geometry_bind(shgroup, state->batch);
}
- GPU_draw_list_command_add(
- DST.draw_list, state->v_first, state->v_count, state->base_inst, state->inst_count);
+ GPU_draw_list_append(DST.draw_list, state->batch, state->base_inst, state->inst_count);
}
/* Fallback when unsupported */
else {
- draw_geometry_execute(shgroup,
- state->batch,
- state->v_first,
- state->v_count,
- state->base_inst,
- state->inst_count,
- state->baseinst_loc);
+ draw_geometry_execute(
+ shgroup, state->batch, 0, 0, state->base_inst, state->inst_count, state->baseinst_loc);
}
}
-#ifndef NDEBUG
-/**
- * Opengl specification is strict on buffer binding.
- *
- * " If any active uniform block is not backed by a
- * sufficiently large buffer object, the results of shader
- * execution are undefined, and may result in GL interruption or
- * termination. " - Opengl 3.3 Core Specification
- *
- * For now we only check if the binding is correct. Not the size of
- * the bound ubo.
- *
- * See T55475.
- * */
-static bool ubo_bindings_validate(DRWShadingGroup *shgroup)
-{
- bool valid = true;
-# ifdef DEBUG_UBO_BINDING
- /* Check that all active uniform blocks have a non-zero buffer bound. */
- GLint program = 0;
- GLint active_blocks = 0;
-
- glGetIntegerv(GL_CURRENT_PROGRAM, &program);
- glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCKS, &active_blocks);
-
- for (uint i = 0; i < active_blocks; i++) {
- int binding = 0;
- int buffer = 0;
-
- glGetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_BINDING, &binding);
- glGetIntegeri_v(GL_UNIFORM_BUFFER_BINDING, binding, &buffer);
-
- if (buffer == 0) {
- char blockname[64];
- glGetActiveUniformBlockName(program, i, sizeof(blockname), NULL, blockname);
-
- if (valid) {
- printf("Trying to draw with missing UBO binding.\n");
- valid = false;
- }
-
- 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);
- }
- }
-# endif
- return valid;
-}
-#endif
-
static void draw_update_uniforms(DRWShadingGroup *shgroup,
DRWCommandsState *state,
bool *use_tfeedback)
@@ -816,18 +598,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;
@@ -838,8 +620,8 @@ static void draw_update_uniforms(DRWShadingGroup *shgroup,
break;
case DRW_UNIFORM_TFEEDBACK_TARGET:
BLI_assert(uni->pvalue && (*use_tfeedback == false));
- *use_tfeedback = GPU_shader_transform_feedback_enable(
- shgroup->shader, ((GPUVertBuf *)uni->pvalue)->vbo_id);
+ *use_tfeedback = GPU_shader_transform_feedback_enable(shgroup->shader,
+ ((GPUVertBuf *)uni->pvalue));
break;
/* Legacy/Fallback support. */
case DRW_UNIFORM_BASE_INSTANCE:
@@ -854,8 +636,6 @@ static void draw_update_uniforms(DRWShadingGroup *shgroup,
}
}
}
-
- BLI_assert(ubo_bindings_validate(shgroup));
}
BLI_INLINE void draw_select_buffer(DRWShadingGroup *shgroup,
@@ -873,10 +653,10 @@ BLI_INLINE void draw_select_buffer(DRWShadingGroup *shgroup,
/* Batching */
if (!is_instancing) {
/* FIXME: Meh a bit nasty. */
- if (batch->gl_prim_type == convert_prim_type_to_gl(GPU_PRIM_TRIS)) {
+ if (batch->prim_type == GPU_PRIM_TRIS) {
count = 3;
}
- else if (batch->gl_prim_type == convert_prim_type_to_gl(GPU_PRIM_LINES)) {
+ else if (batch->prim_type == GPU_PRIM_LINES) {
count = 2;
}
}
@@ -927,27 +707,22 @@ 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);
if (state->resource_chunk != chunk) {
if (state->chunkid_loc != -1) {
- GPU_shader_uniform_int(NULL, state->chunkid_loc, chunk);
+ 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;
}
@@ -955,7 +730,7 @@ static void draw_call_resource_bind(DRWCommandsState *state, const DRWResourceHa
if (state->resourceid_loc != -1) {
int id = DRW_handle_id_get(handle);
if (state->resource_id != id) {
- GPU_shader_uniform_int(NULL, state->resourceid_loc, id);
+ GPU_shader_uniform_int(DST.shader, state->resourceid_loc, id);
state->resource_id = id;
}
}
@@ -1015,8 +790,6 @@ static void draw_call_batching_start(DRWCommandsState *state)
state->resource_id = -1;
state->base_inst = 0;
state->inst_count = 0;
- state->v_first = 0;
- state->v_count = 0;
state->batch = NULL;
state->select_id = -1;
@@ -1039,15 +812,10 @@ static void draw_call_batching_do(DRWShadingGroup *shgroup,
draw_call_batching_flush(shgroup, state);
state->batch = call->batch;
- state->v_first = (call->batch->elem) ? call->batch->elem->index_start : 0;
- state->v_count = (call->batch->elem) ? call->batch->elem->index_len :
- call->batch->verts[0]->vertex_len;
state->inst_count = 1;
state->base_inst = id;
draw_call_resource_bind(state, &call->handle);
-
- GPU_draw_list_init(DST.draw_list, state->batch);
}
/* Is the id consecutive? */
else if (id != state->base_inst + state->inst_count) {
@@ -1070,13 +838,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]);
}
}
@@ -1106,15 +874,11 @@ 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);
DST.shader = shgroup->shader;
- /* XXX hacking gawain */
- if (DST.batch) {
- DST.batch->program_in_use = false;
- }
DST.batch = NULL;
}
@@ -1152,19 +916,14 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state)
switch (cmd_type) {
case DRW_CMD_CLEAR:
- GPU_framebuffer_clear(
-#ifndef NDEBUG
- GPU_framebuffer_active_get(),
-#else
- NULL,
-#endif
- cmd->clear.clear_channels,
- (float[4]){cmd->clear.r / 255.0f,
- cmd->clear.g / 255.0f,
- cmd->clear.b / 255.0f,
- cmd->clear.a / 255.0f},
- cmd->clear.depth,
- cmd->clear.stencil);
+ GPU_framebuffer_clear(GPU_framebuffer_active_get(),
+ cmd->clear.clear_channels,
+ (float[4]){cmd->clear.r / 255.0f,
+ cmd->clear.g / 255.0f,
+ cmd->clear.b / 255.0f,
+ cmd->clear.a / 255.0f},
+ cmd->clear.depth,
+ cmd->clear.stencil);
break;
case DRW_CMD_DRWSTATE:
state.drw_state_enabled |= cmd->state.enable;
@@ -1246,7 +1005,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;
@@ -1286,7 +1045,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);
@@ -1305,7 +1064,6 @@ static void drw_draw_pass_ex(DRWPass *pass,
}
if (DST.batch) {
- DST.batch->program_in_use = false;
DST.batch = NULL;
}
@@ -1324,7 +1082,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_shader.c b/source/blender/draw/intern/draw_manager_shader.c
index 1c260721efb..7602bbb39ac 100644
--- a/source/blender/draw/intern/draw_manager_shader.c
+++ b/source/blender/draw/intern/draw_manager_shader.c
@@ -312,16 +312,18 @@ void DRW_deferred_shader_remove(GPUMaterial *mat)
/** \{ */
-GPUShader *DRW_shader_create(const char *vert,
- const char *geom,
- const char *frag,
- const char *defines)
+GPUShader *DRW_shader_create_ex(
+ const char *vert, const char *geom, const char *frag, const char *defines, const char *name)
{
- return GPU_shader_create(vert, frag, geom, NULL, defines, __func__);
+ return GPU_shader_create(vert, frag, geom, NULL, defines, name);
}
-GPUShader *DRW_shader_create_with_lib(
- const char *vert, const char *geom, const char *frag, const char *lib, const char *defines)
+GPUShader *DRW_shader_create_with_lib_ex(const char *vert,
+ const char *geom,
+ const char *frag,
+ const char *lib,
+ const char *defines,
+ const char *name)
{
GPUShader *sh;
char *vert_with_lib = NULL;
@@ -334,7 +336,7 @@ GPUShader *DRW_shader_create_with_lib(
geom_with_lib = BLI_string_joinN(lib, geom);
}
- sh = GPU_shader_create(vert_with_lib, frag_with_lib, geom_with_lib, NULL, defines, __func__);
+ sh = GPU_shader_create(vert_with_lib, frag_with_lib, geom_with_lib, NULL, defines, name);
MEM_freeN(vert_with_lib);
MEM_freeN(frag_with_lib);
@@ -345,18 +347,19 @@ GPUShader *DRW_shader_create_with_lib(
return sh;
}
-GPUShader *DRW_shader_create_with_shaderlib(const char *vert,
- const char *geom,
- const char *frag,
- const DRWShaderLibrary *lib,
- const char *defines)
+GPUShader *DRW_shader_create_with_shaderlib_ex(const char *vert,
+ const char *geom,
+ const char *frag,
+ const DRWShaderLibrary *lib,
+ const char *defines,
+ const char *name)
{
GPUShader *sh;
char *vert_with_lib = DRW_shader_library_create_shader_string(lib, vert);
char *frag_with_lib = DRW_shader_library_create_shader_string(lib, frag);
char *geom_with_lib = (geom) ? DRW_shader_library_create_shader_string(lib, geom) : NULL;
- sh = GPU_shader_create(vert_with_lib, frag_with_lib, geom_with_lib, NULL, defines, __func__);
+ sh = GPU_shader_create(vert_with_lib, frag_with_lib, geom_with_lib, NULL, defines, name);
MEM_SAFE_FREE(vert_with_lib);
MEM_SAFE_FREE(frag_with_lib);
@@ -383,22 +386,22 @@ GPUShader *DRW_shader_create_with_transform_feedback(const char *vert,
__func__);
}
-GPUShader *DRW_shader_create_fullscreen(const char *frag, const char *defines)
+GPUShader *DRW_shader_create_fullscreen_ex(const char *frag, const char *defines, const char *name)
{
- return GPU_shader_create(
- datatoc_common_fullscreen_vert_glsl, frag, NULL, NULL, defines, __func__);
+ return GPU_shader_create(datatoc_common_fullscreen_vert_glsl, frag, NULL, NULL, defines, name);
}
-GPUShader *DRW_shader_create_fullscreen_with_shaderlib(const char *frag,
- const DRWShaderLibrary *lib,
- const char *defines)
+GPUShader *DRW_shader_create_fullscreen_with_shaderlib_ex(const char *frag,
+ const DRWShaderLibrary *lib,
+ const char *defines,
+ const char *name)
{
GPUShader *sh;
char *vert = datatoc_common_fullscreen_vert_glsl;
char *frag_with_lib = DRW_shader_library_create_shader_string(lib, frag);
- sh = GPU_shader_create(vert, frag_with_lib, NULL, NULL, defines, __func__);
+ sh = GPU_shader_create(vert, frag_with_lib, NULL, NULL, defines, name);
MEM_SAFE_FREE(frag_with_lib);
diff --git a/source/blender/gpu/intern/gpu_primitive_private.h b/source/blender/draw/intern/draw_manager_testing.h
index e91eec18786..f8b5dd5af46 100644
--- a/source/blender/gpu/intern/gpu_primitive_private.h
+++ b/source/blender/draw/intern/draw_manager_testing.h
@@ -13,24 +13,26 @@
* 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.
+ * Copyright 2016, Blender Foundation.
*/
/** \file
- * \ingroup gpu
- *
- * GPU geometric primitives
+ * \ingroup draw
*/
+/* Internal API only for test cases. */
+
#pragma once
+#include "GPU_shader.h"
+
#ifdef __cplusplus
extern "C" {
#endif
-/* TODO(fclem) move to OGL backend */
-GLenum convert_prim_type_to_gl(GPUPrimType);
+#ifdef WITH_OPENGL_DRAW_TESTS
+void DRW_draw_state_init_gtests(eGPUShaderConfig sh_cfg);
+#endif
#ifdef __cplusplus
}
diff --git a/source/blender/draw/intern/draw_view.c b/source/blender/draw/intern/draw_view.c
index 1458ff5341c..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];
@@ -123,7 +123,7 @@ void DRW_draw_cursor(void)
/* Draw nice Anti Aliased cursor. */
GPU_line_width(1.0f);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
GPU_line_smooth(true);
float eps = 1e-5f;
@@ -188,7 +188,7 @@ void DRW_draw_cursor(void)
GPU_batch_draw(cursor_batch);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
GPU_line_smooth(false);
GPU_matrix_pop();
GPU_matrix_projection_set(original_proj);
diff --git a/source/blender/draw/intern/shaders/common_globals_lib.glsl b/source/blender/draw/intern/shaders/common_globals_lib.glsl
index 40a527a6ba4..bd1b1fb6f3a 100644
--- a/source/blender/draw/intern/shaders/common_globals_lib.glsl
+++ b/source/blender/draw/intern/shaders/common_globals_lib.glsl
@@ -117,8 +117,7 @@ layout(std140) uniform globalsBlock
float sizeEdgeFix;
float sizeFaceDot;
float sizeChecker;
-
- float pad_globalsBlock;
+ float sizeVertexGpencil;
};
#define sizeViewportInv (sizeViewport.zw)
diff --git a/source/blender/draw/intern/smaa_textures.h b/source/blender/draw/intern/smaa_textures.h
index 7556f3a1952..fcf0ced1eed 100644
--- a/source/blender/draw/intern/smaa_textures.h
+++ b/source/blender/draw/intern/smaa_textures.h
@@ -15081,4 +15081,3 @@ static const unsigned char searchTexBytes[] = {
};
/* clang-format off */
-
diff --git a/source/blender/draw/tests/shaders_test.cc b/source/blender/draw/tests/shaders_test.cc
new file mode 100644
index 00000000000..124049a13e2
--- /dev/null
+++ b/source/blender/draw/tests/shaders_test.cc
@@ -0,0 +1,279 @@
+/* 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_GLSettings glSettings = {0};
+ ghost_system = GHOST_CreateSystem();
+ ghost_context = GHOST_CreateOpenGLContext(ghost_system, glSettings);
+ 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_bloom_blit_get(false), nullptr);
+ EXPECT_NE(EEVEE_shaders_bloom_blit_get(true), nullptr);
+ EXPECT_NE(EEVEE_shaders_bloom_downsample_get(false), nullptr);
+ EXPECT_NE(EEVEE_shaders_bloom_downsample_get(true), nullptr);
+ EXPECT_NE(EEVEE_shaders_bloom_upsample_get(false), nullptr);
+ EXPECT_NE(EEVEE_shaders_bloom_upsample_get(true), nullptr);
+ EXPECT_NE(EEVEE_shaders_bloom_resolve_get(false), nullptr);
+ EXPECT_NE(EEVEE_shaders_bloom_resolve_get(true), nullptr);
+ 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_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c
index 53401e0c05a..0a464e6709a 100644
--- a/source/blender/editors/animation/anim_channels_defines.c
+++ b/source/blender/editors/animation/anim_channels_defines.c
@@ -4386,9 +4386,7 @@ void ANIM_channel_draw(
}
/* set blending again, as may not be set in previous step */
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
/* step 1) draw backdrop ........................................... */
if (acf->draw_backdrop) {
@@ -4437,7 +4435,7 @@ void ANIM_channel_draw(
}
/* turn off blending, since not needed anymore... */
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
/* icon is drawn as widget now... */
if (acf->has_setting(ac, ale, ACHANNEL_SETTING_VISIBLE)) {
diff --git a/source/blender/editors/animation/anim_draw.c b/source/blender/editors/animation/anim_draw.c
index b2f687b49af..73df0518f06 100644
--- a/source/blender/editors/animation/anim_draw.c
+++ b/source/blender/editors/animation/anim_draw.c
@@ -98,9 +98,7 @@ void ANIM_draw_previewrange(const bContext *C, View2D *v2d, int end_frame_width)
/* only draw this if preview range is set */
if (PRVRANGEON) {
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
@@ -121,7 +119,7 @@ void ANIM_draw_previewrange(const bContext *C, View2D *v2d, int end_frame_width)
immUnbindProgram();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
}
@@ -133,9 +131,7 @@ void ANIM_draw_previewrange(const bContext *C, View2D *v2d, int end_frame_width)
void ANIM_draw_framerange(Scene *scene, View2D *v2d)
{
/* draw darkened area outside of active timeline frame range */
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
@@ -151,7 +147,7 @@ void ANIM_draw_framerange(Scene *scene, View2D *v2d)
immRectf(pos, v2d->cur.xmin, v2d->cur.ymin, v2d->cur.xmax, v2d->cur.ymax);
}
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
/* thin lines where the actual frames are */
immUniformThemeColorShade(TH_BACK, -60);
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/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c
index bcdbf4c74f0..3bfa3b9d5be 100644
--- a/source/blender/editors/animation/anim_markers.c
+++ b/source/blender/editors/animation/anim_markers.c
@@ -499,16 +499,14 @@ static void draw_marker(
marker_color_get(marker, text_color, line_color);
- GPU_blend(true);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
draw_marker_line(line_color, xpos, UI_DPI_FAC * 20, region_height);
int icon_id = marker_get_icon_id(marker, flag);
UI_icon_draw(xpos - 0.55f * UI_DPI_ICON_SIZE, UI_DPI_FAC * 18, icon_id);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
float name_y = UI_DPI_FAC * 18;
/* Give an offset to the marker name when selected,
@@ -529,13 +527,11 @@ static void draw_markers_background(rctf *rect)
immUniformColor4ubv(shade);
- GPU_blend(true);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
immRectf(pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
immUnbindProgram();
}
diff --git a/source/blender/editors/animation/anim_motion_paths.c b/source/blender/editors/animation/anim_motion_paths.c
index 4c10c66dfa6..2a37302872a 100644
--- a/source/blender/editors/animation/anim_motion_paths.c
+++ b/source/blender/editors/animation/anim_motion_paths.c
@@ -69,9 +69,9 @@ typedef struct MPathTarget {
/* ........ */
/* update scene for current frame */
-static void motionpaths_calc_update_scene(Main *bmain, struct Depsgraph *depsgraph)
+static void motionpaths_calc_update_scene(struct Depsgraph *depsgraph)
{
- BKE_scene_graph_update_for_newframe(depsgraph, bmain);
+ BKE_scene_graph_update_for_newframe(depsgraph);
}
Depsgraph *animviz_depsgraph_build(Main *bmain,
@@ -91,11 +91,11 @@ Depsgraph *animviz_depsgraph_build(Main *bmain,
}
/* Build graph from all requested IDs. */
- DEG_graph_build_from_ids(depsgraph, bmain, scene, view_layer, ids, num_ids);
+ DEG_graph_build_from_ids(depsgraph, ids, num_ids);
MEM_freeN(ids);
/* Update once so we can access pointers of evaluated animation data. */
- motionpaths_calc_update_scene(bmain, depsgraph);
+ motionpaths_calc_update_scene(depsgraph);
return depsgraph;
}
@@ -471,7 +471,7 @@ void animviz_calc_motionpaths(Depsgraph *depsgraph,
}
else {
/* Update relevant data for new frame. */
- motionpaths_calc_update_scene(bmain, depsgraph);
+ motionpaths_calc_update_scene(depsgraph);
}
/* perform baking for targets */
@@ -484,7 +484,7 @@ void animviz_calc_motionpaths(Depsgraph *depsgraph,
* We always have to restore the current frame though. */
CFRA = cfra;
if (range != ANIMVIZ_CALC_RANGE_CURRENT_FRAME && restore) {
- motionpaths_calc_update_scene(bmain, depsgraph);
+ motionpaths_calc_update_scene(depsgraph);
}
if (is_active_depsgraph) {
diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c
index c69f3ce3e3a..4d6d7fa3ad5 100644
--- a/source/blender/editors/animation/keyframes_draw.c
+++ b/source/blender/editors/animation/keyframes_draw.c
@@ -692,7 +692,7 @@ static void draw_keylist(View2D *v2d,
const float smaller_sz = 0.35f * icon_sz;
const float ipo_sz = 0.1f * icon_sz;
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
/* locked channels are less strongly shown, as feedback for locked channels in DopeSheet */
/* TODO: allow this opacity factor to be themed? */
@@ -848,7 +848,7 @@ static void draw_keylist(View2D *v2d,
}
}
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
/* *************************** Channel Drawing Funcs *************************** */
diff --git a/source/blender/editors/animation/time_scrub_ui.c b/source/blender/editors/animation/time_scrub_ui.c
index edc36326c57..0615e21c4a5 100644
--- a/source/blender/editors/animation/time_scrub_ui.c
+++ b/source/blender/editors/animation/time_scrub_ui.c
@@ -66,13 +66,11 @@ static void draw_background(const rcti *rect)
immUniformThemeColor(TH_TIME_SCRUB_BACKGROUND);
- GPU_blend(true);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
immRectf(pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
immUnbindProgram();
}
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/armature/pose_transform.c b/source/blender/editors/armature/pose_transform.c
index a6cf8552ca4..b09015096a6 100644
--- a/source/blender/editors/armature/pose_transform.c
+++ b/source/blender/editors/armature/pose_transform.c
@@ -23,6 +23,7 @@
#include "DNA_anim_types.h"
#include "DNA_armature_types.h"
+#include "DNA_constraint_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
@@ -338,6 +339,46 @@ static void applyarmature_process_selected_recursive(bArmature *arm,
}
}
+/* Reset bone constraint so that it is correct after the pose has been applied. */
+static void applyarmature_reset_bone_constraint(const bConstraint *constraint)
+{
+ /* TODO(Sybren): This function needs too much knowledge of the internals of specific constraints.
+ * When it is extended with one or two more constraints, move the functionality into a
+ * bConstraintTypeInfo callback function. */
+ switch (constraint->type) {
+ case CONSTRAINT_TYPE_STRETCHTO: {
+ bStretchToConstraint *stretch_to = (bStretchToConstraint *)constraint->data;
+ stretch_to->orglength = 0.0f; /* Force recalculation on next evaluation. */
+ break;
+ }
+ default:
+ /* Most constraints don't need resetting. */
+ break;
+ }
+}
+
+/* Reset bone constraints of the given pose channel so that they are correct after the pose has
+ * been applied. */
+static void applyarmature_reset_bone_constraints(const bPoseChannel *pchan)
+{
+ LISTBASE_FOREACH (bConstraint *, constraint, &pchan->constraints) {
+ applyarmature_reset_bone_constraint(constraint);
+ }
+}
+
+/* Reset all (or only selected) bone constraints so that they are correct after the pose has been
+ * applied. */
+static void applyarmature_reset_constraints(bPose *pose, const bool use_selected)
+{
+ for (bPoseChannel *pchan = pose->chanbase.first; pchan; pchan = pchan->next) {
+ BLI_assert(pchan->bone != NULL);
+ if (use_selected && (pchan->bone->flag & BONE_SELECTED) == 0) {
+ continue;
+ }
+ applyarmature_reset_bone_constraints(pchan);
+ }
+}
+
/* set the current pose as the restpose */
static int apply_armature_pose2bones_exec(bContext *C, wmOperator *op)
{
@@ -416,6 +457,9 @@ static int apply_armature_pose2bones_exec(bContext *C, wmOperator *op)
/* fix parenting of objects which are bone-parented */
applyarmature_fix_boneparents(C, scene, ob);
+ /* For the affected bones, reset specific constraints that are now known to be invalid. */
+ applyarmature_reset_constraints(pose, use_selected);
+
/* note, notifier might evolve */
WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE);
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 748bf040fbb..889041daacf 100644
--- a/source/blender/editors/curve/editcurve_paint.c
+++ b/source/blender/editors/curve/editcurve_paint.c
@@ -419,8 +419,8 @@ 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_blend(true);
+ GPU_depth_test(GPU_DEPTH_NONE);
+ GPU_blend(GPU_BLEND_ALPHA);
GPU_line_smooth(true);
GPU_line_width(3.0f);
@@ -441,8 +441,8 @@ static void curve_draw_stroke_3d(const struct bContext *UNUSED(C),
immEnd();
/* Reset defaults */
- GPU_depth_test(true);
- GPU_blend(false);
+ GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
+ GPU_blend(GPU_BLEND_NONE);
GPU_line_smooth(false);
immUnbindProgram();
@@ -666,7 +666,7 @@ static void curve_draw_exec_precalc(wmOperator *op)
selem_prev = selem;
}
scale_px = ((len_3d > 0.0f) && (len_2d > 0.0f)) ? (len_3d / len_2d) : 0.0f;
- float error_threshold = (cps->error_threshold * U.pixelsize) * scale_px;
+ float error_threshold = (cps->error_threshold * U.dpi_fac) * scale_px;
RNA_property_float_set(op->ptr, prop, error_threshold);
}
@@ -685,7 +685,7 @@ static void curve_draw_exec_precalc(wmOperator *op)
}
if (len_squared_v2v2(selem_first->mval, selem_last->mval) <=
- square_f(STROKE_CYCLIC_DIST_PX * U.pixelsize)) {
+ square_f(STROKE_CYCLIC_DIST_PX * U.dpi_fac)) {
use_cyclic = true;
}
}
diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt
index 0dcb8de37f1..b8cc704eb23 100644
--- a/source/blender/editors/datafiles/CMakeLists.txt
+++ b/source/blender/editors/datafiles/CMakeLists.txt
@@ -744,6 +744,7 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES
ops.pose.relax
ops.sculpt.border_hide
ops.sculpt.border_mask
+ ops.sculpt.cloth_filter
ops.sculpt.lasso_mask
ops.sculpt.mesh_filter
ops.sequencer.blade
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/gizmo_library/gizmo_types/arrow3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c
index 341f43d0662..d4d9f9bf424 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c
@@ -195,9 +195,9 @@ static void arrow_draw_intern(ArrowGizmo3D *arrow, const bool select, const bool
GPU_matrix_push();
GPU_matrix_mul(matrix_final);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
arrow_draw_geom(arrow, select, color);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
GPU_matrix_pop();
@@ -207,9 +207,9 @@ static void arrow_draw_intern(ArrowGizmo3D *arrow, const bool select, const bool
GPU_matrix_push();
GPU_matrix_mul(inter->init_matrix_final);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
arrow_draw_geom(arrow, select, (const float[4]){0.5f, 0.5f, 0.5f, 0.5f});
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
GPU_matrix_pop();
}
diff --git a/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c
index c5231b3cd96..13f3903f0b2 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c
@@ -195,7 +195,7 @@ static void button2d_draw_intern(const bContext *C,
}
else {
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
if (draw_options & ED_GIZMO_BUTTON_SHOW_BACKDROP) {
const float fill_alpha = RNA_float_get(gz->ptr, "backdrop_fill_alpha");
@@ -226,10 +226,10 @@ static void button2d_draw_intern(const bContext *C,
float color_contrast[4];
copy_v3_fl(color_contrast, rgb_to_grayscale(color) < 0.2f ? 1 : 0);
color_contrast[3] = color[3];
- GPU_batch_uniform_4f(button->shape_batch[i], "color", UNPACK4(color_contrast));
+ GPU_shader_uniform_4f(button->shape_batch[i]->shader, "color", UNPACK4(color_contrast));
}
else {
- GPU_batch_uniform_4f(button->shape_batch[i], "color", UNPACK4(color));
+ GPU_shader_uniform_4f(button->shape_batch[i]->shader, "color", UNPACK4(color));
}
GPU_batch_draw(button->shape_batch[i]);
@@ -265,7 +265,7 @@ static void button2d_draw_intern(const bContext *C,
UI_icon_draw_alpha(pos[0], pos[1], button->icon, alpha);
GPU_polygon_smooth(true);
}
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
if (need_to_pop) {
@@ -283,9 +283,9 @@ static void gizmo_button2d_draw(const bContext *C, wmGizmo *gz)
{
const bool is_highlight = (gz->state & WM_GIZMO_STATE_HIGHLIGHT) != 0;
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
button2d_draw_intern(C, gz, false, is_highlight);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
static int gizmo_button2d_test_select(bContext *C, wmGizmo *gz, const int mval[2])
diff --git a/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c
index 406d66dfd8f..be5f488d759 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c
@@ -626,14 +626,14 @@ static void gizmo_cage2d_draw_intern(wmGizmo *gz,
/* Handy for quick testing draw (if it's outside bounds). */
if (false) {
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
immUniformColor4fv((const float[4]){1, 1, 1, 0.5f});
float s = 0.5f;
immRectf(pos, -s, -s, s, s);
immUnbindProgram();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
if (select) {
@@ -722,7 +722,7 @@ static void gizmo_cage2d_draw_intern(wmGizmo *gz,
float color[4], black[3] = {0, 0, 0};
gizmo_color_get(gz, highlight, color);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
float outline_line_width = gz->line_width + 3.0f;
cage2d_draw_circle_wire(&r, margin, black, transform_flag, draw_options, outline_line_width);
@@ -732,7 +732,7 @@ static void gizmo_cage2d_draw_intern(wmGizmo *gz,
cage2d_draw_circle_handles(&r, margin, color, transform_flag, true);
cage2d_draw_circle_handles(&r, margin, (const float[3]){0, 0, 0}, transform_flag, false);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
else {
BLI_assert(0);
diff --git a/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c
index 0bc65fe10a5..644a4247d84 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c
@@ -303,14 +303,14 @@ static void gizmo_cage3d_draw_intern(
/* Handy for quick testing draw (if it's outside bounds). */
if (false) {
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4fv((const float[4]){1, 1, 1, 0.5f});
float s = 0.5f;
immRectf(pos, -s, -s, s, s);
immUnbindProgram();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
if (select) {
@@ -382,7 +382,7 @@ static void gizmo_cage3d_draw_intern(
float color[4], black[3] = {0, 0, 0};
gizmo_color_get(gz, highlight, color);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
cage3d_draw_circle_wire(
size_real, margin, black, transform_flag, draw_options, gz->line_width + 3.0f);
@@ -395,7 +395,7 @@ static void gizmo_cage3d_draw_intern(
cage3d_draw_circle_handles(rv3d, matrix_final, size_real, margin, color, true, 40);
GPU_polygon_smooth(false);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
else {
BLI_assert(0);
diff --git a/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c
index 5d7c3a9e717..e1860d5d0fd 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c
@@ -482,9 +482,9 @@ static void gizmo_dial_draw(const bContext *C, wmGizmo *gz)
clip_plane[3] += DIAL_CLIP_BIAS;
}
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
dial_draw_intern(C, gz, false, is_highlight, clip_plane);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
static int gizmo_dial_modal(bContext *C,
diff --git a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c
index db57a33f543..ad7036f4460 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c
@@ -207,9 +207,9 @@ static void move3d_draw_intern(const bContext *C,
GPU_matrix_mul(matrix_align);
}
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
move_geom_draw(gz, color, select, draw_options);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
GPU_matrix_pop();
if (gz->interaction_data) {
@@ -220,9 +220,9 @@ static void move3d_draw_intern(const bContext *C,
GPU_matrix_mul(matrix_align);
}
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
move_geom_draw(gz, (const float[4]){0.5f, 0.5f, 0.5f, 0.5f}, select, draw_options);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
GPU_matrix_pop();
}
}
@@ -240,9 +240,9 @@ static void gizmo_move_draw(const bContext *C, wmGizmo *gz)
(void)is_modal;
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
move3d_draw_intern(C, gz, false, is_highlight);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
static int gizmo_move_modal(bContext *C,
diff --git a/source/blender/editors/gizmo_library/gizmo_types/primitive3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/primitive3d_gizmo.c
index 48b2e3348c9..177687b6afd 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/primitive3d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/primitive3d_gizmo.c
@@ -96,9 +96,9 @@ static void gizmo_primitive_draw_intern(wmGizmo *gz,
GPU_matrix_push();
GPU_matrix_mul(matrix_final);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
gizmo_primitive_draw_geom(color_inner, color_outer, draw_style);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
GPU_matrix_pop();
@@ -112,9 +112,9 @@ static void gizmo_primitive_draw_intern(wmGizmo *gz,
GPU_matrix_push();
GPU_matrix_mul(inter->init_matrix_final);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
gizmo_primitive_draw_geom(color_inner, color_outer, draw_style);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
GPU_matrix_pop();
}
diff --git a/source/blender/editors/gpencil/annotate_draw.c b/source/blender/editors/gpencil/annotate_draw.c
index df479325027..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);
}
@@ -734,9 +734,7 @@ static void annotation_draw_data(
GPU_line_smooth(true);
/* turn on alpha-blending */
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
/* Do not write to depth (avoid self-occlusion). */
bool prev_depth_mask = GPU_depth_mask_get();
@@ -746,8 +744,8 @@ static void annotation_draw_data(
annotation_draw_data_layers(gpd, offsx, offsy, winx, winy, cfra, dflag);
/* turn off alpha blending, then smooth lines */
- GPU_blend(false); // alpha blending
- GPU_line_smooth(false); // smooth lines
+ GPU_blend(GPU_BLEND_NONE); // alpha blending
+ GPU_line_smooth(false); // smooth lines
GPU_depth_mask(prev_depth_mask);
}
diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c
index 30e4fe0b531..8237e6cfd62 100644
--- a/source/blender/editors/gpencil/annotate_paint.c
+++ b/source/blender/editors/gpencil/annotate_paint.c
@@ -1700,9 +1700,7 @@ static void annotation_draw_eraser(bContext *UNUSED(C), int x, int y, void *p_pt
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
GPU_line_smooth(true);
- GPU_blend(true);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
immUniformColor4ub(255, 100, 100, 20);
imm_draw_circle_fill_2d(shdr_pos, x, y, p->radius, 40);
@@ -1730,7 +1728,7 @@ static void annotation_draw_eraser(bContext *UNUSED(C), int x, int y, void *p_pt
immUnbindProgram();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
GPU_line_smooth(false);
}
}
@@ -1768,7 +1766,7 @@ static void annotation_draw_stabilizer(bContext *C, int x, int y, void *p_ptr)
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
GPU_line_smooth(true);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
GPU_line_width(1.25f);
const float color[3] = {1.0f, 0.39f, 0.39f};
@@ -1793,7 +1791,7 @@ static void annotation_draw_stabilizer(bContext *C, int x, int y, void *p_ptr)
immEnd();
/* Returns back all GPU settings */
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
GPU_line_smooth(false);
immUnbindProgram();
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_edit.c b/source/blender/editors/gpencil/gpencil_edit.c
index 99e98df3397..1bbba6c4b8a 100644
--- a/source/blender/editors/gpencil/gpencil_edit.c
+++ b/source/blender/editors/gpencil/gpencil_edit.c
@@ -3645,7 +3645,6 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
Scene *scene = CTX_data_scene(C);
- Main *bmain = CTX_data_main(C);
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
ARegion *region = CTX_wm_region(C);
int oldframe = (int)DEG_get_ctime(depsgraph);
@@ -3669,7 +3668,7 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op)
if ((mode == GP_REPROJECT_SURFACE) && (cfra_prv != gpf_->framenum)) {
cfra_prv = gpf_->framenum;
CFRA = gpf_->framenum;
- BKE_scene_graph_update_for_newframe(depsgraph, bmain);
+ BKE_scene_graph_update_for_newframe(depsgraph);
}
ED_gpencil_stroke_reproject(depsgraph, &gsc, sctx, gpl, gpf_, gps, mode, keep_original);
@@ -3679,7 +3678,7 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op)
/* return frame state and DB to original state */
CFRA = oldframe;
- BKE_scene_graph_update_for_newframe(depsgraph, bmain);
+ BKE_scene_graph_update_for_newframe(depsgraph);
if (sctx != NULL) {
ED_transform_snap_object_context_destroy(sctx);
diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c
index 2855f2cc0d7..247cc218c2f 100644
--- a/source/blender/editors/gpencil/gpencil_fill.c
+++ b/source/blender/editors/gpencil/gpencil_fill.c
@@ -237,6 +237,7 @@ static void gpencil_draw_datablock(tGPDfill *tgpf, const float ink[4])
bGPdata *gpd = tgpf->gpd;
Brush *brush = tgpf->brush;
BrushGpencilSettings *brush_settings = brush->gpencil_settings;
+ ToolSettings *ts = tgpf->scene->toolsettings;
tGPDdraw tgpw;
tgpw.rv3d = tgpf->rv3d;
@@ -251,7 +252,7 @@ static void gpencil_draw_datablock(tGPDfill *tgpf, const float ink[4])
tgpw.disable_fill = 1;
tgpw.dflag |= (GP_DRAWFILLS_ONLY3D | GP_DRAWFILLS_NOSTATUS);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
bGPDlayer *gpl_active = BKE_gpencil_layer_active_get(gpd);
BLI_assert(gpl_active != NULL);
@@ -309,7 +310,14 @@ static void gpencil_draw_datablock(tGPDfill *tgpf, const float ink[4])
/* if active layer and no keyframe, create a new one */
if (gpl == tgpf->gpl) {
if ((gpl->actframe == NULL) || (gpl->actframe->framenum != tgpf->active_cfra)) {
- BKE_gpencil_layer_frame_get(gpl, tgpf->active_cfra, GP_GETFRAME_ADD_NEW);
+ short add_frame_mode;
+ if (ts->gpencil_flags & GP_TOOL_FLAG_RETAIN_LAST) {
+ add_frame_mode = GP_GETFRAME_ADD_COPY;
+ }
+ else {
+ add_frame_mode = GP_GETFRAME_ADD_NEW;
+ }
+ BKE_gpencil_layer_frame_get(gpl, tgpf->active_cfra, add_frame_mode);
}
}
@@ -363,7 +371,7 @@ static void gpencil_draw_datablock(tGPDfill *tgpf, const float ink[4])
}
}
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
/* draw strokes in offscreen buffer */
@@ -446,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);
@@ -459,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/gpencil/gpencil_merge.c b/source/blender/editors/gpencil/gpencil_merge.c
index 53dbc1620d0..4aae0b97e05 100644
--- a/source/blender/editors/gpencil/gpencil_merge.c
+++ b/source/blender/editors/gpencil/gpencil_merge.c
@@ -584,42 +584,14 @@ static int gpencil_stroke_merge_material_exec(bContext *C, wmOperator *op)
const float val_threshold = RNA_float_get(op->ptr, "val_threshold");
/* Review materials. */
- GHash *mat_table = BLI_ghash_int_new(__func__);
-
short *totcol = BKE_object_material_len_p(ob);
if (totcol == 0) {
return OPERATOR_CANCELLED;
}
- bool changed = BKE_gpencil_merge_materials_table_get(
- ob, hue_threshold, sat_threshold, val_threshold, mat_table);
-
- int removed = BLI_ghash_len(mat_table);
-
- /* Update stroke material index. */
- if (changed) {
- CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
- LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
- LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- if (ED_gpencil_stroke_can_use(C, gps) == false) {
- continue;
- }
- if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) {
- continue;
- }
-
- if (BLI_ghash_haskey(mat_table, POINTER_FROM_INT(gps->mat_nr))) {
- int *idx = BLI_ghash_lookup(mat_table, POINTER_FROM_INT(gps->mat_nr));
- gps->mat_nr = POINTER_AS_INT(idx);
- }
- }
- }
- }
- CTX_DATA_END;
- }
-
- /* Free hash memory. */
- BLI_ghash_free(mat_table, NULL, NULL);
+ int removed;
+ bool changed = BKE_gpencil_merge_materials(
+ ob, hue_threshold, sat_threshold, val_threshold, &removed);
/* notifiers */
if (changed) {
diff --git a/source/blender/editors/gpencil/gpencil_mesh.c b/source/blender/editors/gpencil/gpencil_mesh.c
index a6088e31ff8..f4e40c2670f 100644
--- a/source/blender/editors/gpencil/gpencil_mesh.c
+++ b/source/blender/editors/gpencil/gpencil_mesh.c
@@ -246,7 +246,7 @@ static int gpencil_bake_mesh_animation_exec(bContext *C, wmOperator *op)
/* Move scene to new frame. */
CFRA = i;
- BKE_scene_graph_update_for_newframe(depsgraph, bmain);
+ BKE_scene_graph_update_for_newframe(depsgraph);
/* Loop all objects in the list. */
LISTBASE_FOREACH (GpBakeOb *, elem, &list) {
@@ -287,7 +287,7 @@ static int gpencil_bake_mesh_animation_exec(bContext *C, wmOperator *op)
/* Return scene frame state and DB to original state. */
CFRA = oldframe;
- BKE_scene_graph_update_for_newframe(depsgraph, bmain);
+ BKE_scene_graph_update_for_newframe(depsgraph);
/* Remove unused materials. */
int actcol = ob_gpencil->actcol;
diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c
index 680e7b37d31..3f62c3ec8c0 100644
--- a/source/blender/editors/gpencil/gpencil_paint.c
+++ b/source/blender/editors/gpencil/gpencil_paint.c
@@ -2268,9 +2268,7 @@ static void gpencil_draw_eraser(bContext *UNUSED(C), int x, int y, void *p_ptr)
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
GPU_line_smooth(true);
- GPU_blend(true);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
immUniformColor4ub(255, 100, 100, 20);
imm_draw_circle_fill_2d(shdr_pos, x, y, p->radius, 40);
@@ -2298,7 +2296,7 @@ static void gpencil_draw_eraser(bContext *UNUSED(C), int x, int y, void *p_ptr)
immUnbindProgram();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
GPU_line_smooth(false);
}
}
diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c
index 75f08c37cba..a4c8fc770e2 100644
--- a/source/blender/editors/gpencil/gpencil_primitive.c
+++ b/source/blender/editors/gpencil/gpencil_primitive.c
@@ -109,7 +109,7 @@
/* ************************************************ */
/* Core/Shared Utilities */
-const static EnumPropertyItem gpencil_primitive_type[] = {
+static const EnumPropertyItem gpencil_primitive_type[] = {
{GP_STROKE_BOX, "BOX", 0, "Box", ""},
{GP_STROKE_LINE, "LINE", 0, "Line", ""},
{GP_STROKE_POLYLINE, "POLYLINE", 0, "Polyline", ""},
diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c
index dcba8f16ee9..82d5eedf576 100644
--- a/source/blender/editors/gpencil/gpencil_select.c
+++ b/source/blender/editors/gpencil/gpencil_select.c
@@ -1114,6 +1114,8 @@ static int gpencil_generic_select_exec(
gps->flag &= ~GP_STROKE_SELECT;
}
CTX_DATA_END;
+
+ changed = true;
}
/* select/deselect points */
diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c
index 5aae10a5db5..8b77709bacb 100644
--- a/source/blender/editors/gpencil/gpencil_utils.c
+++ b/source/blender/editors/gpencil/gpencil_utils.c
@@ -1760,9 +1760,7 @@ void ED_gpencil_brush_draw_eraser(Brush *brush, int x, int y)
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
GPU_line_smooth(true);
- GPU_blend(true);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
immUniformColor4ub(255, 100, 100, 20);
imm_draw_circle_fill_2d(shdr_pos, x, y, radius, 40);
@@ -1790,7 +1788,7 @@ void ED_gpencil_brush_draw_eraser(Brush *brush, int x, int y)
immUnbindProgram();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
GPU_line_smooth(false);
}
@@ -1944,7 +1942,7 @@ static void gpencil_brush_cursor_draw(bContext *C, int x, int y, void *customdat
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
GPU_line_smooth(true);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
/* Inner Ring: Color from UI panel */
immUniformColor4f(color[0], color[1], color[2], 0.8f);
@@ -1963,13 +1961,13 @@ static void gpencil_brush_cursor_draw(bContext *C, int x, int y, void *customdat
immUniformColor4f(darkcolor[0], darkcolor[1], darkcolor[2], 0.8f);
imm_draw_circle_wire_2d(pos, x, y, radius + 1, 40);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
GPU_line_smooth(false);
/* Draw line for lazy mouse */
if ((last_mouse_position) && (brush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE_TEMP)) {
GPU_line_smooth(true);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
copy_v3_v3(color, brush->add_col);
immUniformColor4f(color[0], color[1], color[2], 0.8f);
@@ -1981,7 +1979,7 @@ static void gpencil_brush_cursor_draw(bContext *C, int x, int y, void *customdat
last_mouse_position[1] + region->winrct.ymin);
immEnd();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
GPU_line_smooth(false);
}
diff --git a/source/blender/editors/include/ED_info.h b/source/blender/editors/include/ED_info.h
index df6b6a20ddc..e3ce494e09a 100644
--- a/source/blender/editors/include/ED_info.h
+++ b/source/blender/editors/include/ED_info.h
@@ -31,8 +31,13 @@ struct Main;
/* info_stats.c */
void ED_info_stats_clear(struct ViewLayer *view_layer);
const char *ED_info_statusbar_string(struct Main *bmain,
- struct bScreen *screen,
- struct bContext *C);
+ struct Scene *scene,
+ struct ViewLayer *view_layer);
+
+const char *ED_info_statistics_string(struct Main *bmain,
+ struct Scene *scene,
+ struct ViewLayer *view_layer);
+
void ED_info_draw_stats(
struct Main *bmain, Scene *scene, ViewLayer *view_layer, int x, int *y, int height);
diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h
index f2cad1acc84..660e4de09d3 100644
--- a/source/blender/editors/include/ED_mesh.h
+++ b/source/blender/editors/include/ED_mesh.h
@@ -59,6 +59,7 @@ void EDBM_verts_mirror_cache_begin_ex(struct BMEditMesh *em,
const int axis,
const bool use_self,
const bool use_select,
+ const bool respecthide,
const bool use_topology,
float maxdist,
int *r_index);
@@ -66,6 +67,7 @@ void EDBM_verts_mirror_cache_begin(struct BMEditMesh *em,
const int axis,
const bool use_self,
const bool use_select,
+ const bool respecthide,
const bool use_toplogy);
void EDBM_verts_mirror_apply(struct BMEditMesh *em, const int sel_from, const int sel_to);
struct BMVert *EDBM_verts_mirror_get(struct BMEditMesh *em, struct BMVert *v);
diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h
index 4c7dd4fe66c..f1ddc10f1a8 100644
--- a/source/blender/editors/include/ED_object.h
+++ b/source/blender/editors/include/ED_object.h
@@ -62,6 +62,11 @@ struct Object *ED_object_context(const struct bContext *C);
struct Object *ED_object_active_context(const struct bContext *C);
void ED_collection_hide_menu_draw(const struct bContext *C, struct uiLayout *layout);
+Object **ED_object_array_in_mode_or_selected(struct bContext *C,
+ bool (*filter_fn)(struct Object *ob, void *user_data),
+ void *filter_user_data,
+ uint *r_objects_len);
+
/* object_utils.c */
bool ED_object_calc_active_center_for_editmode(struct Object *obedit,
const bool select_only,
diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h
index 29fe766b2d0..d9c7128c2ee 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);
@@ -187,11 +181,7 @@ void ED_area_newspace(struct bContext *C, ScrArea *area, int type, const bool sk
void ED_area_prevspace(struct bContext *C, ScrArea *area);
void ED_area_swapspace(struct bContext *C, ScrArea *sa1, ScrArea *sa2);
int ED_area_headersize(void);
-int ED_area_header_alignment_or_fallback(const ScrArea *area, int fallback);
-int ED_area_header_alignment(const ScrArea *area);
int ED_area_footersize(void);
-int ED_area_footer_alignment_or_fallback(const ScrArea *area, int fallback);
-int ED_area_footer_alignment(const ScrArea *area);
int ED_area_global_size_y(const ScrArea *area);
int ED_area_global_min_size_y(const ScrArea *area);
int ED_area_global_max_size_y(const ScrArea *area);
@@ -354,6 +344,7 @@ bool ED_operator_console_active(struct bContext *C);
bool ED_operator_object_active(struct bContext *C);
bool ED_operator_object_active_editable_ex(struct bContext *C, const Object *ob);
bool ED_operator_object_active_editable(struct bContext *C);
+bool ED_operator_object_active_local_editable_ex(struct bContext *C, const Object *ob);
bool ED_operator_object_active_local_editable(struct bContext *C);
bool ED_operator_object_active_editable_mesh(struct bContext *C);
bool ED_operator_object_active_editable_font(struct bContext *C);
diff --git a/source/blender/editors/include/ED_sculpt.h b/source/blender/editors/include/ED_sculpt.h
index c3abde479f1..bfc46534b99 100644
--- a/source/blender/editors/include/ED_sculpt.h
+++ b/source/blender/editors/include/ED_sculpt.h
@@ -53,6 +53,10 @@ void ED_sculpt_undosys_type(struct UndoType *ut);
void ED_sculpt_undo_geometry_begin(struct Object *ob, const char *name);
void ED_sculpt_undo_geometry_end(struct Object *ob);
+/* Face sets. */
+int ED_sculpt_face_sets_find_next_available_id(struct Mesh *mesh);
+void ED_sculpt_face_sets_initialize_none_to_id(struct Mesh *mesh, const int new_id);
+
/* Undo for changes happening on a base mesh for multires sculpting.
* if there is no multires sculpt active regular undo is used. */
void ED_sculpt_undo_push_multires_mesh_begin(struct bContext *C, const char *str);
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 ddc235fa5d2..a7b87d38472 100644
--- a/source/blender/editors/include/UI_resources.h
+++ b/source/blender/editors/include/UI_resources.h
@@ -263,7 +263,6 @@ typedef enum ThemeColorID {
TH_PAINT_CURVE_PIVOT,
TH_UV_SHADOW,
- TH_UV_OTHERS,
TH_FREESTYLE_EDGE_MARK,
TH_FREESTYLE_FACE_MARK,
@@ -432,12 +431,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/include/UI_view2d.h b/source/blender/editors/include/UI_view2d.h
index 6e0f4434b7b..d14731b81b7 100644
--- a/source/blender/editors/include/UI_view2d.h
+++ b/source/blender/editors/include/UI_view2d.h
@@ -120,6 +120,12 @@ void UI_view2d_curRect_validate(struct View2D *v2d);
void UI_view2d_curRect_reset(struct View2D *v2d);
void UI_view2d_sync(struct bScreen *screen, struct ScrArea *area, struct View2D *v2dcur, int flag);
+/* Perform all required updates after `v2d->cur` as been modified.
+ * This includes like validation view validation (#UI_view2d_curRect_validate).
+ *
+ * Current intent is to use it from user code, such as view navigation and zoom operations. */
+void UI_view2d_curRect_changed(const struct bContext *C, struct View2D *v2d);
+
void UI_view2d_totRect_set(struct View2D *v2d, int width, int height);
void UI_view2d_totRect_set_resize(struct View2D *v2d, int width, int height, bool resize);
diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c
index 22fbffa9030..9cede126890 100644
--- a/source/blender/editors/interface/interface.c
+++ b/source/blender/editors/interface/interface.c
@@ -44,6 +44,8 @@
#include "BLI_utildefines.h"
+#include "BLO_readfile.h"
+
#include "BKE_animsys.h"
#include "BKE_context.h"
#include "BKE_idprop.h"
@@ -69,7 +71,9 @@
#include "RNA_access.h"
-#include "BPY_extern.h"
+#ifdef WITH_PYTHON
+# include "BPY_extern_run.h"
+#endif
#include "ED_numinput.h"
#include "ED_screen.h"
@@ -300,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;
}
@@ -325,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]);
@@ -354,9 +358,7 @@ void ui_region_winrct_get_no_margin(const struct ARegion *region, struct rcti *r
void UI_block_translate(uiBlock *block, int x, int y)
{
- uiBut *but;
-
- for (but = block->buttons.first; but; but = but->next) {
+ LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
BLI_rctf_translate(&but->rect, x, y);
}
@@ -366,12 +368,13 @@ void UI_block_translate(uiBlock *block, int x, int y)
static void ui_block_bounds_calc_text(uiBlock *block, float offset)
{
const uiStyle *style = UI_style_get();
- uiBut *bt, *init_col_bt, *col_bt;
+ uiBut *col_bt;
int i = 0, j, x1addval = offset;
UI_fontstyle_set(&style->widget);
- for (init_col_bt = bt = block->buttons.first; bt; bt = bt->next) {
+ uiBut *init_col_bt = block->buttons.first;
+ LISTBASE_FOREACH (uiBut *, bt, &block->buttons) {
if (!ELEM(bt->type, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE, UI_BTYPE_SEPR_SPACER)) {
j = BLF_width(style->widget.uifont_id, bt->drawstr, sizeof(bt->drawstr));
@@ -407,7 +410,6 @@ static void ui_block_bounds_calc_text(uiBlock *block, float offset)
void ui_block_bounds_calc(uiBlock *block)
{
- uiBut *bt;
int xof;
if (BLI_listbase_is_empty(&block->buttons)) {
@@ -422,7 +424,7 @@ void ui_block_bounds_calc(uiBlock *block)
BLI_rctf_init_minmax(&block->rect);
- for (bt = block->buttons.first; bt; bt = bt->next) {
+ LISTBASE_FOREACH (uiBut *, bt, &block->buttons) {
BLI_rctf_union(&block->rect, &bt->rect);
}
@@ -435,7 +437,7 @@ void ui_block_bounds_calc(uiBlock *block)
block->rect.xmax = block->rect.xmin + max_ff(BLI_rctf_size_x(&block->rect), block->minbounds);
/* hardcoded exception... but that one is annoying with larger safety */
- bt = block->buttons.first;
+ uiBut *bt = block->buttons.first;
if (bt && STREQLEN(bt->str, "ERROR", 5)) {
xof = 10;
}
@@ -697,9 +699,10 @@ static bool ui_but_equals_old(const uiBut *but, const uiBut *oldbut)
uiBut *ui_but_find_old(uiBlock *block_old, const uiBut *but_new)
{
- uiBut *but_old;
- for (but_old = block_old->buttons.first; but_old; but_old = but_old->next) {
- if (ui_but_equals_old(but_new, but_old)) {
+ uiBut *but_old = NULL;
+ LISTBASE_FOREACH (uiBut *, but, &block_old->buttons) {
+ if (ui_but_equals_old(but_new, but)) {
+ but_old = but;
break;
}
}
@@ -707,9 +710,10 @@ uiBut *ui_but_find_old(uiBlock *block_old, const uiBut *but_new)
}
uiBut *ui_but_find_new(uiBlock *block_new, const uiBut *but_old)
{
- uiBut *but_new;
- for (but_new = block_new->buttons.first; but_new; but_new = but_new->next) {
- if (ui_but_equals_old(but_new, but_old)) {
+ uiBut *but_new = NULL;
+ LISTBASE_FOREACH (uiBut *, but, &block_new->buttons) {
+ if (ui_but_equals_old(but, but_old)) {
+ but_new = but;
break;
}
}
@@ -767,8 +771,6 @@ static bool ui_but_update_from_old_block(const bContext *C,
but->editstr = oldbut->editstr;
but->editval = oldbut->editval;
but->editvec = oldbut->editvec;
- but->editcoba = oldbut->editcoba;
- but->editcumap = oldbut->editcumap;
but->selsta = oldbut->selsta;
but->selend = oldbut->selend;
but->softmin = oldbut->softmin;
@@ -1128,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)));
@@ -1154,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,
@@ -1359,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));
@@ -1368,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,
@@ -1463,7 +1465,6 @@ static void ui_but_pie_direction_string(uiBut *but, char *buf, int size)
static void ui_menu_block_set_keymaps(const bContext *C, uiBlock *block)
{
- uiBut *but;
char buf[128];
BLI_assert(block->flag & (UI_BLOCK_LOOP | UI_BLOCK_SHOW_SHORTCUT_ALWAYS));
@@ -1477,7 +1478,7 @@ static void ui_menu_block_set_keymaps(const bContext *C, uiBlock *block)
}
if (block->flag & UI_BLOCK_RADIAL) {
- for (but = block->buttons.first; but; but = but->next) {
+ LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
if (but->pie_dir != UI_RADIAL_NONE) {
ui_but_pie_direction_string(but, buf, sizeof(buf));
ui_but_add_shortcut(but, buf, false);
@@ -1485,7 +1486,7 @@ static void ui_menu_block_set_keymaps(const bContext *C, uiBlock *block)
}
}
else {
- for (but = block->buttons.first; but; but = but->next) {
+ LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
if (block->flag & UI_BLOCK_SHOW_SHORTCUT_ALWAYS) {
/* Skip icon-only buttons (as used in the toolbar). */
if (but->drawstr[0] == '\0') {
@@ -1573,10 +1574,7 @@ static void ui_but_extra_operator_icon_free(uiButExtraOpIcon *extra_icon)
void ui_but_extra_operator_icons_free(uiBut *but)
{
-
- for (uiButExtraOpIcon *op_icon = but->extra_op_icons.first, *op_icon_next; op_icon;
- op_icon = op_icon_next) {
- op_icon_next = op_icon->next;
+ LISTBASE_FOREACH_MUTABLE (uiButExtraOpIcon *, op_icon, &but->extra_op_icons) {
ui_but_extra_operator_icon_free(op_icon);
}
BLI_listbase_clear(&but->extra_op_icons);
@@ -1658,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;
@@ -1708,7 +1706,6 @@ static void ui_but_predefined_extra_operator_icons_add(uiBut *but)
void UI_block_update_from_old(const bContext *C, uiBlock *block)
{
uiBut *but_old;
- uiBut *but;
if (!block->oldblock) {
return;
@@ -1720,7 +1717,7 @@ void UI_block_update_from_old(const bContext *C, uiBlock *block)
UI_butstore_update(block);
}
- for (but = block->buttons.first; but; but = but->next) {
+ LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
if (ui_but_update_from_old_block(C, block, &but, &but_old)) {
ui_but_update(but);
@@ -1745,7 +1742,6 @@ void UI_block_end_ex(const bContext *C, uiBlock *block, const int xy[2], int r_x
Scene *scene = CTX_data_scene(C);
ARegion *region = CTX_wm_region(C);
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
- uiBut *but;
BLI_assert(block->active);
@@ -1755,7 +1751,7 @@ void UI_block_end_ex(const bContext *C, uiBlock *block, const int xy[2], int r_x
* on matching buttons, we need this to make button event handling non
* blocking, while still allowing buttons to be remade each redraw as it
* is expected by blender code */
- for (but = block->buttons.first; but; but = but->next) {
+ LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
/* temp? Proper check for graying out */
if (but->optype) {
wmOperatorType *ot = but->optype;
@@ -1875,7 +1871,6 @@ void UI_block_draw(const bContext *C, uiBlock *block)
{
uiStyle style = *UI_style_get_dpi(); /* XXX pass on as arg */
ARegion *region;
- uiBut *but;
rcti rect;
/* get menu region or area region */
@@ -1889,8 +1884,7 @@ void UI_block_draw(const bContext *C, uiBlock *block)
}
/* we set this only once */
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
/* scale fonts */
ui_fontscale(&style.paneltitle.points, block->aspect);
@@ -1941,7 +1935,7 @@ void UI_block_draw(const bContext *C, uiBlock *block)
UI_widgetbase_draw_cache_begin();
/* widgets */
- for (but = block->buttons.first; but; but = but->next) {
+ LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
if (!(but->flag & (UI_HIDDEN | UI_SCROLLED))) {
ui_but_to_pixelrect(&rect, region, block, but);
@@ -2053,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;
}
@@ -2507,23 +2501,24 @@ int ui_but_string_get_max_length(uiBut *but)
uiBut *ui_but_drag_multi_edit_get(uiBut *but)
{
- uiBut *but_iter;
+ uiBut *return_but = NULL;
BLI_assert(but->flag & UI_BUT_DRAG_MULTI);
- for (but_iter = but->block->buttons.first; but_iter; but_iter = but_iter->next) {
+ LISTBASE_FOREACH (uiBut *, but_iter, &but->block->buttons) {
if (but_iter->editstr) {
+ return_but = but_iter;
break;
}
}
- return but_iter;
+ return return_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 :| */
@@ -2538,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);
@@ -2556,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) {
@@ -2589,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 */
@@ -2660,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;
@@ -2779,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)) {
@@ -2814,7 +2809,7 @@ char *ui_but_string_get_dynamic(uiBut *but, int *r_str_size)
}
/**
- * Report a generic error prefix when evaluating a string with #BPY_execute_string_as_number
+ * Report a generic error prefix when evaluating a string with #BPY_run_string_as_number
* as the Python error on it's own doesn't provide enough context.
*/
#define UI_NUMBER_EVAL_ERROR_PREFIX IFACE_("Error evaluating number, see Info editor for details")
@@ -2839,7 +2834,7 @@ static bool ui_number_from_string(bContext *C, const char *str, double *r_value)
{
bool ok;
#ifdef WITH_PYTHON
- ok = BPY_execute_string_as_number(C, NULL, str, UI_NUMBER_EVAL_ERROR_PREFIX, r_value);
+ ok = BPY_run_string_as_number(C, NULL, str, UI_NUMBER_EVAL_ERROR_PREFIX, r_value);
#else
UNUSED_VARS(C);
*r_value = atof(str);
@@ -2850,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;
@@ -2869,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;
}
@@ -3083,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;
@@ -3098,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;
@@ -3377,11 +3372,7 @@ void UI_blocklist_free(const bContext *C, ListBase *lb)
void UI_blocklist_free_inactive(const bContext *C, ListBase *lb)
{
- uiBlock *block, *nextblock;
-
- for (block = lb->first; block; block = nextblock) {
- nextblock = block->next;
-
+ LISTBASE_FOREACH_MUTABLE (uiBlock *, block, lb) {
if (!block->handle) {
if (!block->active) {
BLI_remlink(lb, block);
@@ -3490,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");
}
@@ -3497,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);
@@ -3520,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);
}
}
@@ -3610,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;
@@ -3797,6 +3791,18 @@ static void ui_but_alloc_info(const eButType type,
alloc_size = sizeof(uiButHSVCube);
alloc_str = "uiButHSVCube";
break;
+ case UI_BTYPE_COLORBAND:
+ alloc_size = sizeof(uiButColorBand);
+ alloc_str = "uiButColorBand";
+ break;
+ case UI_BTYPE_CURVE:
+ alloc_size = sizeof(uiButCurveMapping);
+ alloc_str = "uiButCurveMapping";
+ break;
+ case UI_BTYPE_CURVEPROFILE:
+ alloc_size = sizeof(uiButCurveProfile);
+ alloc_str = "uiButCurveProfile";
+ break;
default:
alloc_size = sizeof(uiBut);
alloc_str = "uiBut";
@@ -4794,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;
}
@@ -5179,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;
}
@@ -5565,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;
}
@@ -5943,10 +5949,9 @@ uiBut *uiDefIconTextButO(uiBlock *block,
int UI_blocklist_min_y_get(ListBase *lb)
{
- uiBlock *block;
int min = 0;
- for (block = lb->first; block; block = block->next) {
+ LISTBASE_FOREACH (uiBlock *, block, lb) {
if (block == lb->first || block->rect.ymin < min) {
min = block->rect.ymin;
}
@@ -5963,7 +5968,6 @@ void UI_block_direction_set(uiBlock *block, char direction)
/* this call escapes if there's alignment flags */
void UI_block_order_flip(uiBlock *block)
{
- uiBut *but;
float centy, miny = 10000, maxy = -10000;
if (U.uiflag & USER_MENUFIXEDORDER) {
@@ -5973,7 +5977,7 @@ void UI_block_order_flip(uiBlock *block)
return;
}
- for (but = block->buttons.first; but; but = but->next) {
+ LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
if (but->drawflag & UI_BUT_ALIGN) {
return;
}
@@ -5986,7 +5990,7 @@ void UI_block_order_flip(uiBlock *block)
}
/* mirror trick */
centy = (miny + maxy) / 2.0f;
- for (but = block->buttons.first; but; but = but->next) {
+ LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
but->rect.ymin = centy - (but->rect.ymin - centy);
but->rect.ymax = centy - (but->rect.ymax - centy);
SWAP(float, but->rect.ymin, but->rect.ymax);
@@ -6126,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)
@@ -6961,6 +6965,8 @@ void UI_init_userdef(Main *bmain)
/* fix saved themes */
init_userdef_do_versions(bmain);
uiStyleInit();
+
+ BLO_sanitize_experimental_features_userpref_blend(&U);
}
void UI_reinit_font(void)
diff --git a/source/blender/editors/interface/interface_align.c b/source/blender/editors/interface/interface_align.c
index 4981ef111e0..e92adc8a2ec 100644
--- a/source/blender/editors/interface/interface_align.c
+++ b/source/blender/editors/interface/interface_align.c
@@ -24,6 +24,7 @@
#include "DNA_screen_types.h"
#include "DNA_userdef_types.h"
+#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_rect.h"
@@ -385,7 +386,6 @@ static void ui_block_align_but_to_region(uiBut *but, const ARegion *region)
*/
void ui_block_align_calc(uiBlock *block, const ARegion *region)
{
- uiBut *but;
int num_buttons = 0;
const int sides_to_ui_but_align_flags[4] = SIDE_TO_UI_BUT_ALIGN;
@@ -398,7 +398,7 @@ void ui_block_align_calc(uiBlock *block, const ARegion *region)
/* First loop: we count number of buttons belonging to an align group,
* and clear their align flag.
* Tabs get some special treatment here, they get aligned to region border. */
- for (but = block->buttons.first; but; but = but->next) {
+ LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
/* special case: tabs need to be aligned to a region border, drawflag tells which one */
if (but->type == UI_BTYPE_TAB) {
ui_block_align_but_to_region(but, region);
@@ -431,7 +431,8 @@ void ui_block_align_calc(uiBlock *block, const ARegion *region)
memset(butal_array, 0, sizeof(*butal_array) * (size_t)num_buttons);
/* Second loop: we initialize our ButAlign data for each button. */
- for (but = block->buttons.first, butal = butal_array; but; but = but->next) {
+ butal = butal_array;
+ LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
if (but->alignnr != 0) {
butal->but = but;
butal->borders[LEFT] = &but->rect.xmin;
@@ -726,11 +727,10 @@ static void ui_block_align_calc_but(uiBut *first, short nr)
void ui_block_align_calc(uiBlock *block, const struct ARegion *UNUSED(region))
{
- uiBut *but;
short nr;
/* align buttons with same align nr */
- for (but = block->buttons.first; but;) {
+ LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
if (but->alignnr) {
nr = but->alignnr;
ui_block_align_calc_but(but, nr);
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 46876fca9a5..02a9c3742d7 100644
--- a/source/blender/editors/interface/interface_context_menu.c
+++ b/source/blender/editors/interface/interface_context_menu.c
@@ -47,7 +47,10 @@
#include "RNA_access.h"
-#include "BPY_extern.h"
+#ifdef WITH_PYTHON
+# include "BPY_extern.h"
+# include "BPY_extern_run.h"
+#endif
#include "WM_api.h"
#include "WM_types.h"
@@ -88,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));
@@ -407,7 +410,7 @@ static void ui_but_user_menu_add(bContext *C, uiBut *but, bUserMenu *um)
"'%s').label",
idname);
char *expr_result = NULL;
- if (BPY_execute_string_as_string(C, expr_imports, expr, __func__, &expr_result)) {
+ if (BPY_run_string_as_string(C, expr_imports, expr, __func__, &expr_result)) {
STRNCPY(drawstr, expr_result);
MEM_freeN(expr_result);
}
@@ -545,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 */
@@ -1041,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 05f6e61ff40..13dd4ce3001 100644
--- a/source/blender/editors/interface/interface_draw.c
+++ b/source/blender/editors/interface/interface_draw.c
@@ -161,13 +161,13 @@ void UI_draw_roundbox_aa(
GPUBatch *batch = ui_batch_roundbox_widget_get();
GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE);
- GPU_batch_uniform_4fv_array(batch, "parameters", 11, (float *)&widget_params);
+ GPU_batch_uniform_4fv_array(batch, "parameters", 11, (float(*)[4]) & widget_params);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
GPU_batch_draw(batch);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
void UI_draw_roundbox_4fv(
@@ -290,13 +290,13 @@ void UI_draw_roundbox_4fv(
GPUBatch *batch = ui_batch_roundbox_widget_get();
GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE);
- GPU_batch_uniform_4fv_array(batch, "parameters", 11, (float *)&widget_params);
+ GPU_batch_uniform_4fv_array(batch, "parameters", 11, (float(*)[4]) & widget_params);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
GPU_batch_draw(batch);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
#if 0
@@ -479,14 +479,14 @@ void UI_draw_roundbox_shade_x(bool filled,
.alpha_discard = 1.0f,
};
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
GPUBatch *batch = ui_batch_roundbox_widget_get();
GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE);
- GPU_batch_uniform_4fv_array(batch, "parameters", 11, (float *)&widget_params);
+ GPU_batch_uniform_4fv_array(batch, "parameters", 11, (float(*)[4]) & widget_params);
GPU_batch_draw(batch);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
#if 0 /* unused */
@@ -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,20 +742,19 @@ 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
/* prevent drawing outside widget area */
int scissor[4];
- GPU_scissor_get_i(scissor);
+ GPU_scissor_get(scissor);
GPU_scissor(rect->xmin, rect->ymin, w, h);
# endif
- GPU_blend(true);
/* Combine with premultiplied alpha. */
- GPU_blend_set_func_separate(GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA_PREMULT);
if (w != ibuf->x || h != ibuf->y) {
/* We scale the bitmap, rather than have OGL do a worse job. */
@@ -780,10 +780,7 @@ void ui_draw_but_IMAGE(ARegion *UNUSED(region),
1.0f,
col);
- GPU_blend(false);
- /* Reset default. */
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_NONE);
# if 0
// restore scissortest
@@ -817,26 +814,22 @@ 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);
}
}
}
-static void draw_scope_end(const rctf *rect, GLint *scissor)
+static void draw_scope_end(const rctf *rect)
{
- /* Restore scissor test. */
- GPU_scissor(scissor[0], scissor[1], scissor[2], scissor[3]);
-
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
/* outline */
UI_draw_roundbox_corner_set(UI_CNR_ALL);
@@ -866,7 +859,7 @@ static void histogram_draw_one(float r,
}
GPU_line_smooth(true);
- GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE, GPU_ONE, GPU_ONE);
+ GPU_blend(GPU_BLEND_ADDITIVE);
immUniformColor4fv(color);
@@ -876,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();
@@ -887,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);
}
@@ -896,11 +889,10 @@ static void histogram_draw_one(float r,
/* curve outline */
immUniformColor4f(0.0f, 0.0f, 0.0f, 0.25f);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ 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();
@@ -917,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 = {
@@ -927,12 +919,10 @@ 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(true);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
float color[4];
UI_GetThemeColor4fv(TH_PREVIEW_BACK, color);
@@ -942,14 +932,14 @@ void ui_draw_but_HISTOGRAM(ARegion *UNUSED(region),
/* need scissor test, histogram can draw outside of boundary */
int scissor[4];
- GPU_scissor_get_i(scissor);
+ GPU_scissor_get(scissor);
GPU_scissor((rect.xmin - 1),
(rect.ymin - 1),
(rect.xmax + 1) - (rect.xmin - 1),
(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);
@@ -999,8 +989,11 @@ void ui_draw_but_HISTOGRAM(ARegion *UNUSED(region),
immUnbindProgram();
+ /* Restore scissor test. */
+ GPU_scissor(UNPACK4(scissor));
+
/* outline */
- draw_scope_end(&rect, scissor);
+ draw_scope_end(&rect);
}
#undef HISTOGRAM_TOT_GRID_LINES
@@ -1008,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);
@@ -1051,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);
@@ -1071,9 +1064,7 @@ void ui_draw_but_WAVEFORM(ARegion *UNUSED(region),
/* Flush text cache before changing scissors. */
BLF_batch_draw_flush();
- GPU_blend(true);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
float color[4];
UI_GetThemeColor4fv(TH_PREVIEW_BACK, color);
@@ -1082,7 +1073,7 @@ void ui_draw_but_WAVEFORM(ARegion *UNUSED(region),
true, rect.xmin - 1, rect.ymin - 1, rect.xmax + 1, rect.ymax + 1, 3.0f, color);
/* need scissor test, waveform can draw outside of boundary */
- GPU_scissor_get_i(scissor);
+ GPU_scissor_get(scissor);
GPU_scissor((rect.xmin - 1),
(rect.ymin - 1),
(rect.xmax + 1) - (rect.xmin - 1),
@@ -1100,12 +1091,10 @@ void ui_draw_but_WAVEFORM(ARegion *UNUSED(region),
/* Flush text cache before drawing things on top. */
BLF_batch_draw_flush();
- GPU_blend(true);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ 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);
@@ -1167,7 +1156,7 @@ void ui_draw_but_WAVEFORM(ARegion *UNUSED(region),
}
if (scopes->ok && scopes->waveform_1 != NULL) {
- GPU_blend_set_func(GPU_ONE, GPU_ONE);
+ GPU_blend(GPU_BLEND_ADDITIVE);
GPU_point_size(1.0);
/* LUMA (1 channel) */
@@ -1212,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);
@@ -1257,10 +1246,13 @@ void ui_draw_but_WAVEFORM(ARegion *UNUSED(region),
immUnbindProgram();
+ /* Restore scissor test. */
+ GPU_scissor(UNPACK4(scissor));
+
/* outline */
- draw_scope_end(&rect, scissor);
+ draw_scope_end(&rect);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
static float polar_to_x(float center, float diam, float ampli, float angle)
@@ -1393,17 +1385,15 @@ 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(true);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
float color[4];
UI_GetThemeColor4fv(TH_PREVIEW_BACK, color);
@@ -1413,14 +1403,14 @@ void ui_draw_but_VECTORSCOPE(ARegion *UNUSED(region),
/* need scissor test, hvectorscope can draw outside of boundary */
int scissor[4];
- GPU_scissor_get_i(scissor);
+ GPU_scissor_get(scissor);
GPU_scissor((rect.xmin - 1),
(rect.ymin - 1),
(rect.xmax + 1) - (rect.xmin - 1),
(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);
@@ -1467,7 +1457,7 @@ void ui_draw_but_VECTORSCOPE(ARegion *UNUSED(region),
/* pixel point cloud */
const float col[3] = {alpha, alpha, alpha};
- GPU_blend_set_func(GPU_ONE, GPU_ONE);
+ GPU_blend(GPU_BLEND_ADDITIVE);
GPU_point_size(1.0);
GPU_matrix_push();
@@ -1481,10 +1471,12 @@ void ui_draw_but_VECTORSCOPE(ARegion *UNUSED(region),
immUnbindProgram();
+ /* Restore scissor test. */
+ GPU_scissor(UNPACK4(scissor));
/* outline */
- draw_scope_end(&rect, scissor);
+ draw_scope_end(&rect);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
static void ui_draw_colorband_handle_tri_hlight(
@@ -1547,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);
@@ -1595,7 +1587,7 @@ static void ui_draw_colorband_handle(uint shdr_pos,
shdr_pos, x - half_width, y1 - 1, x + half_width, y1 + height, false);
/* draw all triangles blended */
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
ui_draw_colorband_handle_tri(shdr_pos, x, y1 + height, half_width, half_width, true);
@@ -1619,7 +1611,7 @@ static void ui_draw_colorband_handle(uint shdr_pos,
immUniformColor3ub(0, 0, 0);
ui_draw_colorband_handle_tri_hlight(shdr_pos, x, y1 + height, half_width, half_width);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
immUniformColor3ub(128, 128, 128);
ui_draw_colorband_handle_box(
@@ -1639,16 +1631,18 @@ void ui_draw_but_COLORBAND(uiBut *but, const uiWidgetColors *UNUSED(wcol), const
struct ColorManagedDisplay *display = ui_block_cm_display_get(but->block);
uint pos_id, col_id;
- ColorBand *coba = (ColorBand *)(but->editcoba ? but->editcoba : but->poin);
+ uiButColorBand *but_coba = (uiButColorBand *)but;
+ ColorBand *coba = (but_coba->edit_coba == NULL) ? (ColorBand *)but->poin : but_coba->edit_coba;
+
if (coba == NULL) {
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) {
@@ -1675,7 +1669,7 @@ void ui_draw_but_COLORBAND(uiBut *but, const uiWidgetColors *UNUSED(wcol), const
immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR);
/* layer: color ramp */
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
CBData *cbd = coba->data;
@@ -1687,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);
@@ -1707,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);
@@ -1723,7 +1717,7 @@ void ui_draw_but_COLORBAND(uiBut *but, const uiWidgetColors *UNUSED(wcol), const
immUnbindProgram();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
/* New format */
format = immVertexFormat();
@@ -1735,7 +1729,7 @@ void ui_draw_but_COLORBAND(uiBut *but, const uiWidgetColors *UNUSED(wcol), const
imm_draw_box_wire_2d(pos_id, x1, y1, x1 + sizex, rect->ymax);
/* layer: box outline */
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
immUniformColor4f(0.0f, 0.0f, 0.0f, 0.5f);
immBegin(GPU_PRIM_LINES, 2);
@@ -1750,12 +1744,12 @@ void ui_draw_but_COLORBAND(uiBut *but, const uiWidgetColors *UNUSED(wcol), const
immVertex2f(pos_id, x1 + sizex, y1 - 1);
immEnd();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
/* 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);
}
}
@@ -1763,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);
}
@@ -1790,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);
@@ -1811,14 +1805,14 @@ 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);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
GPU_line_smooth(true);
imm_draw_circle_wire_2d(pos, 0.0f, 0.0f, 1.0f, 32);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
GPU_line_smooth(false);
if (use_project_matrix) {
@@ -1831,37 +1825,35 @@ void ui_draw_but_UNITVEC(uiBut *but, const uiWidgetColors *wcol, const rcti *rec
immUnbindProgram();
}
-static void ui_draw_but_curve_grid(
- uint pos, const rcti *rect, float zoomx, float zoomy, float offsx, float offsy, float step)
+static void ui_draw_but_curve_grid(const uint pos,
+ const rcti *rect,
+ const float zoom_x,
+ const float zoom_y,
+ const float offset_x,
+ const float offset_y,
+ const float step)
{
- float dx = step * zoomx;
- float fx = rect->xmin + zoomx * (-offsx);
- if (fx > rect->xmin) {
- fx -= dx * (floorf(fx - rect->xmin));
- }
+ const float start_x = (ceilf(offset_x / step) * step - offset_x) * zoom_x + rect->xmin;
+ const float start_y = (ceilf(offset_y / step) * step - offset_y) * zoom_y + rect->ymin;
- float dy = step * zoomy;
- float fy = rect->ymin + zoomy * (-offsy);
- if (fy > rect->ymin) {
- fy -= dy * (floorf(fy - rect->ymin));
- }
+ const int line_count_x = ceilf((rect->xmax - start_x) / (step * zoom_x));
+ const int line_count_y = ceilf((rect->ymax - start_y) / (step * zoom_y));
- float line_count = (floorf((rect->xmax - fx) / dx) + 1.0f + floorf((rect->ymax - fy) / dy) +
- 1.0f);
+ if (line_count_x + line_count_y == 0) {
+ return;
+ }
- immBegin(GPU_PRIM_LINES, (int)line_count * 2);
- while (fx <= rect->xmax) {
- immVertex2f(pos, fx, rect->ymin);
- immVertex2f(pos, fx, rect->ymax);
- fx += dx;
+ immBegin(GPU_PRIM_LINES, (line_count_x + line_count_y) * 2);
+ for (int i = 0; i < line_count_x; i++) {
+ const float x = start_x + i * step * zoom_x;
+ immVertex2f(pos, x, rect->ymin);
+ immVertex2f(pos, x, rect->ymax);
}
- while (fy <= rect->ymax) {
- immVertex2f(pos, rect->xmin, fy);
- immVertex2f(pos, rect->xmax, fy);
- fy += dy;
+ for (int i = 0; i < line_count_y; i++) {
+ const float y = start_y + i * step * zoom_y;
+ immVertex2f(pos, rect->xmin, y);
+ immVertex2f(pos, rect->xmax, y);
}
- /* Note: Assertion fails with here when the view is moved farther below the center.
- * Missing two points from the number given with immBegin. */
immEnd();
}
@@ -1888,17 +1880,12 @@ static void gl_shaded_color(const uchar *color, int shade)
void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol, const rcti *rect)
{
- CurveMapping *cumap;
-
- if (but->editcumap) {
- cumap = but->editcumap;
- }
- else {
- cumap = (CurveMapping *)but->poin;
- }
+ uiButCurveMapping *but_cumap = (uiButCurveMapping *)but;
+ 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) {
@@ -1906,10 +1893,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) {
@@ -1920,14 +1907,14 @@ void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol,
/* need scissor test, curve can draw outside of boundary */
int scissor[4];
- GPU_scissor_get_i(scissor);
+ GPU_scissor_get(scissor);
rcti scissor_new = {
.xmin = rect->xmin,
.ymin = rect->ymin,
.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,
@@ -1960,13 +1947,11 @@ void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol,
if (but->a1 == UI_GRAD_H) {
/* grid, hsv uses different grid */
- GPU_blend(true);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
ARRAY_SET_ITEMS(color_backdrop, 0, 0, 0, 48.0 / 255.0);
immUniformColor4fv(color_backdrop);
ui_draw_but_curve_grid(pos, rect, zoomx, zoomy, offsx, offsy, 0.1666666f);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
else {
if (cumap->flag & CUMA_DO_CLIP) {
@@ -2028,7 +2013,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);
@@ -2079,24 +2064,22 @@ void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol,
}
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
/* Curve filled. */
immUniformColor3ubvAlpha(wcol->item, 128);
- GPU_polygon_smooth(true);
immBegin(GPU_PRIM_TRI_STRIP, (CM_TABLE * 2 + 2) + 4);
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);
}
immVertex2f(pos, line_range.xmax, rect->ymin);
immVertex2f(pos, line_range.xmax, line_range.ymax);
immEnd();
- GPU_polygon_smooth(false);
/* Curve line. */
GPU_line_width(1.0f);
@@ -2105,8 +2088,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);
@@ -2114,13 +2097,13 @@ void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol,
/* Reset state for fill & line. */
GPU_line_smooth(false);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
immUnbindProgram();
/* 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. */
@@ -2139,8 +2122,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);
}
@@ -2181,19 +2164,16 @@ void ui_draw_but_CURVEPROFILE(ARegion *region,
{
uint i;
float fx, fy;
- CurveProfile *profile;
- if (but->editprofile) {
- profile = but->editprofile;
- }
- else {
- profile = (CurveProfile *)but->poin;
- }
+
+ uiButCurveProfile *but_profile = (uiButCurveProfile *)but;
+ CurveProfile *profile = (but_profile->edit_profile == NULL) ? (CurveProfile *)but->poin :
+ 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) {
@@ -2202,14 +2182,14 @@ void ui_draw_but_CURVEPROFILE(ARegion *region,
/* Test needed because path can draw outside of boundary. */
int scissor[4];
- GPU_scissor_get_i(scissor);
+ GPU_scissor_get(scissor);
rcti scissor_new = {
.xmin = rect->xmin,
.ymin = rect->ymin,
.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,
@@ -2254,10 +2234,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");
@@ -2301,7 +2281,7 @@ void ui_draw_but_CURVEPROFILE(ARegion *region,
/* Draw the triangles for the profile fill. */
immUniformColor3ubvAlpha((const uchar *)wcol->item, 128);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
GPU_polygon_smooth(false);
immBegin(GPU_PRIM_TRIS, 3 * tot_triangles);
for (i = 0; i < tot_triangles; i++) {
@@ -2368,7 +2348,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. */
@@ -2389,7 +2369,7 @@ void ui_draw_but_CURVEPROFILE(ARegion *region,
/* Draw the control points. */
GPU_line_smooth(false);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
GPU_point_size(max_ff(3.0f, min_ff(UI_DPI_FAC / but->block->aspect * 5.0f, 5.0f)));
immBegin(GPU_PRIM_POINTS, tot_points);
for (i = 0; i < tot_points; i++) {
@@ -2403,7 +2383,7 @@ void ui_draw_but_CURVEPROFILE(ARegion *region,
/* Draw the handle points. */
if (selected_free_points > 0) {
GPU_line_smooth(false);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
GPU_point_size(max_ff(2.0f, min_ff(UI_DPI_FAC / but->block->aspect * 4.0f, 4.0f)));
immBegin(GPU_PRIM_POINTS, selected_free_points * 2);
for (i = 0; i < tot_points; i++) {
@@ -2467,16 +2447,14 @@ 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(true);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
/* need scissor test, preview image can draw outside of boundary */
int scissor[4];
- GPU_scissor_get_i(scissor);
+ GPU_scissor_get(scissor);
GPU_scissor((rect.xmin - 1),
(rect.ymin - 1),
(rect.xmax + 1) - (rect.xmin - 1),
@@ -2556,8 +2534,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);
@@ -2568,10 +2546,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);
@@ -2601,10 +2579,12 @@ void ui_draw_but_TRACKPREVIEW(ARegion *UNUSED(region),
true, rect.xmin - 1, rect.ymin, rect.xmax + 1, rect.ymax + 1, 3.0f, color);
}
+ /* Restore scissor test. */
+ GPU_scissor(UNPACK4(scissor));
/* outline */
- draw_scope_end(&rect, scissor);
+ draw_scope_end(&rect);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
/* ****************************************************** */
@@ -2685,10 +2665,10 @@ static void ui_shadowbox(uint pos,
void UI_draw_box_shadow(uchar alpha, float minx, float miny, float maxx, float maxy)
{
- GPU_blend(true);
+ 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);
@@ -2705,7 +2685,7 @@ void UI_draw_box_shadow(uchar alpha, float minx, float miny, float maxx, float m
immUnbindProgram();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
void ui_draw_dropshadow(
@@ -2731,7 +2711,7 @@ void ui_draw_dropshadow(
a = i * aspect;
}
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
const float dalpha = alpha * 2.0f / 255.0f;
float calpha = dalpha;
float visibility = 1.0f;
@@ -2768,7 +2748,7 @@ void ui_draw_dropshadow(
GPUBatch *batch = ui_batch_roundbox_shadow_get();
GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_SHADOW);
- GPU_batch_uniform_4fv_array(batch, "parameters", 4, (float *)&widget_params);
+ GPU_batch_uniform_4fv_array(batch, "parameters", 4, (float(*)[4]) & widget_params);
GPU_batch_uniform_1f(batch, "alpha", 1.0f - visibility);
GPU_batch_draw(batch);
@@ -2782,5 +2762,5 @@ void ui_draw_dropshadow(
radius + 0.5f,
color);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
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_eyedropper_gpencil_color.c b/source/blender/editors/interface/interface_eyedropper_gpencil_color.c
index aa5b4d2c255..f7c41a7142b 100644
--- a/source/blender/editors/interface/interface_eyedropper_gpencil_color.c
+++ b/source/blender/editors/interface/interface_eyedropper_gpencil_color.c
@@ -38,6 +38,7 @@
#include "BKE_context.h"
#include "BKE_gpencil.h"
+#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_material.h"
#include "BKE_paint.h"
@@ -208,9 +209,13 @@ static void eyedropper_add_palette_color(bContext *C, const float col_conv[4])
/* Check for Palette in Draw and Vertex Paint Mode. */
if (paint->palette == NULL) {
- paint->palette = BKE_palette_add(bmain, "Grease Pencil");
+ Palette *palette = BKE_palette_add(bmain, "Grease Pencil");
+ id_us_min(&palette->id);
+
+ BKE_paint_palette_set(paint, palette);
+
if (vertexpaint->palette == NULL) {
- vertexpaint->palette = paint->palette;
+ BKE_paint_palette_set(vertexpaint, palette);
}
}
/* Check if the color exist already. */
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index 2d3a6181f09..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 *afterf, after;
- PointerRNA opptr;
- ListBase funcs;
-
/* copy to avoid recursive calls */
- funcs = UIAfterFuncs;
+ ListBase funcs = UIAfterFuncs;
BLI_listbase_clear(&UIAfterFuncs);
- for (afterf = funcs.first; afterf; afterf = after.next) {
- after = *afterf; /* copy to avoid memleak on exit() */
+ LISTBASE_FOREACH_MUTABLE (uiAfterFunc *, afterf, &funcs) {
+ 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;
@@ -1009,14 +1030,12 @@ static void ui_apply_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data)
static void ui_apply_but_ROW(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data)
{
- uiBut *bt;
-
ui_but_value_set(but, but->hardmax);
ui_apply_but_func(C, but);
/* states of other row buttons */
- for (bt = block->buttons.first; bt; bt = bt->next) {
+ LISTBASE_FOREACH (uiBut *, bt, &block->buttons) {
if (bt != but && bt->poin == but->poin && ELEM(bt->type, UI_BTYPE_ROW, UI_BTYPE_LISTROW)) {
ui_but_update_edited(bt);
}
@@ -1144,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
@@ -1163,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;
@@ -1180,9 +1195,7 @@ static uiButMultiState *ui_multibut_lookup(uiHandleButtonData *data, const uiBut
static void ui_multibut_restore(bContext *C, uiHandleButtonData *data, uiBlock *block)
{
- uiBut *but;
-
- for (but = block->buttons.first; but; but = but->next) {
+ LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
if (but->flag & UI_BUT_DRAG_MULTI) {
uiButMultiState *mbut_state = ui_multibut_lookup(data, but);
if (mbut_state) {
@@ -1235,7 +1248,6 @@ static bool ui_multibut_states_tag(uiBut *but_active,
uiHandleButtonData *data,
const wmEvent *event)
{
- uiBut *but;
float seg[2][2];
bool changed = false;
@@ -1253,7 +1265,7 @@ static bool ui_multibut_states_tag(uiBut *but_active,
data->multi_data.has_mbuts = false;
/* follow ui_but_find_mouse_over_ex logic */
- for (but = but_active->block->buttons.first; but; but = but->next) {
+ LISTBASE_FOREACH (uiBut *, but, &but_active->block->buttons) {
bool drag_prev = false;
bool drag_curr = false;
@@ -1318,12 +1330,11 @@ static void ui_multibut_states_apply(bContext *C, uiHandleButtonData *data, uiBl
const double value_delta = data->value - data->origvalue;
const double value_scale = data->multi_data.is_proportional ? (data->value / data->origvalue) :
0.0;
- uiBut *but;
BLI_assert(data->multi_data.init == BUTTON_MULTI_INIT_ENABLE);
BLI_assert(data->multi_data.skip == false);
- for (but = block->buttons.first; but; but = but->next) {
+ LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
if (but->flag & UI_BUT_DRAG_MULTI) {
/* mbut_states for delta */
uiButMultiState *mbut_state = ui_multibut_lookup(data, but);
@@ -1447,18 +1458,15 @@ static bool ui_drag_toggle_set_xy_xy(
/* popups such as layers won't re-evaluate on redraw */
const bool do_check = (region->regiontype == RGN_TYPE_TEMPORARY);
bool changed = false;
- uiBlock *block;
-
- for (block = region->uiblocks.first; block; block = block->next) {
- uiBut *but;
+ LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
float xy_a_block[2] = {UNPACK2(xy_src)};
float xy_b_block[2] = {UNPACK2(xy_dst)};
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]);
- for (but = block->buttons.first; but; but = but->next) {
+ LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
/* Note: ctrl is always true here because (at least for now)
* we always want to consider text control in this case, even when not embossed. */
if (ui_but_is_interactive(but, true)) {
@@ -1467,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) {
@@ -1496,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:
@@ -1535,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];
@@ -1608,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) {
@@ -1627,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);
@@ -1640,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) {
@@ -1745,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 {
@@ -1789,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' */
@@ -1801,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);
@@ -1816,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;
@@ -2025,12 +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)
{
- 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;
@@ -2083,21 +2085,42 @@ static void ui_apply_but(
}
/* ensures we are writing actual values */
- editstr = but->editstr;
- editval = but->editval;
- editvec = but->editvec;
- editcoba = but->editcoba;
- editcumap = but->editcumap;
- editprofile = but->editprofile;
+ 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;
+ }
+ else if (but_type == UI_BTYPE_CURVE) {
+ uiButCurveMapping *but_cumap = (uiButCurveMapping *)but;
+ editcumap = but_cumap->edit_cumap;
+ }
+ else if (but_type == UI_BTYPE_CURVEPROFILE) {
+ uiButCurveProfile *but_profile = (uiButCurveProfile *)but;
+ editprofile = but_profile->edit_profile;
+ }
but->editstr = NULL;
but->editval = NULL;
but->editvec = NULL;
- but->editcoba = NULL;
- but->editcumap = NULL;
- but->editprofile = NULL;
+ if (but_type == UI_BTYPE_COLORBAND) {
+ uiButColorBand *but_coba = (uiButColorBand *)but;
+ but_coba->edit_coba = NULL;
+ }
+ else if (but_type == UI_BTYPE_CURVE) {
+ uiButCurveMapping *but_cumap = (uiButCurveMapping *)but;
+ but_cumap->edit_cumap = NULL;
+ }
+ else if (but_type == UI_BTYPE_CURVEPROFILE) {
+ uiButCurveProfile *but_profile = (uiButCurveProfile *)but;
+ but_profile->edit_profile = NULL;
+ }
/* handle different types */
- switch (but->type) {
+ switch (but_type) {
case UI_BTYPE_BUT:
case UI_BTYPE_DECORATOR:
ui_apply_but_BUT(C, but, data);
@@ -2203,9 +2226,18 @@ static void ui_apply_but(
but->editstr = editstr;
but->editval = editval;
but->editvec = editvec;
- but->editcoba = editcoba;
- but->editcumap = editcumap;
- but->editprofile = editprofile;
+ if (but_type == UI_BTYPE_COLORBAND) {
+ uiButColorBand *but_coba = (uiButColorBand *)but;
+ but_coba->edit_coba = editcoba;
+ }
+ else if (but_type == UI_BTYPE_CURVE) {
+ uiButCurveMapping *but_cumap = (uiButCurveMapping *)but;
+ but_cumap->edit_cumap = editcumap;
+ }
+ else if (but_type == UI_BTYPE_CURVEPROFILE) {
+ uiButCurveProfile *but_profile = (uiButCurveProfile *)but;
+ but_profile->edit_profile = editprofile;
+ }
}
/** \} */
@@ -2217,10 +2249,9 @@ static void ui_apply_but(
/* only call if event type is EVT_DROP */
static void ui_but_drop(bContext *C, const wmEvent *event, uiBut *but, uiHandleButtonData *data)
{
- wmDrag *wmd;
ListBase *drags = event->customdata; /* drop event type has listbase customdata by default */
- for (wmd = drags->first; wmd; wmd = wmd->next) {
+ LISTBASE_FOREACH (wmDrag *, wmd, drags) {
if (wmd->type == WM_DRAG_ID) {
/* align these types with UI_but_active_drop_name */
if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) {
@@ -2249,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;
@@ -2323,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);
@@ -2335,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);
@@ -2349,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;
@@ -2379,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;
@@ -2441,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);
}
@@ -2537,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);
@@ -2579,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:
@@ -2670,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:
@@ -2750,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);
}
@@ -2772,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 */
@@ -2879,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;
@@ -2948,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. */
@@ -2996,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) {
@@ -3148,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);
}
@@ -3175,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)) {
@@ -3199,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);
@@ -3262,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;
@@ -3317,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;
@@ -3360,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 */
@@ -3405,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,
@@ -3417,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;
@@ -3426,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;
@@ -3439,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,
@@ -3451,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;
@@ -3460,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;
@@ -3480,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) {
@@ -3498,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
}
@@ -3529,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 */
@@ -3540,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)) {
@@ -3689,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) {
@@ -3753,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);
}
@@ -3813,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);
@@ -3848,14 +3862,17 @@ static void ui_do_but_textedit_select(
static void ui_numedit_begin(uiBut *but, uiHandleButtonData *data)
{
if (but->type == UI_BTYPE_CURVE) {
- but->editcumap = (CurveMapping *)but->poin;
+ uiButCurveMapping *but_cumap = (uiButCurveMapping *)but;
+ but_cumap->edit_cumap = (CurveMapping *)but->poin;
}
- if (but->type == UI_BTYPE_CURVEPROFILE) {
- but->editprofile = (CurveProfile *)but->poin;
+ else if (but->type == UI_BTYPE_CURVEPROFILE) {
+ uiButCurveProfile *but_profile = (uiButCurveProfile *)but;
+ but_profile->edit_profile = (CurveProfile *)but->poin;
}
else if (but->type == UI_BTYPE_COLORBAND) {
+ uiButColorBand *but_coba = (uiButColorBand *)but;
data->coba = (ColorBand *)but->poin;
- but->editcoba = data->coba;
+ but_coba->edit_coba = data->coba;
}
else if (ELEM(but->type,
UI_BTYPE_UNITVEC,
@@ -3941,10 +3958,18 @@ static void ui_numedit_end(uiBut *but, uiHandleButtonData *data)
{
but->editval = NULL;
but->editvec = NULL;
- but->editcoba = NULL;
- but->editcumap = NULL;
- but->editprofile = NULL;
-
+ if (but->type == UI_BTYPE_COLORBAND) {
+ uiButColorBand *but_coba = (uiButColorBand *)but;
+ but_coba->edit_coba = NULL;
+ }
+ else if (but->type == UI_BTYPE_CURVE) {
+ uiButCurveMapping *but_cumap = (uiButCurveMapping *)but;
+ but_cumap->edit_cumap = NULL;
+ }
+ else if (but->type == UI_BTYPE_CURVEPROFILE) {
+ uiButCurveProfile *but_profile = (uiButCurveProfile *)but;
+ but_profile->edit_profile = NULL;
+ }
data->dragstartx = 0;
data->draglastx = 0;
data->dragchange = false;
@@ -4138,7 +4163,7 @@ static uiButExtraOpIcon *ui_but_extra_operator_icon_mouse_over_get(uiBut *but,
}
/* Inverse order, from right to left. */
- for (uiButExtraOpIcon *op_icon = but->extra_op_icons.last; op_icon; op_icon = op_icon->prev) {
+ LISTBASE_FOREACH_BACKWARD (uiButExtraOpIcon *, op_icon, &but->extra_op_icons) {
if ((x > (xmax - icon_size)) && x < xmax) {
return op_icon;
}
@@ -4267,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);
@@ -4377,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;
@@ -4516,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 */
@@ -4586,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);
@@ -4609,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)*/
}
@@ -4805,7 +4829,7 @@ static bool ui_numedit_but_NUM(uiBut *but,
if (is_float == false) {
/* at minimum, moving cursor 2 pixels should change an int button. */
- CLAMP_MIN(non_linear_scale, 0.5f * U.pixelsize);
+ CLAMP_MIN(non_linear_scale, 0.5f * UI_DPI_FAC);
}
data->dragf += (((float)(mx - data->draglastx)) / deler) * non_linear_scale;
@@ -4853,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;
@@ -4903,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);
@@ -5124,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);
}
@@ -5215,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) {
@@ -5418,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) {
@@ -5440,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) {
@@ -5476,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));
@@ -5486,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) {
@@ -5551,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 */
@@ -5636,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 */
@@ -5646,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]);
@@ -5664,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;
@@ -5694,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);
@@ -5769,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);
}
@@ -5873,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) {
@@ -5990,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);
@@ -6063,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;
@@ -6106,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);
@@ -6169,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) {
@@ -6271,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
@@ -6296,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);
@@ -6375,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);
@@ -6447,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) {
@@ -6551,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) {
@@ -6562,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);
@@ -6578,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);
@@ -6663,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];
@@ -6691,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) {
@@ -6741,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) {
@@ -6777,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;
@@ -6805,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);
@@ -6817,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
@@ -6830,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);
@@ -6847,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;
}
@@ -6861,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;
@@ -6905,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;
@@ -6940,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];
@@ -6968,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;
@@ -6981,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);
@@ -7235,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;
@@ -7254,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) {
@@ -7311,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;
@@ -7330,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) {
@@ -7388,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;
@@ -7401,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);
}
@@ -7424,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) {
@@ -7472,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);
@@ -7491,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) {
@@ -7737,15 +7722,13 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *
static void ui_blocks_set_tooltips(ARegion *region, const bool enable)
{
- uiBlock *block;
-
if (!region) {
return;
}
/* we disabled buttons when when they were already shown, and
* re-enable them on mouse move */
- for (block = region->uiblocks.first; block; block = block->next) {
+ LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
block->tooltipdisabled = !enable;
}
}
@@ -7770,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);
@@ -7813,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) {
@@ -7847,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;
}
@@ -8019,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);
@@ -8124,7 +8101,6 @@ static void button_activate_exit(
{
wmWindow *win = data->window;
uiBlock *block = but->block;
- uiBut *bt;
if (but->type == UI_BTYPE_GRIP) {
WM_cursor_modal_restore(win);
@@ -8142,7 +8118,7 @@ static void button_activate_exit(
#ifdef USE_DRAG_MULTINUM
if (data->multi_data.has_mbuts) {
- for (bt = block->buttons.first; bt; bt = bt->next) {
+ LISTBASE_FOREACH (uiBut *, bt, &block->buttons) {
if (bt->flag & UI_BUT_DRAG_MULTI) {
bt->flag &= ~UI_BUT_DRAG_MULTI;
@@ -8198,12 +8174,12 @@ static void button_activate_exit(
}
/* disable tooltips until mousemove + last active flag */
- for (block = data->region->uiblocks.first; block; block = block->next) {
- for (bt = block->buttons.first; bt; bt = bt->next) {
+ LISTBASE_FOREACH (uiBlock *, block_iter, &data->region->uiblocks) {
+ LISTBASE_FOREACH (uiBut *, bt, &block_iter->buttons) {
bt->flag &= ~UI_BUT_LAST_ACTIVE;
}
- block->tooltipdisabled = 1;
+ block_iter->tooltipdisabled = 1;
}
ui_blocks_set_tooltips(data->region, false);
@@ -8250,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);
}
@@ -8268,12 +8242,11 @@ static uiBut *ui_context_button_active(ARegion *region, bool (*but_check_cb)(uiB
uiBut *but_found = NULL;
while (region) {
- uiBlock *block;
- uiBut *but, *activebut = NULL;
+ uiBut *activebut = NULL;
/* find active button */
- for (block = region->uiblocks.first; block; block = block->next) {
- for (but = block->buttons.first; but; but = but->next) {
+ LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
+ LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
if (but->active) {
activebut = but;
}
@@ -8396,7 +8369,6 @@ void UI_context_active_but_clear(bContext *C, wmWindow *win, ARegion *region)
wmOperator *UI_context_active_operator_get(const struct bContext *C)
{
ARegion *region_ctx = CTX_wm_region(C);
- uiBlock *block;
/* background mode */
if (region_ctx == NULL) {
@@ -8404,7 +8376,7 @@ wmOperator *UI_context_active_operator_get(const struct bContext *C)
}
/* scan active regions ui */
- for (block = region_ctx->uiblocks.first; block; block = block->next) {
+ LISTBASE_FOREACH (uiBlock *, block, &region_ctx->uiblocks) {
if (block->ui_operator) {
return block->ui_operator;
}
@@ -8413,13 +8385,12 @@ wmOperator *UI_context_active_operator_get(const struct bContext *C)
/* scan popups */
{
bScreen *screen = CTX_wm_screen(C);
- ARegion *region;
- for (region = screen->regionbase.first; region; region = region->next) {
+ LISTBASE_FOREACH (ARegion *, region, &screen->regionbase) {
if (region == region_ctx) {
continue;
}
- for (block = region->uiblocks.first; block; block = block->next) {
+ LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
if (block->ui_operator) {
return block->ui_operator;
}
@@ -8438,15 +8409,13 @@ 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);
- uiBlock *block;
- uiBut *but, *activebut;
while (region) {
/* find active button */
- activebut = NULL;
+ uiBut *activebut = NULL;
- for (block = region->uiblocks.first; block; block = block->next) {
- for (but = block->buttons.first; but; but = but->next) {
+ LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
+ LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
ui_but_anim_flag(but, &anim_eval_context);
ui_but_override_flag(CTX_data_main(C), but);
if (UI_but_is_decorator(but)) {
@@ -8489,11 +8458,8 @@ void UI_context_update_anim_flag(const bContext *C)
static uiBut *ui_but_find_open_event(ARegion *region, const wmEvent *event)
{
- uiBlock *block;
- uiBut *but;
-
- for (block = region->uiblocks.first; block; block = block->next) {
- for (but = block->buttons.first; but; but = but->next) {
+ LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
+ LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
if (but == event->customdata) {
return but;
}
@@ -8504,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);
@@ -8518,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);
@@ -8536,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;
@@ -8597,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);
}
@@ -8645,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) {
@@ -8679,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) {
@@ -8898,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') &&
@@ -8953,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. */
@@ -9003,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;
@@ -9086,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)) {
@@ -9192,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
@@ -9220,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;
}
@@ -9227,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) */
@@ -9252,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;
@@ -9455,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,
@@ -9483,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);
@@ -9513,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);
@@ -9523,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;
@@ -9545,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);
@@ -9742,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;
@@ -9987,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)) {
@@ -10074,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);
@@ -10153,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;
@@ -10214,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) {
@@ -10249,10 +10181,8 @@ static int ui_but_pie_menu_apply(bContext *C,
static uiBut *ui_block_pie_dir_activate(uiBlock *block, const wmEvent *event, RadialDirection dir)
{
- uiBut *but;
-
if ((block->flag & UI_BLOCK_NUMSELECT) && event->val == KM_PRESS) {
- for (but = block->buttons.first; but; but = but->next) {
+ LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
if (but->pie_dir == dir && !ELEM(but->type, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE)) {
return but;
}
@@ -10264,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);
@@ -10282,14 +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;
- uiBut *but;
- 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;
@@ -10300,13 +10220,13 @@ 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 */
- but = ui_region_find_active_but(region);
+ uiBut *but_active = ui_region_find_active_but(region);
if (menu->scrolltimer == NULL) {
menu->scrolltimer = WM_event_add_timer(
@@ -10314,17 +10234,16 @@ 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 && button_modal_state(but->active->state)) {
+ if (but_active && button_modal_state(but_active->active->state)) {
retval = ui_handle_menu_button(C, event, menu);
}
else {
@@ -10337,16 +10256,16 @@ 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;
block->pie_data.flags |= UI_PIE_ANIMATION_FINISHED;
}
- for (but = block->buttons.first; but; but = but->next) {
+ LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
if (but->pie_dir != UI_RADIAL_NONE) {
float vec[2];
float center[2];
@@ -10385,7 +10304,7 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle
}
if (len_sq < 1.0f) {
- but = ui_region_find_active_but(menu->region);
+ uiBut *but = ui_region_find_active_but(menu->region);
if (but) {
return ui_but_pie_menu_apply(C, menu, but, true);
@@ -10411,7 +10330,7 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle
block->pie_data.flags |= UI_PIE_CLICK_STYLE;
}
else {
- but = ui_region_find_active_but(menu->region);
+ uiBut *but = ui_region_find_active_but(menu->region);
if (but && (U.pie_menu_confirm > 0) &&
(dist >= U.dpi_fac * (U.pie_menu_threshold + U.pie_menu_confirm))) {
@@ -10429,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) {
@@ -10496,7 +10415,7 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle
case EVT_ZKEY: {
if ((event->val == KM_PRESS || event->val == KM_DBL_CLICK) &&
!IS_EVENT_MOD(event, shift, ctrl, oskey)) {
- for (but = block->buttons.first; but; but = but->next) {
+ LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
if (but->menu_key == event->type) {
ui_but_pie_button_activate(C, but, menu);
}
@@ -10529,7 +10448,7 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle
ATTR_FALLTHROUGH;
CASE_NUM_TO_DIR(9, UI_RADIAL_NE);
{
- but = ui_block_pie_dir_activate(block, event, num_dir);
+ uiBut *but = ui_block_pie_dir_activate(block, event, num_dir);
retval = ui_but_pie_button_activate(C, but, menu);
break;
}
@@ -10552,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;
@@ -10569,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);
}
@@ -10596,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;
}
@@ -10626,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;
@@ -10668,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);
@@ -10719,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;
}
@@ -10748,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.
@@ -10841,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) {
@@ -10869,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 */
@@ -10993,26 +10898,28 @@ bool UI_textbutton_activate_rna(const bContext *C,
const void *rna_poin_data,
const char *rna_prop_id)
{
- uiBlock *block;
- uiBut *but = NULL;
+ uiBlock *block_text = NULL;
+ uiBut *but_text = NULL;
- for (block = region->uiblocks.first; block; block = block->next) {
- for (but = block->buttons.first; but; but = but->next) {
+ LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
+ LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
if (but->type == UI_BTYPE_TEXT) {
if (but->rnaprop && but->rnapoin.data == rna_poin_data) {
if (STREQ(RNA_property_identifier(but->rnaprop), rna_prop_id)) {
+ block_text = block;
+ but_text = but;
break;
}
}
}
}
- if (but) {
+ if (but_text) {
break;
}
}
- if (but) {
- UI_but_active_only(C, region, block, but);
+ if (but_text) {
+ UI_but_active_only(C, region, block_text, but_text);
return true;
}
return false;
@@ -11021,23 +10928,25 @@ bool UI_textbutton_activate_rna(const bContext *C,
bool UI_textbutton_activate_but(const bContext *C, uiBut *actbut)
{
ARegion *region = CTX_wm_region(C);
- uiBlock *block;
- uiBut *but = NULL;
+ uiBlock *block_text = NULL;
+ uiBut *but_text = NULL;
- for (block = region->uiblocks.first; block; block = block->next) {
- for (but = block->buttons.first; but; but = but->next) {
+ LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
+ LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
if (but == actbut && but->type == UI_BTYPE_TEXT) {
+ block_text = block;
+ but_text = but;
break;
}
}
- if (but) {
+ if (but_text) {
break;
}
}
- if (but) {
- UI_but_active_only(C, region, block, but);
+ if (but_text) {
+ UI_but_active_only(C, region, block_text, but_text);
return true;
}
return false;
diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c
index a7b7bad2fe6..aae0d7c525f 100644
--- a/source/blender/editors/interface/interface_icons.c
+++ b/source/blender/editors/interface/interface_icons.c
@@ -28,6 +28,7 @@
#include "MEM_guardedalloc.h"
#include "GPU_batch.h"
+#include "GPU_batch_presets.h"
#include "GPU_immediate.h"
#include "GPU_matrix.h"
#include "GPU_state.h"
@@ -264,9 +265,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 +302,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 +324,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 +491,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 +733,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 +744,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 +759,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 +791,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 +821,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 +937,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 +1131,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 +1247,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 +1352,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 +1480,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,15 +1566,17 @@ 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);
GPU_shader_uniform_vector(
shader, data_loc, 4, ICON_DRAW_CACHE_SIZE * 3, (float *)texture_draw_calls->drawcall_cache);
- GPU_draw_primitive(GPU_PRIM_TRIS, 6 * texture_draw_calls->calls);
+ GPUBatch *quad = GPU_batch_preset_quad();
+ GPU_batch_set_shader(quad, shader);
+ GPU_batch_draw_instanced(quad, texture_draw_calls->calls);
GPU_texture_unbind(texture);
@@ -1595,7 +1598,7 @@ static void icon_draw_cache_flush_ex(bool only_full_caches)
/* We need to flush widget base first to ensure correct ordering. */
UI_widgetbase_draw_cache_flush();
- GPU_blend_set_func(GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA_PREMULT);
if (!only_full_caches || g_icon_draw_cache.normal.calls == ICON_DRAW_CACHE_SIZE) {
icon_draw_cache_texture_flush_ex(icongltex.tex[0], &g_icon_draw_cache.normal);
@@ -1605,8 +1608,7 @@ static void icon_draw_cache_flush_ex(bool only_full_caches)
icon_draw_cache_texture_flush_ex(icongltex.tex[1], &g_icon_draw_cache.border);
}
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
}
}
@@ -1620,9 +1622,9 @@ void UI_icon_draw_cache_end(void)
return;
}
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
icon_draw_cache_flush_ex(false);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
static void icon_draw_texture_cached(float x,
@@ -1690,7 +1692,7 @@ static void icon_draw_texture(float x,
/* We need to flush widget base first to ensure correct ordering. */
UI_widgetbase_draw_cache_flush();
- GPU_blend_set_func(GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA_PREMULT);
float x1, x2, y1, y2;
@@ -1704,10 +1706,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});
@@ -1722,12 +1724,13 @@ static void icon_draw_texture(float x,
GPU_texture_bind(texture, img_binding);
GPU_sampler_icon_bind(img_binding);
- GPU_draw_primitive(GPU_PRIM_TRI_STRIP, 4);
+ GPUBatch *quad = GPU_batch_preset_quad();
+ GPU_batch_set_shader(quad, shader);
+ GPU_batch_draw(quad);
GPU_texture_unbind(texture);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
}
/* Drawing size for preview images */
@@ -1815,11 +1818,9 @@ static void icon_draw_size(float x,
di->data.geom.inverted = invert;
}
- GPU_blend_set_func_separate(
- GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA_PREMULT);
icon_draw_rect(x, y, w, h, aspect, w, h, ibuf->rect, alpha, desaturate);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
}
else if (di->type == ICON_TYPE_EVENT) {
const short event_type = di->data.input.event_type;
@@ -1842,7 +1843,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);
@@ -1900,12 +1901,10 @@ static void icon_draw_size(float x,
}
/* Preview images use premultiplied alpha. */
- GPU_blend_set_func_separate(
- GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA_PREMULT);
icon_draw_rect(
x, y, w, h, aspect, pi->w[size], pi->h[size], pi->rect[size], alpha, desaturate);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
}
}
else if (di->type == ICON_TYPE_GPLAYER) {
@@ -2228,7 +2227,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;
}
@@ -2340,7 +2339,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 6718db39e61..87be3745f87 100644
--- a/source/blender/editors/interface/interface_intern.h
+++ b/source/blender/editors/interface/interface_intern.h
@@ -33,6 +33,8 @@
struct AnimationEvalContext;
struct ARegion;
+struct CurveMapping;
+struct CurveProfile;
struct ID;
struct ImBuf;
struct Scene;
@@ -266,9 +268,6 @@ struct uiBut {
char *editstr;
double *editval;
float *editvec;
- void *editcoba;
- void *editcumap;
- void *editprofile;
uiButPushedStateFunc pushed_state_func;
void *pushed_state_arg;
@@ -320,6 +319,7 @@ typedef struct uiButDecorator {
int rnaindex;
} uiButDecorator;
+/** Derived struct for #UI_BTYPE_PROGRESS_BAR. */
typedef struct uiButProgressbar {
uiBut but;
@@ -327,12 +327,34 @@ typedef struct uiButProgressbar {
float progress;
} uiButProgressbar;
+/** Derived struct for #UI_BTYPE_HSVCUBE. */
typedef struct uiButHSVCube {
uiBut but;
eButGradientType gradient_type;
} uiButHSVCube;
+/** Derived struct for #UI_BTYPE_CURVEPROFILE. */
+typedef struct uiButColorBand {
+ uiBut but;
+
+ struct ColorBand *edit_coba;
+} uiButColorBand;
+
+/** Derived struct for #UI_BTYPE_CURVEPROFILE. */
+typedef struct uiButCurveProfile {
+ uiBut but;
+
+ struct CurveProfile *edit_profile;
+} uiButCurveProfile;
+
+/** Derived struct for #UI_BTYPE_CURVE. */
+typedef struct uiButCurveMapping {
+ uiBut but;
+
+ struct CurveMapping *edit_cumap;
+} uiButCurveMapping;
+
/**
* Additional, superimposed icon for a button, invoking an operator.
*/
@@ -772,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 888cacb64eb..f7955c15dc4 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;
}
@@ -453,12 +453,12 @@ static uiLayout *ui_item_local_sublayout(uiLayout *test, uiLayout *layout, bool
static void ui_layer_but_cb(bContext *C, void *arg_but, void *arg_index)
{
wmWindow *win = CTX_wm_window(C);
- uiBut *but = arg_but, *cbut;
+ uiBut *but = arg_but;
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);
@@ -471,7 +471,7 @@ static void ui_layer_but_cb(bContext *C, void *arg_but, void *arg_index)
RNA_property_update(C, ptr, prop);
- for (cbut = but->block->buttons.first; cbut; cbut = cbut->next) {
+ LISTBASE_FOREACH (uiBut *, cbut, &but->block->buttons) {
ui_but_update(cbut);
}
}
@@ -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);
}
@@ -1072,8 +1072,7 @@ void UI_context_active_but_prop_get_filebrowser(const bContext *C,
bool *r_is_userdef)
{
ARegion *region = CTX_wm_menu(C) ? CTX_wm_menu(C) : CTX_wm_region(C);
- uiBlock *block;
- uiBut *but, *prevbut = NULL;
+ uiBut *prevbut = NULL;
memset(r_ptr, 0, sizeof(*r_ptr));
*r_prop = NULL;
@@ -1084,8 +1083,8 @@ void UI_context_active_but_prop_get_filebrowser(const bContext *C,
return;
}
- for (block = region->uiblocks.first; block; block = block->next) {
- for (but = block->buttons.first; but; but = but->next) {
+ LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
+ LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
if (but && but->rnapoin.data) {
if (RNA_property_type(but->rnaprop) == PROP_STRING) {
prevbut = but;
@@ -1180,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;
}
@@ -1224,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) {
@@ -1978,9 +1977,7 @@ void uiItemFullR(uiLayout *layout,
uiLayout *layout;
uiBut *but;
} ui_decorate = {
- .use_prop_decorate = (((layout->item.flag & UI_ITEM_PROP_DECORATE) != 0) &&
- (use_prop_sep && ptr->owner_id &&
- id_can_have_animdata(ptr->owner_id))),
+ .use_prop_decorate = (((layout->item.flag & UI_ITEM_PROP_DECORATE) != 0) && use_prop_sep),
};
#endif /* UI_PROP_DECORATE */
@@ -2044,7 +2041,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) {
@@ -2061,7 +2058,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 */
}
@@ -2100,7 +2097,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;
}
@@ -3194,7 +3191,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);
@@ -3273,7 +3270,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;
}
@@ -3515,14 +3512,13 @@ void uiItemTabsEnumR_prop(
/* single-row layout */
static void ui_litem_estimate_row(uiLayout *litem)
{
- uiItem *item;
int itemw, itemh;
bool min_size_flag = true;
litem->w = 0;
litem->h = 0;
- for (item = litem->items.first; item; item = item->next) {
+ LISTBASE_FOREACH (uiItem *, item, &litem->items) {
ui_item_size(item, &itemw, &itemh);
min_size_flag = min_size_flag && (item->flag & UI_ITEM_FIXED_SIZE);
@@ -3547,7 +3543,7 @@ static int ui_litem_min_width(int itemw)
static void ui_litem_layout_row(uiLayout *litem)
{
- uiItem *item, *last_free_item = NULL;
+ uiItem *last_free_item = NULL;
int x, y, w, tot, totw, neww, newtotw, itemw, minw, itemh, offset;
int fixedw, freew, fixedx, freex, flag = 0, lastw = 0;
float extra_pixel;
@@ -3558,7 +3554,7 @@ static void ui_litem_layout_row(uiLayout *litem)
totw = 0;
tot = 0;
- for (item = litem->items.first; item; item = item->next) {
+ LISTBASE_FOREACH (uiItem *, item, &litem->items) {
ui_item_size(item, &itemw, &itemh);
totw += itemw;
tot++;
@@ -3581,7 +3577,7 @@ static void ui_litem_layout_row(uiLayout *litem)
newtotw = totw;
extra_pixel = 0.0f;
- for (item = litem->items.first; item; item = item->next) {
+ LISTBASE_FOREACH (uiItem *, item, &litem->items) {
if (item->flag & UI_ITEM_AUTO_FIXED_SIZE) {
continue;
}
@@ -3633,7 +3629,7 @@ static void ui_litem_layout_row(uiLayout *litem)
extra_pixel = 0.0f;
x = litem->x;
- for (item = litem->items.first; item; item = item->next) {
+ LISTBASE_FOREACH (uiItem *, item, &litem->items) {
ui_item_size(item, &itemw, &itemh);
minw = ui_litem_min_width(itemw);
@@ -3682,7 +3678,7 @@ static void ui_litem_layout_row(uiLayout *litem)
if (extra_pixel > 0 && litem->alignment == UI_LAYOUT_ALIGN_EXPAND && last_free_item &&
last_item && last_item->flag & UI_ITEM_AUTO_FIXED_SIZE) {
ui_item_move(last_free_item, 0, extra_pixel);
- for (item = last_free_item->next; item; item = item->next) {
+ for (uiItem *item = last_free_item->next; item; item = item->next) {
ui_item_move(item, extra_pixel, extra_pixel);
}
}
@@ -3696,14 +3692,13 @@ static void ui_litem_layout_row(uiLayout *litem)
/* single-column layout */
static void ui_litem_estimate_column(uiLayout *litem, bool is_box)
{
- uiItem *item;
int itemw, itemh;
bool min_size_flag = true;
litem->w = 0;
litem->h = 0;
- for (item = litem->items.first; item; item = item->next) {
+ LISTBASE_FOREACH (uiItem *, item, &litem->items) {
ui_item_size(item, &itemw, &itemh);
min_size_flag = min_size_flag && (item->flag & UI_ITEM_FIXED_SIZE);
@@ -3723,13 +3718,12 @@ static void ui_litem_estimate_column(uiLayout *litem, bool is_box)
static void ui_litem_layout_column(uiLayout *litem, bool is_box)
{
- uiItem *item;
int itemh, x, y;
x = litem->x;
y = litem->y;
- for (item = litem->items.first; item; item = item->next) {
+ LISTBASE_FOREACH (uiItem *, item, &litem->items) {
ui_item_size(item, NULL, &itemh);
y -= itemh;
@@ -3789,7 +3783,6 @@ static bool ui_item_is_radial_drawable(uiButtonItem *bitem)
static void ui_litem_layout_radial(uiLayout *litem)
{
- uiItem *item;
int itemh, itemw, x, y;
int itemnum = 0;
int totitems = 0;
@@ -3799,7 +3792,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;
@@ -3807,7 +3800,7 @@ static void ui_litem_layout_radial(uiLayout *litem)
int minx = x, miny = y, maxx = x, maxy = y;
/* first count total items */
- for (item = litem->items.first; item; item = item->next) {
+ LISTBASE_FOREACH (uiItem *, item, &litem->items) {
totitems++;
}
@@ -3815,7 +3808,7 @@ static void ui_litem_layout_radial(uiLayout *litem)
litem->root->block->pie_data.flags |= UI_PIE_DEGREES_RANGE_LARGE;
}
- for (item = litem->items.first; item; item = item->next) {
+ LISTBASE_FOREACH (uiItem *, item, &litem->items) {
/* not all button types are drawn in a radial menu, do filtering here */
if (ui_item_is_radial_displayable(item)) {
RadialDirection dir;
@@ -3965,14 +3958,13 @@ static void ui_litem_estimate_column_flow(uiLayout *litem)
{
const uiStyle *style = litem->root->style;
uiLayoutItemFlow *flow = (uiLayoutItemFlow *)litem;
- uiItem *item;
int col, x, y, emh, emy, miny, itemw, itemh, maxw = 0;
int toth, totitem;
/* compute max needed width and total height */
toth = 0;
totitem = 0;
- for (item = litem->items.first; item; item = item->next) {
+ LISTBASE_FOREACH (uiItem *, item, &litem->items) {
ui_item_size(item, &itemw, &itemh);
maxw = MAX2(maxw, itemw);
toth += itemh;
@@ -4004,7 +3996,7 @@ static void ui_litem_estimate_column_flow(uiLayout *litem)
/* create column per column */
col = 0;
- for (item = litem->items.first; item; item = item->next) {
+ LISTBASE_FOREACH (uiItem *, item, &litem->items) {
ui_item_size(item, &itemw, &itemh);
y -= itemh + style->buttonspacey;
@@ -4030,14 +4022,13 @@ static void ui_litem_layout_column_flow(uiLayout *litem)
{
const uiStyle *style = litem->root->style;
uiLayoutItemFlow *flow = (uiLayoutItemFlow *)litem;
- uiItem *item;
int col, x, y, w, emh, emy, miny, itemw, itemh;
int toth, totitem;
/* compute max needed width and total height */
toth = 0;
totitem = 0;
- for (item = litem->items.first; item; item = item->next) {
+ LISTBASE_FOREACH (uiItem *, item, &litem->items) {
ui_item_size(item, &itemw, &itemh);
toth += itemh;
totitem++;
@@ -4055,7 +4046,7 @@ static void ui_litem_layout_column_flow(uiLayout *litem)
/* create column per column */
col = 0;
w = (litem->w - (flow->totcol - 1) * style->columnspace) / flow->totcol;
- for (item = litem->items.first; item; item = item->next) {
+ LISTBASE_FOREACH (uiItem *, item, &litem->items) {
ui_item_size(item, &itemw, &itemh);
itemw = (litem->alignment == UI_LAYOUT_ALIGN_EXPAND) ? w : min_ii(w, itemw);
@@ -4121,9 +4112,6 @@ static void ui_litem_grid_flow_compute(ListBase *items,
UILayoutGridFlowInput *parameters,
UILayoutGridFlowOutput *results)
{
- uiItem *item;
- int i;
-
float tot_w = 0.0f, tot_h = 0.0f;
float global_avg_w = 0.0f, global_totweight_w = 0.0f;
int global_max_h = 0;
@@ -4163,7 +4151,8 @@ static void ui_litem_grid_flow_compute(ListBase *items,
memset(max_h, 0, sizeof(*max_h) * parameters->tot_rows);
}
- for (i = 0, item = items->first; item; item = item->next, i++) {
+ int i = 0;
+ LISTBASE_FOREACH (uiItem *, item, items) {
int item_w, item_h;
ui_item_size(item, &item_w, &item_h);
@@ -4186,6 +4175,7 @@ static void ui_litem_grid_flow_compute(ListBase *items,
if (results->tot_items) {
(*results->tot_items)++;
}
+ i++;
}
/* Finalize computing of column average sizes */
@@ -4394,10 +4384,8 @@ static void ui_litem_estimate_grid_flow(uiLayout *litem)
static void ui_litem_layout_grid_flow(uiLayout *litem)
{
- int i;
const uiStyle *style = litem->root->style;
uiLayoutItemGridFlow *gflow = (uiLayoutItemGridFlow *)litem;
- uiItem *item;
if (gflow->tot_items == 0) {
litem->w = litem->h = 0;
@@ -4436,7 +4424,8 @@ static void ui_litem_layout_grid_flow(uiLayout *litem)
.heights_array = heights,
}));
- for (item = litem->items.first, i = 0; item; item = item->next, i++) {
+ int i;
+ LISTBASE_FOREACH_INDEX (uiItem *, item, &litem->items, i) {
const int col = gflow->row_major ? i % gflow->tot_columns : i / gflow->tot_rows;
const int row = gflow->row_major ? i / gflow->tot_columns : i % gflow->tot_rows;
int item_w, item_h;
@@ -4459,7 +4448,6 @@ static void ui_litem_layout_grid_flow(uiLayout *litem)
/* free layout */
static void ui_litem_estimate_absolute(uiLayout *litem)
{
- uiItem *item;
int itemx, itemy, itemw, itemh, minx, miny;
minx = 1e6;
@@ -4467,7 +4455,7 @@ static void ui_litem_estimate_absolute(uiLayout *litem)
litem->w = 0;
litem->h = 0;
- for (item = litem->items.first; item; item = item->next) {
+ LISTBASE_FOREACH (uiItem *, item, &litem->items) {
ui_item_offset(item, &itemx, &itemy);
ui_item_size(item, &itemw, &itemh);
@@ -4484,7 +4472,6 @@ static void ui_litem_estimate_absolute(uiLayout *litem)
static void ui_litem_layout_absolute(uiLayout *litem)
{
- uiItem *item;
float scalex = 1.0f, scaley = 1.0f;
int x, y, newx, newy, itemx, itemy, itemh, itemw, minx, miny, totw, toth;
@@ -4493,7 +4480,7 @@ static void ui_litem_layout_absolute(uiLayout *litem)
totw = 0;
toth = 0;
- for (item = litem->items.first; item; item = item->next) {
+ LISTBASE_FOREACH (uiItem *, item, &litem->items) {
ui_item_offset(item, &itemx, &itemy);
ui_item_size(item, &itemw, &itemh);
@@ -4517,7 +4504,7 @@ static void ui_litem_layout_absolute(uiLayout *litem)
x = litem->x;
y = litem->y - scaley * toth;
- for (item = litem->items.first; item; item = item->next) {
+ LISTBASE_FOREACH (uiItem *, item, &litem->items) {
ui_item_offset(item, &itemx, &itemy);
ui_item_size(item, &itemw, &itemh);
@@ -4552,7 +4539,6 @@ static void ui_litem_estimate_split(uiLayout *litem)
static void ui_litem_layout_split(uiLayout *litem)
{
uiLayoutItemSplit *split = (uiLayoutItemSplit *)litem;
- uiItem *item;
float percentage, extra_pixel = 0.0f;
const int tot = BLI_listbase_count(&litem->items);
int itemh, x, y, w, colw = 0;
@@ -4570,7 +4556,7 @@ static void ui_litem_layout_split(uiLayout *litem)
colw = w * percentage;
colw = MAX2(colw, 0);
- for (item = litem->items.first; item; item = item->next) {
+ LISTBASE_FOREACH (uiItem *, item, &litem->items) {
ui_item_size(item, NULL, &itemh);
ui_item_position(item, x, y - itemh, colw, itemh);
@@ -4595,13 +4581,12 @@ static void ui_litem_layout_split(uiLayout *litem)
/* overlap layout */
static void ui_litem_estimate_overlap(uiLayout *litem)
{
- uiItem *item;
int itemw, itemh;
litem->w = 0;
litem->h = 0;
- for (item = litem->items.first; item; item = item->next) {
+ LISTBASE_FOREACH (uiItem *, item, &litem->items) {
ui_item_size(item, &itemw, &itemh);
litem->w = MAX2(itemw, litem->w);
@@ -4611,13 +4596,12 @@ static void ui_litem_estimate_overlap(uiLayout *litem)
static void ui_litem_layout_overlap(uiLayout *litem)
{
- uiItem *item;
int itemw, itemh, x, y;
x = litem->x;
y = litem->y;
- for (item = litem->items.first; item; item = item->next) {
+ LISTBASE_FOREACH (uiItem *, item, &litem->items) {
ui_item_size(item, &itemw, &itemh);
ui_item_position(item, x, y - itemh, litem->w, itemh);
@@ -4775,7 +4759,6 @@ static uiLayoutItemBx *ui_layout_box(uiLayout *layout, int type)
uiLayout *uiLayoutRadial(uiLayout *layout)
{
uiLayout *litem;
- uiItem *item;
/* radial layouts are only valid for radial menus */
if (layout->root->type != UI_LAYOUT_PIEMENU) {
@@ -4783,7 +4766,7 @@ uiLayout *uiLayoutRadial(uiLayout *layout)
}
/* only one radial wheel per root layout is allowed, so check and return that, if it exists */
- for (item = layout->root->layout->items.first; item; item = item->next) {
+ LISTBASE_FOREACH (uiItem *, item, &layout->root->layout->items) {
litem = (uiLayout *)item;
if (litem->item.type == ITEM_LAYOUT_RADIAL) {
UI_block_layout_set_current(layout->root->block, litem);
@@ -4813,8 +4796,7 @@ uiLayout *uiLayoutBox(uiLayout *layout)
*/
void ui_layout_list_set_labels_active(uiLayout *layout)
{
- uiButtonItem *bitem;
- for (bitem = layout->items.first; bitem; bitem = bitem->item.next) {
+ LISTBASE_FOREACH (uiButtonItem *, bitem, &layout->items) {
if (bitem->item.type != ITEM_BUTTON) {
ui_layout_list_set_labels_active((uiLayout *)(&bitem->item));
}
@@ -5055,10 +5037,9 @@ int uiLayoutGetEmboss(uiLayout *layout)
static void ui_item_scale(uiLayout *litem, const float scale[2])
{
- uiItem *item;
int x, y, w, h;
- for (item = litem->items.last; item; item = item->prev) {
+ LISTBASE_FOREACH_BACKWARD (uiItem *, item, &litem->items) {
if (item->type != ITEM_BUTTON) {
uiLayout *subitem = (uiLayout *)item;
ui_item_scale(subitem, scale);
@@ -5083,12 +5064,10 @@ static void ui_item_scale(uiLayout *litem, const float scale[2])
static void ui_item_estimate(uiItem *item)
{
- uiItem *subitem;
-
if (item->type != ITEM_BUTTON) {
uiLayout *litem = (uiLayout *)item;
- for (subitem = litem->items.first; subitem; subitem = subitem->next) {
+ LISTBASE_FOREACH (uiItem *, subitem, &litem->items) {
ui_item_estimate(subitem);
}
@@ -5146,11 +5125,10 @@ static void ui_item_estimate(uiItem *item)
static void ui_item_align(uiLayout *litem, short nr)
{
- uiItem *item;
uiButtonItem *bitem;
uiLayoutItemBx *box;
- for (item = litem->items.last; item; item = item->prev) {
+ LISTBASE_FOREACH_BACKWARD (uiItem *, item, &litem->items) {
if (item->type == ITEM_BUTTON) {
bitem = (uiButtonItem *)item;
#ifndef USE_UIBUT_SPATIAL_ALIGN
@@ -5182,10 +5160,9 @@ static void ui_item_align(uiLayout *litem, short nr)
static void ui_item_flag(uiLayout *litem, int flag)
{
- uiItem *item;
uiButtonItem *bitem;
- for (item = litem->items.last; item; item = item->prev) {
+ LISTBASE_FOREACH_BACKWARD (uiItem *, item, &litem->items) {
if (item->type == ITEM_BUTTON) {
bitem = (uiButtonItem *)item;
bitem->but->flag |= flag;
@@ -5198,8 +5175,6 @@ static void ui_item_flag(uiLayout *litem, int flag)
static void ui_item_layout(uiItem *item)
{
- uiItem *subitem;
-
if (item->type != ITEM_BUTTON) {
uiLayout *litem = (uiLayout *)item;
@@ -5252,7 +5227,7 @@ static void ui_item_layout(uiItem *item)
break;
}
- for (subitem = litem->items.first; subitem; subitem = subitem->next) {
+ LISTBASE_FOREACH (uiItem *, subitem, &litem->items) {
if (item->flag & UI_ITEM_BOX_ITEM) {
subitem->flag |= UI_ITEM_BOX_ITEM;
}
@@ -5286,11 +5261,7 @@ static void ui_layout_end(uiBlock *block, uiLayout *layout, int *r_x, int *r_y)
static void ui_layout_free(uiLayout *layout)
{
- uiItem *item, *next;
-
- for (item = layout->items.first; item; item = next) {
- next = item->next;
-
+ LISTBASE_FOREACH_MUTABLE (uiItem *, item, &layout->items) {
if (item->type == ITEM_BUTTON) {
MEM_freeN(item);
}
@@ -5474,8 +5445,6 @@ void uiLayoutSetFunc(uiLayout *layout, uiMenuHandleFunc handlefunc, void *argv)
void UI_block_layout_resolve(uiBlock *block, int *r_x, int *r_y)
{
- uiLayoutRoot *root;
-
BLI_assert(block->active);
if (r_x) {
@@ -5487,7 +5456,7 @@ void UI_block_layout_resolve(uiBlock *block, int *r_x, int *r_y)
block->curlayout = NULL;
- for (root = block->layouts.first; root; root = root->next) {
+ LISTBASE_FOREACH (uiLayoutRoot *, root, &block->layouts) {
ui_layout_add_padding_button(root);
/* NULL in advance so we don't interfere when adding button */
diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c
index 5a49f3e70d0..d6919fb4de7 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);
@@ -715,8 +715,7 @@ static void ui_context_selected_bones_via_pose(bContext *C, ListBase *r_lb)
lb = CTX_data_collection_get(C, "selected_pose_bones");
if (!BLI_listbase_is_empty(&lb)) {
- CollectionPointerLink *link;
- for (link = lb.first; link; link = link->next) {
+ LISTBASE_FOREACH (CollectionPointerLink *, link, &lb) {
bPoseChannel *pchan = link->ptr.data;
RNA_pointer_create(link->ptr.owner_id, &RNA_Bone, pchan->bone, &link->ptr);
}
@@ -843,12 +842,10 @@ bool UI_context_copy_to_selected_list(bContext *C,
/* Now filter by type */
if (node) {
- CollectionPointerLink *link, *link_next;
lb = CTX_data_collection_get(C, "selected_nodes");
- for (link = lb.first; link; link = link_next) {
+ LISTBASE_FOREACH_MUTABLE (CollectionPointerLink *, link, &lb) {
bNode *node_data = link->ptr.data;
- link_next = link->next;
if (node_data->type != node->type) {
BLI_remlink(&lb, link);
@@ -876,9 +873,7 @@ bool UI_context_copy_to_selected_list(bContext *C,
/* de-duplicate obdata */
if (!BLI_listbase_is_empty(&lb)) {
- CollectionPointerLink *link, *link_next;
-
- for (link = lb.first; link; link = link->next) {
+ LISTBASE_FOREACH (CollectionPointerLink *, link, &lb) {
Object *ob = (Object *)link->ptr.owner_id;
if (ob->data) {
ID *id_data = ob->data;
@@ -886,10 +881,9 @@ bool UI_context_copy_to_selected_list(bContext *C,
}
}
- for (link = lb.first; link; link = link_next) {
+ LISTBASE_FOREACH_MUTABLE (CollectionPointerLink *, link, &lb) {
Object *ob = (Object *)link->ptr.owner_id;
ID *id_data = ob->data;
- link_next = link->next;
if ((id_data == NULL) || (id_data->tag & LIB_TAG_DOIT) == 0 || ID_IS_LINKED(id_data) ||
(GS(id_data->name) != id_code)) {
@@ -970,12 +964,11 @@ static bool copy_to_selected_button(bContext *C, bool all, bool poll)
if (ptr.data && prop) {
char *path = NULL;
bool use_path_from_id;
- CollectionPointerLink *link;
ListBase lb = {NULL};
if (UI_context_copy_to_selected_list(C, &ptr, prop, &lb, &use_path_from_id, &path) &&
!BLI_listbase_is_empty(&lb)) {
- for (link = lb.first; link; link = link->next) {
+ LISTBASE_FOREACH (CollectionPointerLink *, link, &lb) {
if (link->ptr.data != ptr.data) {
if (use_path_from_id) {
/* Path relative to ID. */
@@ -1181,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;
}
@@ -1299,13 +1292,14 @@ static int editsource_text_edit(bContext *C,
const int line)
{
struct Main *bmain = CTX_data_main(C);
- Text *text;
+ Text *text = NULL;
/* Developers may wish to copy-paste to an external editor. */
printf("%s:%d\n", filepath, line);
- for (text = bmain->texts.first; text; text = text->id.next) {
- if (text->filepath && BLI_path_cmp(text->filepath, filepath) == 0) {
+ LISTBASE_FOREACH (Text *, text_iter, &bmain->texts) {
+ if (text_iter->filepath && BLI_path_cmp(text_iter->filepath, filepath) == 0) {
+ text = text_iter;
break;
}
}
diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c
index d334007a097..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. ***********/
+/** \} */
-static Panel *UI_panel_add_instanced_ex(ScrArea *area,
- ARegion *region,
+/* -------------------------------------------------------------------- */
+/** \name Functions for Instanced Panels
+ * \{ */
+
+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,14 +931,14 @@ 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(true);
+ GPU_blend(GPU_BLEND_ALPHA);
immUniformColor4ubv(box_wcol->outline);
immRectf(pos, rect->xmin, headrect.ymin - U.pixelsize, rect->xmax, headrect.ymin);
@@ -1013,49 +951,43 @@ void ui_draw_aligned_panel(uiStyle *style,
rect->xmax,
headrect.ymin - U.pixelsize - 1);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
immUnbindProgram();
}
}
/* 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(true);
+ 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();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
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);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
UI_icon_draw_ex(headrect.xmax - ((PNL_ICON * 2.2f) / block->aspect),
headrect.ymin + (5.0f / block->aspect),
(panel->flag & PNL_PIN) ? ICON_PINNED : ICON_UNPINNED,
@@ -1064,7 +996,7 @@ void ui_draw_aligned_panel(uiStyle *style,
0.0f,
col_title,
false);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
/* horizontal 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;
@@ -1134,7 +1056,7 @@ void ui_draw_aligned_panel(uiStyle *style,
}
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
/* Draw panel backdrop if it wasn't already been drawn by the single opaque round box earlier.
* Note: Sub-panels blend with panels, so they can't be opaque. */
@@ -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 *************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Category Drawing (Tabs)
+ * \{ */
-static int get_panel_header(const Panel *panel)
+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));
+ }
+
+ /* 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);
+ }
- return PNL_HEADER;
+ /* 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;
- }
- }
- /* outside left/right side */
- else if ((block->rect.xmin > mx) || (block->rect.xmax < mx)) {
- /* pass */
+ if (!IN_RANGE((float)mx, block->rect.xmin, block->rect.xmax)) {
+ return PANEL_MOUSE_OUTSIDE;
}
- 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;
- }
- }
- else if (block->panel->flag & PNL_CLOSEDX) {
- if (my >= block->rect.ymax) {
- button = 1;
- }
+ float expansion_area_xmax = block->rect.xmax;
+ if (show_drag) {
+ expansion_area_xmax -= (PNL_ICON * 1.5f);
}
- 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(true);
- 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(false);
- }
-
- 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(true);
-
-#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(false);
-
- /* 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;
+
+ /* 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);
- /* reset scroll to the top [#38348] */
- UI_view2d_offset(&region->v2d, -1.0f, 1.0f);
+ /* 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);
+ 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 edb5d51a392..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;
}
@@ -276,7 +276,7 @@ uiBut *ui_but_find_mouse_over_ex(ARegion *region, const int x, const int y, cons
float mx = x, my = y;
ui_window_to_block_fl(region, block, &mx, &my);
- for (uiBut *but = block->buttons.last; but; but = but->prev) {
+ LISTBASE_FOREACH_BACKWARD (uiBut *, but, &block->buttons) {
if (ui_but_is_interactive(but, labeledit)) {
if (but->pie_dir != UI_RADIAL_NONE) {
if (ui_but_isect_pie_seg(block, but)) {
@@ -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;
@@ -324,7 +324,7 @@ uiBut *ui_but_find_rect_over(const struct ARegion *region, const rcti *rect_px)
rctf rect_block;
ui_window_to_block_rctf(region, block, &rect_block, &rect_px_fl);
- for (uiBut *but = block->buttons.last; but; but = but->prev) {
+ LISTBASE_FOREACH_BACKWARD (uiBut *, but, &block->buttons) {
if (ui_but_is_interactive(but, labeledit)) {
/* No pie menu support. */
BLI_assert(but->pie_dir == UI_RADIAL_NONE);
@@ -354,7 +354,7 @@ uiBut *ui_list_find_mouse_over_ex(ARegion *region, int x, int y)
LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
float mx = x, my = y;
ui_window_to_block_fl(region, block, &mx, &my);
- for (uiBut *but = block->buttons.last; but; but = but->prev) {
+ LISTBASE_FOREACH_BACKWARD (uiBut *, but, &block->buttons) {
if (but->type == UI_BTYPE_LISTBOX && ui_but_contains_pt(but, mx, my)) {
return but;
}
@@ -399,14 +399,10 @@ uiBut *ui_but_next(uiBut *but)
uiBut *ui_but_first(uiBlock *block)
{
- uiBut *but;
-
- but = block->buttons.first;
- while (but) {
+ LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
if (ui_but_is_editable(but)) {
return but;
}
- but = but->next;
}
return NULL;
}
diff --git a/source/blender/editors/interface/interface_region_color_picker.c b/source/blender/editors/interface/interface_region_color_picker.c
index 0d1b483716e..de80d6270bf 100644
--- a/source/blender/editors/interface/interface_region_color_picker.c
+++ b/source/blender/editors/interface/interface_region_color_picker.c
@@ -183,7 +183,6 @@ static void ui_update_color_picker_buts_rgb(uiBut *from_but,
ColorPicker *cpicker,
const float rgb[3])
{
- uiBut *bt;
float *hsv = cpicker->color_data;
/* Convert from RGB to HSV in perceptually linear space. */
@@ -196,7 +195,7 @@ static void ui_update_color_picker_buts_rgb(uiBut *from_but,
/* this updates button strings,
* is hackish... but button pointers are on stack of caller function */
- for (bt = block->buttons.first; bt; bt = bt->next) {
+ LISTBASE_FOREACH (uiBut *, bt, &block->buttons) {
if (bt->custom_data != cpicker) {
continue;
}
@@ -352,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);
}
@@ -849,9 +848,7 @@ static int ui_colorpicker_small_wheel_cb(const bContext *UNUSED(C),
}
if (add != 0.0f) {
- uiBut *but;
-
- for (but = block->buttons.first; but; but = but->next) {
+ LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
if (but->type == UI_BTYPE_HSVCUBE && but->active == NULL) {
uiPopupBlockHandle *popup = block->handle;
float rgb[3];
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_menu_popup.c b/source/blender/editors/interface/interface_region_menu_popup.c
index 077e8888d53..881ba58174b 100644
--- a/source/blender/editors/interface/interface_region_menu_popup.c
+++ b/source/blender/editors/interface/interface_region_menu_popup.c
@@ -130,9 +130,10 @@ static uiBut *ui_popup_menu_memory__internal(uiBlock *block, uiBut *but)
}
/* get */
- for (but = block->buttons.first; but; but = but->next) {
- if (mem[hash_mod] == ui_popup_string_hash(but->str, but->flag & UI_BUT_HAS_SEP_CHAR)) {
- return but;
+ LISTBASE_FOREACH (uiBut *, but_iter, &block->buttons) {
+ if (mem[hash_mod] ==
+ ui_popup_string_hash(but_iter->str, but_iter->flag & UI_BUT_HAS_SEP_CHAR)) {
+ return but_iter;
}
}
@@ -232,7 +233,6 @@ static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, voi
UI_block_flag_enable(block, UI_BLOCK_MOVEMOUSE_QUIT);
if (pup->popup) {
- uiBut *bt;
int offset[2];
uiBut *but_activate = NULL;
@@ -241,6 +241,7 @@ static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, voi
UI_block_direction_set(block, direction);
/* offset the mouse position, possibly based on earlier selection */
+ uiBut *bt;
if ((block->flag & UI_BLOCK_POPUP_MEMORY) && (bt = ui_popup_menu_memory_get(block))) {
/* position mouse on last clicked item, at 0.8*width of the
* button, so it doesn't overlap the text too much, also note
@@ -257,15 +258,16 @@ static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, voi
/* position mouse at 0.8*width of the button and below the tile
* on the first item */
offset[0] = 0;
- for (bt = block->buttons.first; bt; bt = bt->next) {
- offset[0] = min_ii(offset[0], -(bt->rect.xmin + 0.8f * BLI_rctf_size_x(&bt->rect)));
+ LISTBASE_FOREACH (uiBut *, but_iter, &block->buttons) {
+ offset[0] = min_ii(offset[0],
+ -(but_iter->rect.xmin + 0.8f * BLI_rctf_size_x(&but_iter->rect)));
}
offset[1] = 2.1 * UI_UNIT_Y;
- for (bt = block->buttons.first; bt; bt = bt->next) {
- if (ui_but_is_editable(bt)) {
- but_activate = bt;
+ LISTBASE_FOREACH (uiBut *, but_iter, &block->buttons) {
+ if (ui_but_is_editable(but_iter)) {
+ but_activate = but_iter;
break;
}
}
@@ -284,17 +286,10 @@ static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, voi
else {
/* for a header menu we set the direction automatic */
if (!pup->slideout && flip) {
- ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
- if (area && region) {
- if (ELEM(region->regiontype, RGN_TYPE_HEADER, RGN_TYPE_TOOL_HEADER)) {
- if (RGN_ALIGN_ENUM_FROM_MASK(ED_area_header_alignment(area)) == RGN_ALIGN_BOTTOM) {
- UI_block_direction_set(block, UI_DIR_UP);
- UI_block_order_flip(block);
- }
- }
- if (region->regiontype == RGN_TYPE_FOOTER) {
- if (RGN_ALIGN_ENUM_FROM_MASK(ED_area_footer_alignment(area)) == RGN_ALIGN_BOTTOM) {
+ if (region) {
+ if (RGN_TYPE_IS_HEADER_ANY(region->regiontype)) {
+ if (RGN_ALIGN_ENUM_FROM_MASK(region->alignment) == RGN_ALIGN_BOTTOM) {
UI_block_direction_set(block, UI_DIR_UP);
UI_block_order_flip(block);
}
@@ -506,8 +501,6 @@ uiLayout *UI_popup_menu_layout(uiPopupMenu *pup)
void UI_popup_menu_reports(bContext *C, ReportList *reports)
{
- Report *report;
-
uiPopupMenu *pup = NULL;
uiLayout *layout;
@@ -515,7 +508,7 @@ void UI_popup_menu_reports(bContext *C, ReportList *reports)
return;
}
- for (report = reports->list.first; report; report = report->next) {
+ LISTBASE_FOREACH (Report *, report, &reports->list) {
int icon;
const char *msg, *msg_next;
diff --git a/source/blender/editors/interface/interface_region_popover.c b/source/blender/editors/interface/interface_region_popover.c
index 0ad7e570e80..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);
}
@@ -171,7 +171,6 @@ static uiBlock *ui_block_func_POPOVER(bContext *C, uiPopupBlockHandle *handle, v
}
if (!slideout) {
- ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
if (region && region->panels.first) {
@@ -180,14 +179,9 @@ static uiBlock *ui_block_func_POPOVER(bContext *C, uiPopupBlockHandle *handle, v
UI_block_direction_set(block, UI_DIR_UP | UI_DIR_CENTER_X);
}
/* Prefer popover from header to be positioned into the editor. */
- else if (area && region) {
- if (ELEM(region->regiontype, RGN_TYPE_HEADER, RGN_TYPE_TOOL_HEADER)) {
- if (RGN_ALIGN_ENUM_FROM_MASK(ED_area_header_alignment(area)) == RGN_ALIGN_BOTTOM) {
- UI_block_direction_set(block, UI_DIR_UP | UI_DIR_CENTER_X);
- }
- }
- if (region->regiontype == RGN_TYPE_FOOTER) {
- if (RGN_ALIGN_ENUM_FROM_MASK(ED_area_footer_alignment(area)) == RGN_ALIGN_BOTTOM) {
+ else if (region) {
+ if (RGN_TYPE_IS_HEADER_ANY(region->regiontype)) {
+ if (RGN_ALIGN_ENUM_FROM_MASK(region->alignment) == RGN_ALIGN_BOTTOM) {
UI_block_direction_set(block, UI_DIR_UP | UI_DIR_CENTER_X);
}
}
@@ -209,11 +203,12 @@ static uiBlock *ui_block_func_POPOVER(bContext *C, uiPopupBlockHandle *handle, v
if (!handle->refresh) {
uiBut *but = NULL;
uiBut *but_first = NULL;
- for (but = block->buttons.first; but; but = but->next) {
- if ((but_first == NULL) && ui_but_is_editable(but)) {
- but_first = but;
+ LISTBASE_FOREACH (uiBut *, but_iter, &block->buttons) {
+ if ((but_first == NULL) && ui_but_is_editable(but_iter)) {
+ but_first = but_iter;
}
- if (but->flag & (UI_SELECT | UI_SELECT_DRAW)) {
+ if (but_iter->flag & (UI_SELECT | UI_SELECT_DRAW)) {
+ but = but_iter;
break;
}
}
@@ -259,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 13c85952f52..5445b098e5b 100644
--- a/source/blender/editors/interface/interface_region_popup.c
+++ b/source/blender/editors/interface/interface_region_popup.c
@@ -59,8 +59,6 @@
*/
void ui_popup_translate(ARegion *region, const int mdiff[2])
{
- uiBlock *block;
-
BLI_rcti_translate(&region->winrct, UNPACK2(mdiff));
ED_region_update_rect(region);
@@ -68,13 +66,12 @@ void ui_popup_translate(ARegion *region, const int mdiff[2])
ED_region_tag_redraw(region);
/* update blocks */
- for (block = region->uiblocks.first; block; block = block->next) {
+ LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
uiPopupBlockHandle *handle = block->handle;
/* Make empty, will be initialized on next use, see T60608. */
BLI_rctf_init(&handle->prev_block_rect, 0, 0, 0, 0);
- uiSafetyRct *saferct;
- for (saferct = block->saferct.first; saferct; saferct = saferct->next) {
+ LISTBASE_FOREACH (uiSafetyRct *, saferct, &block->saferct) {
BLI_rctf_translate(&saferct->parent, UNPACK2(mdiff));
BLI_rctf_translate(&saferct->safety, UNPACK2(mdiff));
}
@@ -373,16 +370,13 @@ static void ui_block_region_refresh(const bContext *C, ARegion *region)
{
ScrArea *ctx_area = CTX_wm_area(C);
ARegion *ctx_region = CTX_wm_region(C);
- uiBlock *block;
if (region->do_draw & RGN_REFRESH_UI) {
ScrArea *handle_ctx_area;
ARegion *handle_ctx_region;
- uiBlock *block_next;
region->do_draw &= ~RGN_REFRESH_UI;
- for (block = region->uiblocks.first; block; block = block_next) {
- block_next = block->next;
+ LISTBASE_FOREACH_MUTABLE (uiBlock *, block, &region->uiblocks) {
uiPopupBlockHandle *handle = block->handle;
if (handle->can_refresh) {
@@ -409,9 +403,7 @@ static void ui_block_region_refresh(const bContext *C, ARegion *region)
static void ui_block_region_draw(const bContext *C, ARegion *region)
{
- uiBlock *block;
-
- for (block = region->uiblocks.first; block; block = block->next) {
+ LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
UI_block_draw(C, block);
}
}
@@ -441,7 +433,6 @@ static void ui_block_region_popup_window_listener(wmWindow *UNUSED(win),
static void ui_popup_block_clip(wmWindow *window, uiBlock *block)
{
- uiBut *bt;
const float xmin_orig = block->rect.xmin;
const int margin = UI_SCREEN_MARGIN;
int winx, winy;
@@ -475,7 +466,7 @@ static void ui_popup_block_clip(wmWindow *window, uiBlock *block)
/* ensure menu items draw inside left/right boundary */
const float xofs = block->rect.xmin - xmin_orig;
- for (bt = block->buttons.first; bt; bt = bt->next) {
+ LISTBASE_FOREACH (uiBut *, bt, &block->buttons) {
bt->rect.xmin += xofs;
bt->rect.xmax += xofs;
}
@@ -483,11 +474,9 @@ static void ui_popup_block_clip(wmWindow *window, uiBlock *block)
void ui_popup_block_scrolltest(uiBlock *block)
{
- uiBut *bt;
-
block->flag &= ~(UI_BLOCK_CLIPBOTTOM | UI_BLOCK_CLIPTOP);
- for (bt = block->buttons.first; bt; bt = bt->next) {
+ LISTBASE_FOREACH (uiBut *, bt, &block->buttons) {
bt->flag &= ~UI_SCROLLED;
}
@@ -496,7 +485,7 @@ void ui_popup_block_scrolltest(uiBlock *block)
}
/* mark buttons that are outside boundary */
- for (bt = block->buttons.first; bt; bt = bt->next) {
+ LISTBASE_FOREACH (uiBut *, bt, &block->buttons) {
if (bt->rect.ymin < block->rect.ymin) {
bt->flag |= UI_SCROLLED;
block->flag |= UI_BLOCK_CLIPBOTTOM;
@@ -508,7 +497,7 @@ void ui_popup_block_scrolltest(uiBlock *block)
}
/* mark buttons overlapping arrows, if we have them */
- for (bt = block->buttons.first; bt; bt = bt->next) {
+ LISTBASE_FOREACH (uiBut *, bt, &block->buttons) {
if (block->flag & UI_BLOCK_CLIPBOTTOM) {
if (bt->rect.ymin < block->rect.ymin + UI_MENU_SCROLL_ARROW) {
bt->flag |= UI_SCROLLED;
@@ -535,9 +524,10 @@ static void ui_popup_block_remove(bContext *C, uiPopupBlockHandle *handle)
/* There may actually be a different window active than the one showing the popup, so lookup real
* one. */
if (BLI_findindex(&screen->regionbase, handle->region) == -1) {
- for (win = wm->windows.first; win; win = win->next) {
- screen = WM_window_get_active_screen(win);
+ LISTBASE_FOREACH (wmWindow *, win_iter, &wm->windows) {
+ screen = WM_window_get_active_screen(win_iter);
if (BLI_findindex(&screen->regionbase, handle->region) != -1) {
+ win = win_iter;
break;
}
}
@@ -575,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;
@@ -648,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;
@@ -722,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 2010d89165e..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;
@@ -592,15 +592,15 @@ static void ui_searchbox_region_draw_cb(const bContext *C, ARegion *region)
/* indicate more */
if (data->items.more) {
ui_searchbox_butrect(&rect, data, data->items.maxitem - 1);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
UI_icon_draw(rect.xmax - 18, rect.ymin - 7, ICON_TRIA_DOWN);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
if (data->items.offset) {
ui_searchbox_butrect(&rect, data, 0);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
UI_icon_draw(rect.xmin, rect.ymax - 9, ICON_TRIA_UP);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
}
else {
@@ -657,15 +657,15 @@ static void ui_searchbox_region_draw_cb(const bContext *C, ARegion *region)
/* indicate more */
if (data->items.more) {
ui_searchbox_butrect(&rect, data, data->items.maxitem - 1);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
UI_icon_draw((BLI_rcti_size_x(&rect)) / 2, rect.ymin - 9, ICON_TRIA_DOWN);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
if (data->items.offset) {
ui_searchbox_butrect(&rect, data, 0);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
UI_icon_draw((BLI_rcti_size_x(&rect)) / 2, rect.ymax - 7, ICON_TRIA_UP);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
}
}
@@ -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;
@@ -953,15 +953,15 @@ static void ui_searchbox_region_draw_cb__operator(const bContext *UNUSED(C), ARe
/* indicate more */
if (data->items.more) {
ui_searchbox_butrect(&rect, data, data->items.maxitem - 1);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
UI_icon_draw((BLI_rcti_size_x(&rect)) / 2, rect.ymin - 9, ICON_TRIA_DOWN);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
if (data->items.offset) {
ui_searchbox_butrect(&rect, data, 0);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
UI_icon_draw((BLI_rcti_size_x(&rect)) / 2, rect.ymax - 7, ICON_TRIA_UP);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
}
}
diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c
index 41b41cb3d75..b3a65e024f3 100644
--- a/source/blender/editors/interface/interface_region_tooltip.c
+++ b/source/blender/editors/interface/interface_region_tooltip.c
@@ -63,7 +63,7 @@
#include "BLT_translation.h"
#ifdef WITH_PYTHON
-# include "BPY_extern.h"
+# include "BPY_extern_run.h"
#endif
#include "ED_screen.h"
@@ -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;
@@ -433,7 +433,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
if (has_valid_context == false) {
expr_result = BLI_strdup(has_valid_context_error);
}
- else if (BPY_execute_string_as_string(C, expr_imports, expr, __func__, &expr_result)) {
+ else if (BPY_run_string_as_string(C, expr_imports, expr, __func__, &expr_result)) {
if (STREQ(expr_result, "")) {
MEM_freeN(expr_result);
expr_result = NULL;
@@ -490,7 +490,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
if (has_valid_context == false) {
expr_result = BLI_strdup(has_valid_context_error);
}
- else if (BPY_execute_string_as_string(C, expr_imports, expr, __func__, &expr_result)) {
+ else if (BPY_run_string_as_string(C, expr_imports, expr, __func__, &expr_result)) {
if (STREQ(expr_result, ".")) {
MEM_freeN(expr_result);
expr_result = NULL;
@@ -541,7 +541,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
}
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);
@@ -594,7 +594,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
if (has_valid_context == false) {
shortcut = BLI_strdup(has_valid_context_error);
}
- else if (BPY_execute_string_as_intptr(C, expr_imports, expr, __func__, &expr_result)) {
+ else if (BPY_run_string_as_intptr(C, expr_imports, expr, __func__, &expr_result)) {
if (expr_result != 0) {
wmKeyMap *keymap = (wmKeyMap *)expr_result;
LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) {
@@ -658,7 +658,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
if (has_valid_context == false) {
/* pass */
}
- else if (BPY_execute_string_as_string_and_size(
+ else if (BPY_run_string_as_string_and_size(
C, expr_imports, expr, __func__, &expr_result, &expr_result_len)) {
/* pass. */
}
@@ -736,7 +736,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
if (has_valid_context == false) {
/* pass */
}
- else if (BPY_execute_string_as_intptr(C, expr_imports, expr, __func__, &expr_result)) {
+ else if (BPY_run_string_as_intptr(C, expr_imports, expr, __func__, &expr_result)) {
if (expr_result != 0) {
{
uiTooltipField *field = text_field_add(data,
@@ -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 5310ff0e3ec..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));
}
@@ -426,7 +426,6 @@ int UI_fontstyle_height_max(const uiFontStyle *fs)
/* reading without uifont will create one */
void uiStyleInit(void)
{
- uiFont *font;
uiStyle *style = U.uistyles.first;
/* recover from uninitialized dpi */
@@ -435,7 +434,7 @@ void uiStyleInit(void)
}
CLAMP(U.dpi, 48, 144);
- for (font = U.uifonts.first; font; font = font->next) {
+ LISTBASE_FOREACH (uiFont *, font, &U.uifonts) {
BLF_unload_id(font->blf_id);
}
@@ -449,24 +448,24 @@ void uiStyleInit(void)
blf_mono_font_render = -1;
}
- font = U.uifonts.first;
+ uiFont *font_first = U.uifonts.first;
/* default builtin */
- if (font == NULL) {
- font = MEM_callocN(sizeof(uiFont), "ui font");
- BLI_addtail(&U.uifonts, font);
+ if (font_first == NULL) {
+ font_first = MEM_callocN(sizeof(uiFont), "ui font");
+ BLI_addtail(&U.uifonts, font_first);
}
if (U.font_path_ui[0]) {
- BLI_strncpy(font->filename, U.font_path_ui, sizeof(font->filename));
- font->uifont_id = UIFONT_CUSTOM1;
+ BLI_strncpy(font_first->filename, U.font_path_ui, sizeof(font_first->filename));
+ font_first->uifont_id = UIFONT_CUSTOM1;
}
else {
- BLI_strncpy(font->filename, "default", sizeof(font->filename));
- font->uifont_id = UIFONT_DEFAULT;
+ BLI_strncpy(font_first->filename, "default", sizeof(font_first->filename));
+ font_first->uifont_id = UIFONT_DEFAULT;
}
- for (font = U.uifonts.first; font; font = font->next) {
+ LISTBASE_FOREACH (uiFont *, font, &U.uifonts) {
const bool unique = false;
if (font->uifont_id == UIFONT_DEFAULT) {
@@ -518,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) {
@@ -535,7 +535,7 @@ void uiStyleInit(void)
flag_enable |= BLF_MONOCHROME;
}
- for (font = U.uifonts.first; font; font = font->next) {
+ LISTBASE_FOREACH (uiFont *, font, &U.uifonts) {
if (font->blf_id != -1) {
BLF_disable(font->blf_id, flag_disable);
BLF_enable(font->blf_id, flag_enable);
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 56807621358..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,11 +395,10 @@ static void id_search_cb(const bContext *C,
{
TemplateID *template_ui = (TemplateID *)arg_template;
ListBase *lb = template_ui->idlb;
- ID *id;
- int flag = RNA_property_flag(template_ui->prop);
+ const int flag = RNA_property_flag(template_ui->prop);
/* ID listbase */
- for (id = lb->first; id; id = id->next) {
+ LISTBASE_FOREACH (ID *, id, lb) {
if (!id_search_add(C, template_ui, flag, str, items, id)) {
break;
}
@@ -416,11 +415,10 @@ static void id_search_cb_tagged(const bContext *C,
{
TemplateID *template_ui = (TemplateID *)arg_template;
ListBase *lb = template_ui->idlb;
- ID *id;
- int flag = RNA_property_flag(template_ui->prop);
+ const int flag = RNA_property_flag(template_ui->prop);
/* ID listbase */
- for (id = lb->first; id; id = id->next) {
+ LISTBASE_FOREACH (ID *, id, lb) {
if (id->tag & LIB_TAG_DOIT) {
if (!id_search_add(C, template_ui, flag, str, items, id)) {
break;
@@ -522,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) {
@@ -572,6 +570,15 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
if (override_id != NULL) {
BKE_main_id_clear_newpoins(bmain);
+ if (GS(override_id->name) == ID_OB) {
+ Scene *scene = CTX_data_scene(C);
+ if (!BKE_collection_has_object_recursive(scene->master_collection,
+ (Object *)override_id)) {
+ BKE_collection_object_add_from(
+ bmain, scene, (Object *)id, (Object *)override_id);
+ }
+ }
+
/* Assign new pointer, takes care of updates/notifiers */
RNA_id_pointer_create(override_id, &idptr);
}
@@ -1051,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,
@@ -1852,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);
@@ -1876,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);
@@ -1962,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);
@@ -1982,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);
@@ -1994,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);
@@ -2030,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);
@@ -2040,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);
@@ -2053,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. */
@@ -2112,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);
@@ -2135,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);
@@ -2201,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);
@@ -2219,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);
@@ -2303,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");
}
@@ -2411,9 +2411,8 @@ static eAutoPropButsReturn template_operator_property_buts_draw_single(
/* Only do this if we're not refreshing an existing UI. */
if (block->oldblock == NULL) {
const bool is_popup = (block->flag & UI_BLOCK_KEEP_OPEN) != 0;
- uiBut *but;
- for (but = block->buttons.first; but; but = but->next) {
+ LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
/* no undo for buttons for operator redo panels */
UI_but_flag_disable(but, UI_BUT_UNDO);
@@ -3029,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;
@@ -3212,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);
@@ -3887,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);
@@ -4229,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;
@@ -4702,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);
@@ -5406,13 +5405,12 @@ void uiTemplatePalette(uiLayout *layout,
PropertyRNA *prop = RNA_struct_find_property(ptr, propname);
PointerRNA cptr;
Palette *palette;
- PaletteColor *color;
uiBlock *block;
uiLayout *col;
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);
@@ -5428,8 +5426,6 @@ void uiTemplatePalette(uiLayout *layout,
palette = cptr.data;
- color = palette->colors.first;
-
col = uiLayoutColumn(layout, true);
uiLayoutRow(col, true);
uiDefIconButO(block,
@@ -5452,7 +5448,7 @@ void uiTemplatePalette(uiLayout *layout,
UI_UNIT_X,
UI_UNIT_Y,
NULL);
- if (color) {
+ if (palette->colors.first != NULL) {
but = uiDefIconButO(block,
UI_BTYPE_BUT,
"PALETTE_OT_color_move",
@@ -5487,7 +5483,7 @@ void uiTemplatePalette(uiLayout *layout,
col = uiLayoutColumn(layout, true);
uiLayoutRow(col, true);
- for (; color; color = color->next) {
+ LISTBASE_FOREACH (PaletteColor *, color, &palette->colors) {
PointerRNA color_ptr;
if (row_cols >= cols_per_row) {
@@ -5561,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;
@@ -5596,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) {
@@ -5644,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;
@@ -5759,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;
@@ -5771,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;
@@ -6209,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);
@@ -6296,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) {
@@ -6346,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)) {
@@ -6641,9 +6637,8 @@ void uiTemplateRunningJobs(uiLayout *layout, bContext *C)
UI_block_func_handle_set(block, do_running_jobs, NULL);
- Scene *scene;
/* another scene can be rendering too, for example via compositor */
- for (scene = bmain->scenes.first; scene; scene = scene->id.next) {
+ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
if (WM_jobs_test(wm, scene, WM_JOB_TYPE_ANY)) {
handle_event = B_STOPOTHER;
icon = ICON_NONE;
@@ -6736,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);
@@ -7084,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++) {
@@ -7357,10 +7352,12 @@ void uiTemplateCacheFile(uiLayout *layout,
int uiTemplateRecentFiles(uiLayout *layout, int rows)
{
- const RecentFile *recent;
int i;
+ LISTBASE_FOREACH_INDEX (RecentFile *, recent, &G.recent_files, i) {
+ if (i >= rows) {
+ break;
+ }
- for (recent = G.recent_files.first, i = 0; (i < rows) && (recent); recent = recent->next, i++) {
const char *filename = BLI_path_basename(recent->filepath);
PointerRNA ptr;
uiItemFullO(layout,
diff --git a/source/blender/editors/interface/interface_utils.c b/source/blender/editors/interface/interface_utils.c
index 4a1c7be918e..c413cac6023 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);
@@ -489,7 +489,7 @@ void ui_rna_collection_search_update_fn(const struct bContext *C,
/* If no item has an own icon to display, libraries can use the library icons rather than the
* name prefix for showing the library status. */
int name_prefix_offset = cis->name_prefix_offset;
- if (!has_id_icon && cis->is_id) {
+ if (!has_id_icon && cis->is_id && !requires_exact_data_name) {
cis->iconid = UI_library_icon_get(cis->data);
/* No need to re-allocate, string should be shorter than before (lib status prefix is
* removed). */
@@ -714,12 +714,8 @@ bool UI_butstore_is_valid(uiButStore *bs)
bool UI_butstore_is_registered(uiBlock *block, uiBut *but)
{
- uiButStore *bs_handle;
-
- for (bs_handle = block->butstore.first; bs_handle; bs_handle = bs_handle->next) {
- uiButStoreElem *bs_elem;
-
- for (bs_elem = bs_handle->items.first; bs_elem; bs_elem = bs_elem->next) {
+ LISTBASE_FOREACH (uiButStore *, bs_handle, &block->butstore) {
+ LISTBASE_FOREACH (uiButStoreElem *, bs_elem, &bs_handle->items) {
if (*bs_elem->but_p == but) {
return true;
}
@@ -740,10 +736,7 @@ void UI_butstore_register(uiButStore *bs_handle, uiBut **but_p)
void UI_butstore_unregister(uiButStore *bs_handle, uiBut **but_p)
{
- uiButStoreElem *bs_elem, *bs_elem_next;
-
- for (bs_elem = bs_handle->items.first; bs_elem; bs_elem = bs_elem_next) {
- bs_elem_next = bs_elem->next;
+ LISTBASE_FOREACH_MUTABLE (uiButStoreElem *, bs_elem, &bs_handle->items) {
if (bs_elem->but_p == but_p) {
BLI_remlink(&bs_handle->items, bs_elem);
MEM_freeN(bs_elem);
@@ -758,12 +751,10 @@ void UI_butstore_unregister(uiButStore *bs_handle, uiBut **but_p)
*/
bool UI_butstore_register_update(uiBlock *block, uiBut *but_dst, const uiBut *but_src)
{
- uiButStore *bs_handle;
bool found = false;
- for (bs_handle = block->butstore.first; bs_handle; bs_handle = bs_handle->next) {
- uiButStoreElem *bs_elem;
- for (bs_elem = bs_handle->items.first; bs_elem; bs_elem = bs_elem->next) {
+ LISTBASE_FOREACH (uiButStore *, bs_handle, &block->butstore) {
+ LISTBASE_FOREACH (uiButStoreElem *, bs_elem, &bs_handle->items) {
if (*bs_elem->but_p == but_src) {
*bs_elem->but_p = but_dst;
found = true;
@@ -779,14 +770,9 @@ bool UI_butstore_register_update(uiBlock *block, uiBut *but_dst, const uiBut *bu
*/
void UI_butstore_clear(uiBlock *block)
{
- uiButStore *bs_handle;
-
- for (bs_handle = block->butstore.first; bs_handle; bs_handle = bs_handle->next) {
- uiButStoreElem *bs_elem;
-
+ LISTBASE_FOREACH (uiButStore *, bs_handle, &block->butstore) {
bs_handle->block = NULL;
-
- for (bs_elem = bs_handle->items.first; bs_elem; bs_elem = bs_elem->next) {
+ LISTBASE_FOREACH (uiButStoreElem *, bs_elem, &bs_handle->items) {
*bs_elem->but_p = NULL;
}
}
@@ -797,8 +783,6 @@ void UI_butstore_clear(uiBlock *block)
*/
void UI_butstore_update(uiBlock *block)
{
- uiButStore *bs_handle;
-
/* move this list to the new block */
if (block->oldblock) {
if (block->oldblock->butstore.first) {
@@ -812,17 +796,14 @@ void UI_butstore_update(uiBlock *block)
/* warning, loop-in-loop, in practice we only store <10 buttons at a time,
* so this isn't going to be a problem, if that changes old-new mapping can be cached first */
- for (bs_handle = block->butstore.first; bs_handle; bs_handle = bs_handle->next) {
-
+ LISTBASE_FOREACH (uiButStore *, bs_handle, &block->butstore) {
BLI_assert((bs_handle->block == NULL) || (bs_handle->block == block) ||
(block->oldblock && block->oldblock == bs_handle->block));
if (bs_handle->block == block->oldblock) {
- uiButStoreElem *bs_elem;
-
bs_handle->block = block;
- for (bs_elem = bs_handle->items.first; bs_elem; bs_elem = bs_elem->next) {
+ LISTBASE_FOREACH (uiButStoreElem *, bs_elem, &bs_handle->items) {
if (*bs_elem->but_p) {
uiBut *but_new = ui_but_find_new(block, *bs_elem->but_p);
diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c
index c4de2730600..ab0f6d90caa 100644
--- a/source/blender/editors/interface/interface_widgets.c
+++ b/source/blender/editors/interface/interface_widgets.c
@@ -29,6 +29,7 @@
#include "DNA_brush_types.h"
#include "DNA_userdef_types.h"
+#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_rect.h"
#include "BLI_string.h"
@@ -474,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);
@@ -525,9 +526,10 @@ void UI_draw_anti_tria(
/* Note: This won't give back the original color. */
draw_color[3] *= 1.0f / WIDGET_AA_JITTER;
- GPU_blend(true);
+ 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);
@@ -544,19 +546,19 @@ void UI_draw_anti_tria(
immUnbindProgram();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
/* triangle 'icon' inside rect */
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);
}
@@ -569,9 +571,10 @@ void UI_draw_anti_fan(float tri_array[][2], uint length, const float color[4])
copy_v4_v4(draw_color, color);
draw_color[3] *= 2.0f / WIDGET_AA_JITTER;
- GPU_blend(true);
+ 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);
@@ -593,7 +596,7 @@ void UI_draw_anti_fan(float tri_array[][2], uint length, const float color[4])
immUnbindProgram();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
static void widget_init(uiWidgetBase *wtb)
@@ -705,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) ||
@@ -1011,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;
@@ -1095,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,
@@ -1118,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);
}
@@ -1170,7 +1174,7 @@ void UI_widgetbase_draw_cache_flush(void)
/* draw single */
GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE);
GPU_batch_uniform_4fv_array(
- batch, "parameters", MAX_WIDGET_PARAMETERS, (float *)g_widget_base_batch.params);
+ batch, "parameters", MAX_WIDGET_PARAMETERS, (float(*)[4])g_widget_base_batch.params);
GPU_batch_uniform_3fv(batch, "checkerColorAndSize", checker_params);
GPU_batch_draw(batch);
}
@@ -1179,14 +1183,9 @@ void UI_widgetbase_draw_cache_flush(void)
GPU_batch_uniform_4fv_array(batch,
"parameters",
MAX_WIDGET_PARAMETERS * MAX_WIDGET_BASE_BATCH,
- (float *)g_widget_base_batch.params);
+ (float(*)[4])g_widget_base_batch.params);
GPU_batch_uniform_3fv(batch, "checkerColorAndSize", checker_params);
- GPU_matrix_bind(batch->interface);
- GPU_shader_set_srgb_uniform(batch->interface);
- GPU_batch_bind(batch);
- GPU_batch_draw_advanced(batch, 0, 0, 0, g_widget_base_batch.count);
-
- GPU_batch_program_use_end(batch);
+ GPU_batch_draw_instanced(batch, g_widget_base_batch.count);
}
g_widget_base_batch.count = 0;
}
@@ -1202,11 +1201,11 @@ void UI_widgetbase_draw_cache_end(void)
BLI_assert(g_widget_base_batch.enabled == true);
g_widget_base_batch.enabled = false;
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
UI_widgetbase_draw_cache_flush();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
/* Disable cached/instanced drawing and enforce single widget drawing pipeline.
@@ -1252,7 +1251,7 @@ static void draw_widgetbase_batch(uiWidgetBase *wtb)
GPUBatch *batch = ui_batch_roundbox_widget_get();
GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE);
GPU_batch_uniform_4fv_array(
- batch, "parameters", MAX_WIDGET_PARAMETERS, (float *)&wtb->uniform_params);
+ batch, "parameters", MAX_WIDGET_PARAMETERS, (float(*)[4]) & wtb->uniform_params);
GPU_batch_uniform_3fv(batch, "checkerColorAndSize", checker_params);
GPU_batch_draw(batch);
}
@@ -1312,9 +1311,9 @@ static void widgetbase_draw_ex(uiWidgetBase *wtb,
widgetbase_set_uniform_colors_ubv(
wtb, inner_col1, inner_col2, outline_col, emboss_col, tria_col, show_alpha_checkers);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
draw_widgetbase_batch(wtb);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
}
@@ -1347,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);
}
@@ -1368,9 +1367,9 @@ static void widget_draw_icon(
float aspect, height;
if (but->flag & UI_BUT_ICON_PREVIEW) {
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
widget_draw_preview(icon, alpha, rect);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
return;
}
@@ -1406,10 +1405,10 @@ static void widget_draw_icon(
}
}
- GPU_blend(true);
+ 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 */
@@ -1437,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)) {
@@ -1459,7 +1458,7 @@ static void widget_draw_icon(
}
}
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
static void widget_draw_submenu_tria(const uiBut *but,
@@ -1480,16 +1479,16 @@ static void widget_draw_submenu_tria(const uiBut *but,
BLI_rctf_init(&tria_rect, xs, xs + tria_width, ys, ys + tria_height);
BLI_rctf_scale(&tria_rect, 0.4f);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
UI_widgetbase_draw_cache_flush();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
ui_draw_anti_tria_rect(&tria_rect, 'h', col);
}
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;
}
@@ -1497,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;
}
@@ -1826,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 */
@@ -2027,9 +2026,9 @@ static void widget_draw_text(const uiFontStyle *fstyle,
if (drawstr[0] != 0) {
/* We are drawing on top of widget bases. Flush cache. */
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
UI_widgetbase_draw_cache_flush();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
if (but->selsta >= but->ofs) {
selsta_draw = BLF_width(fstyle->uifont_id, drawstr + but->ofs, but->selsta - but->ofs);
@@ -2074,9 +2073,9 @@ static void widget_draw_text(const uiFontStyle *fstyle,
t = 0;
}
/* We are drawing on top of widget bases. Flush cache. */
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
UI_widgetbase_draw_cache_flush();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
@@ -2247,7 +2246,7 @@ static void widget_draw_extra_icons(const uiWidgetColors *wcol,
float alpha)
{
/* inverse order, from right to left. */
- for (uiButExtraOpIcon *op_icon = but->extra_op_icons.last; op_icon; op_icon = op_icon->prev) {
+ LISTBASE_FOREACH_BACKWARD (uiButExtraOpIcon *, op_icon, &but->extra_op_icons) {
rcti temp = *rect;
temp.xmin = temp.xmax - (BLI_rcti_size_y(rect) * 1.08f);
@@ -2271,9 +2270,9 @@ static void widget_draw_node_link_socket(const uiWidgetColors *wcol,
rgba_uchar_to_float(col, but->col);
col[3] *= alpha;
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
UI_widgetbase_draw_cache_flush();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
ED_node_socket_draw(but->custom_data, rect, col, scale);
}
@@ -2289,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;
@@ -2332,9 +2331,9 @@ static void widget_draw_text_icon(const uiFontStyle *fstyle,
/* draw icon in rect above the space reserved for the label */
rect->ymin += text_size;
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
widget_draw_preview(icon, alpha, rect);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
/* offset rect to draw label in */
rect->ymin -= text_size;
@@ -2357,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;
@@ -2402,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;
}
@@ -2490,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);
@@ -2749,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);
@@ -2762,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();
@@ -2789,32 +2789,31 @@ static void widget_menu_back(uiWidgetColors *wcol, rcti *rect, int flag, int dir
rect->ymax += 0.1f * U.widget_unit;
}
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
widget_softshadow(rect, roundboxalign, wcol->roundness * U.widget_unit);
round_box_edges(&wtb, roundboxalign, rect, wcol->roundness * U.widget_unit);
wtb.draw_emboss = false;
widgetbase_draw(&wtb, wcol);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
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);
immUniformColor3f(1.0f, 1.0f, 1.0f);
imm_draw_circle_fill_2d(pos, x, y, 3.0f * U.pixelsize, 8);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
GPU_line_smooth(true);
immUniformColor3f(0.0f, 0.0f, 0.0f);
imm_draw_circle_wire_2d(pos, x, y, 3.0f * U.pixelsize, 12);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
GPU_line_smooth(false);
immUnbindProgram();
@@ -2866,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);
@@ -2904,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);
@@ -2914,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];
@@ -2942,7 +2941,7 @@ static void ui_draw_but_HSVCIRCLE(uiBut *but, const uiWidgetColors *wcol, const
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
GPU_line_smooth(true);
immUniformColor3ubv(wcol->outline);
@@ -2950,7 +2949,7 @@ static void ui_draw_but_HSVCIRCLE(uiBut *but, const uiWidgetColors *wcol, const
immUnbindProgram();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
GPU_line_smooth(false);
/* cursor */
@@ -2980,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 */
@@ -3035,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);
@@ -3198,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));
@@ -3229,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);
}
@@ -3249,9 +3249,9 @@ static void ui_draw_but_HSV_v(uiBut *but, const rcti *rect)
}));
/* We are drawing on top of widget bases. Flush cache. */
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
UI_widgetbase_draw_cache_flush();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
/* cursor */
x = rect->xmin + 0.5f * BLI_rcti_size_x(rect);
@@ -3264,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],
@@ -3272,10 +3272,11 @@ 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(true);
+ GPU_blend(GPU_BLEND_ALPHA);
immUniformColor4ubv(col);
GPU_line_width(1.0f);
@@ -3284,7 +3285,7 @@ static void ui_draw_separator(const rcti *rect, const uiWidgetColors *wcol)
immVertex2f(pos, rect->xmax, y);
immEnd();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
immUnbindProgram();
}
@@ -3624,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 */
@@ -3652,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);
@@ -3668,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;
@@ -3717,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;
@@ -3726,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) {
@@ -3834,19 +3835,20 @@ 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);
bw += (bw < 0.5f) ? 0.5f : -0.5f;
/* We are drawing on top of widget bases. Flush cache. */
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
UI_widgetbase_draw_cache_flush();
- GPU_blend(false);
+ 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);
@@ -3974,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);
@@ -4016,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;
@@ -4206,9 +4208,9 @@ static void widget_tab(uiWidgetColors *wcol, rcti *rect, int state, int roundbox
widgetbase_draw(&wtb, wcol);
/* We are drawing on top of widget bases. Flush cache. */
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
UI_widgetbase_draw_cache_flush();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
#ifdef USE_TAB_SHADED_HIGHLIGHT
/* draw outline (3d look) */
@@ -4238,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 */
@@ -4480,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);
@@ -4823,12 +4826,12 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu
}
if (disabled) {
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
}
wt->text(fstyle, &wt->wcol, but, rect);
if (disabled) {
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
}
}
@@ -4911,7 +4914,7 @@ static void ui_draw_popover_back_impl(const uiWidgetColors *wcol,
rect->xmax - unit_size) :
BLI_rcti_cent_x(rect);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
/* Extracted from 'widget_menu_back', keep separate to avoid menu changes breaking popovers */
{
@@ -4928,14 +4931,15 @@ 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);
const int sign = is_down ? 1 : -1;
float y = is_down ? rect->ymax : rect->ymin;
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
immBegin(GPU_PRIM_TRIS, 3);
immUniformColor4ub(UNPACK3(wcol->outline), 166);
immVertex2f(pos, cent_x - unit_half, y);
@@ -4945,7 +4949,7 @@ static void ui_draw_popover_back_impl(const uiWidgetColors *wcol,
y = y - sign * round(U.pixelsize * 1.41);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
immBegin(GPU_PRIM_TRIS, 3);
immUniformColor4ub(0, 0, 0, 0);
immVertex2f(pos, cent_x - unit_half, y);
@@ -4953,7 +4957,7 @@ static void ui_draw_popover_back_impl(const uiWidgetColors *wcol,
immVertex2f(pos, cent_x, y + sign * unit_half);
immEnd();
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
immBegin(GPU_PRIM_TRIS, 3);
immUniformColor4ubv(wcol->inner);
immVertex2f(pos, cent_x - unit_half, y);
@@ -4964,7 +4968,7 @@ static void ui_draw_popover_back_impl(const uiWidgetColors *wcol,
immUnbindProgram();
}
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
void ui_draw_popover_back(struct ARegion *region,
@@ -5049,23 +5053,23 @@ 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);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
if (btheme->tui.wcol_pie_menu.shaded) {
uchar col1[4], col2[4];
shadecolors4(col1,
@@ -5123,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);
@@ -5134,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,
@@ -5148,7 +5153,7 @@ void ui_draw_pie_center(uiBlock *block)
false);
}
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
GPU_matrix_pop();
}
@@ -5169,11 +5174,9 @@ static void ui_draw_widget_back_color(uiWidgetTypeEnum type,
uiWidgetType *wt = widget_type(type);
if (use_shadow) {
- GPU_blend(true);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
widget_softshadow(rect, UI_CNR_ALL, 0.25f * U.widget_unit);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
rcti rect_copy = *rect;
@@ -5220,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);
@@ -5290,16 +5293,16 @@ 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;
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
/* XXX scale weak get from fstyle? */
UI_icon_draw_ex(xs, ys, iconid, aspect, 1.0f, 0.0f, wt->wcol.text, false);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
/* part text right aligned */
@@ -5335,9 +5338,9 @@ void ui_draw_preview_item(
/* draw icon in rect above the space reserved for the label */
rect->ymin += text_size;
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
widget_draw_preview(iconid, 1.0f, rect);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
BLF_width_and_height(
fstyle->uifont_id, name, BLF_DRAW_STR_DUMMY_MAX, &font_dims[0], &font_dims[1]);
diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c
index 10c31082b2c..4b72d17beb3 100644
--- a/source/blender/editors/interface/resources.c
+++ b/source/blender/editors/interface/resources.c
@@ -777,9 +777,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;
@@ -1477,13 +1474,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 3efed43e08c..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;
@@ -853,14 +853,23 @@ void UI_view2d_curRect_validate(View2D *v2d)
ui_view2d_curRect_validate_resize(v2d, false);
}
+void UI_view2d_curRect_changed(const bContext *C, View2D *v2d)
+{
+ UI_view2d_curRect_validate(v2d);
+
+ ARegion *region = CTX_wm_region(C);
+
+ if (region->type->on_view2d_changed != NULL) {
+ region->type->on_view2d_changed(C, region);
+ }
+}
+
/* ------------------ */
/* Called by menus to activate it, or by view2d operators
* to make sure 'related' views stay in synchrony */
void UI_view2d_sync(bScreen *screen, ScrArea *area, View2D *v2dcur, int flag)
{
- ARegion *region;
-
/* don't continue if no view syncing to be done */
if ((v2dcur->flag & (V2D_VIEWSYNC_SCREEN_TIME | V2D_VIEWSYNC_AREA_VERTICAL)) == 0) {
return;
@@ -868,7 +877,7 @@ void UI_view2d_sync(bScreen *screen, ScrArea *area, View2D *v2dcur, int flag)
/* check if doing within area syncing (i.e. channels/vertical) */
if ((v2dcur->flag & V2D_VIEWSYNC_AREA_VERTICAL) && (area)) {
- for (region = area->regionbase.first; region; region = region->next) {
+ LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
/* don't operate on self */
if (v2dcur != &region->v2d) {
/* only if view has vertical locks enabled */
@@ -894,7 +903,7 @@ void UI_view2d_sync(bScreen *screen, ScrArea *area, View2D *v2dcur, int flag)
/* check if doing whole screen syncing (i.e. time/horizontal) */
if ((v2dcur->flag & V2D_VIEWSYNC_SCREEN_TIME) && (screen)) {
LISTBASE_FOREACH (ScrArea *, area_iter, &screen->areabase) {
- for (region = area_iter->regionbase.first; region; region = region->next) {
+ LISTBASE_FOREACH (ARegion *, region, &area_iter->regionbase) {
/* don't operate on self */
if (v2dcur != &region->v2d) {
/* only if view has horizontal locks enabled */
@@ -1112,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;
@@ -1216,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();
@@ -1269,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);
@@ -1322,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);
@@ -1419,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). */
@@ -1901,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) {
@@ -2119,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) {
@@ -2150,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 d62058699d9..7234e279da8 100644
--- a/source/blender/editors/interface/view2d_ops.c
+++ b/source/blender/editors/interface/view2d_ops.c
@@ -185,8 +185,8 @@ static void view_pan_apply_ex(bContext *C, v2dViewPanData *vpd, float dx, float
v2d->cur.ymax += dy;
}
- /* validate that view is in valid configuration after this operation */
- UI_view2d_curRect_validate(v2d);
+ /* Inform v2d about changes after this operation. */
+ UI_view2d_curRect_changed(C, v2d);
/* don't rebuild full tree in outliner, since we're just changing our view */
ED_region_tag_redraw_no_rebuild(vpd->region);
@@ -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;
@@ -957,8 +957,8 @@ static void view_zoomstep_apply_ex(
}
}
- /* validate that view is in valid configuration after this operation */
- UI_view2d_curRect_validate(v2d);
+ /* Inform v2d about changes after this operation. */
+ UI_view2d_curRect_changed(C, v2d);
if (ED_region_snap_size_apply(region, snap_test)) {
ScrArea *area = CTX_wm_area(C);
@@ -1157,8 +1157,8 @@ static void view_zoomdrag_apply(bContext *C, wmOperator *op)
const bool zoom_to_pos = use_cursor_init && (U.uiflag & USER_ZOOM_TO_MOUSEPOS);
/* get amount to move view by */
- dx = RNA_float_get(op->ptr, "deltax") / U.pixelsize;
- dy = RNA_float_get(op->ptr, "deltay") / U.pixelsize;
+ dx = RNA_float_get(op->ptr, "deltax") / U.dpi_fac;
+ dy = RNA_float_get(op->ptr, "deltay") / U.dpi_fac;
if (U.uiflag & USER_ZOOM_INVERT) {
dx *= -1;
@@ -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;
@@ -1216,8 +1216,8 @@ static void view_zoomdrag_apply(bContext *C, wmOperator *op)
}
}
- /* validate that view is in valid configuration after this operation */
- UI_view2d_curRect_validate(v2d);
+ /* Inform v2d about changes after this operation. */
+ UI_view2d_curRect_changed(C, v2d);
if (ED_region_snap_size_apply(vzd->region, snap_test)) {
ScrArea *area = CTX_wm_area(C);
@@ -1806,7 +1806,7 @@ void UI_view2d_smooth_view(bContext *C, ARegion *region, const rctf *cur, const
if (ok == false) {
v2d->cur = sms.new_cur;
- UI_view2d_curRect_validate(v2d);
+ UI_view2d_curRect_changed(C, v2d);
ED_region_tag_redraw_no_rebuild(region);
UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY);
}
@@ -1853,7 +1853,7 @@ static int view2d_smoothview_invoke(bContext *C, wmOperator *UNUSED(op), const w
BLI_rctf_interp(&v2d->cur, &sms->orig_cur, &sms->new_cur, step);
}
- UI_view2d_curRect_validate(v2d);
+ UI_view2d_curRect_changed(C, v2d);
UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY);
ED_region_tag_redraw_no_rebuild(region);
@@ -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;
@@ -2176,8 +2176,8 @@ static void scroller_activate_apply(bContext *C, wmOperator *op)
break;
}
- /* validate that view is in valid configuration after this operation */
- UI_view2d_curRect_validate(v2d);
+ /* Inform v2d about changes after this operation. */
+ UI_view2d_curRect_changed(C, v2d);
/* request updates to be done... */
ED_region_tag_redraw_no_rebuild(vsm->region);
@@ -2410,8 +2410,8 @@ static int reset_exec(bContext *C, wmOperator *UNUSED(op))
}
}
- /* validate that view is in valid configuration after this operation */
- UI_view2d_curRect_validate(v2d);
+ /* Inform v2d about changes after this operation. */
+ UI_view2d_curRect_changed(C, v2d);
if (ED_region_snap_size_apply(region, snap_test)) {
ScrArea *area = CTX_wm_area(C);
diff --git a/source/blender/editors/io/io_alembic.c b/source/blender/editors/io/io_alembic.c
index 238ebffe153..22171d8be66 100644
--- a/source/blender/editors/io/io_alembic.c
+++ b/source/blender/editors/io/io_alembic.c
@@ -656,7 +656,7 @@ void WM_OT_alembic_import(wmOperatorType *ot)
WM_operator_properties_filesel(ot,
FILE_TYPE_FOLDER | FILE_TYPE_ALEMBIC,
FILE_BLENDER,
- FILE_SAVE,
+ FILE_OPENFILE,
WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH | WM_FILESEL_SHOW_PROPS,
FILE_DEFAULTDISPLAY,
FILE_SORT_ALPHA);
diff --git a/source/blender/editors/io/io_usd.c b/source/blender/editors/io/io_usd.c
index 096dc44c758..45ea52bdebc 100644
--- a/source/blender/editors/io/io_usd.c
+++ b/source/blender/editors/io/io_usd.c
@@ -113,6 +113,7 @@ static int wm_usd_export_exec(bContext *C, wmOperator *op)
MEM_SAFE_FREE(op->customdata);
const bool selected_objects_only = RNA_boolean_get(op->ptr, "selected_objects_only");
+ const bool visible_objects_only = RNA_boolean_get(op->ptr, "visible_objects_only");
const bool export_animation = RNA_boolean_get(op->ptr, "export_animation");
const bool export_hair = RNA_boolean_get(op->ptr, "export_hair");
const bool export_uvmaps = RNA_boolean_get(op->ptr, "export_uvmaps");
@@ -128,6 +129,7 @@ static int wm_usd_export_exec(bContext *C, wmOperator *op)
export_normals,
export_materials,
selected_objects_only,
+ visible_objects_only,
use_instancing,
evaluation_mode,
};
@@ -149,6 +151,7 @@ static void wm_usd_export_draw(bContext *UNUSED(C), wmOperator *op)
col = uiLayoutColumn(box, true);
uiItemR(col, ptr, "selected_objects_only", 0, NULL, ICON_NONE);
+ uiItemR(col, ptr, "visible_objects_only", 0, NULL, ICON_NONE);
col = uiLayoutColumn(box, true);
uiItemR(col, ptr, "export_animation", 0, NULL, ICON_NONE);
@@ -192,6 +195,13 @@ void WM_OT_usd_export(struct wmOperatorType *ot)
"exported as empty transform");
RNA_def_boolean(ot->srna,
+ "visible_objects_only",
+ true,
+ "Visible Only",
+ "Only visible objects are exported. Invisible parents of exported objects are "
+ "exported as empty transform");
+
+ RNA_def_boolean(ot->srna,
"export_animation",
false,
"Animation",
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 c617c921d70..8acbb328ab0 100644
--- a/source/blender/editors/mask/mask_draw.c
+++ b/source/blender/editors/mask/mask_draw.c
@@ -594,9 +594,7 @@ static void draw_mask_layers(const bContext *C,
const int width,
const int height)
{
- GPU_blend(true);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
GPU_program_point_size(true);
MaskLayer *mask_layer;
@@ -633,7 +631,7 @@ static void draw_mask_layers(const bContext *C,
}
GPU_program_point_size(false);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
void ED_mask_draw(const bContext *C, const char draw_flag, const char draw_type)
@@ -740,8 +738,7 @@ void ED_mask_draw_region(
if (overlay_mode != MASK_OVERLAY_ALPHACHANNEL) {
/* More blending types could be supported in the future. */
- GPU_blend(true);
- GPU_blend_set_func(GPU_DST_COLOR, GPU_ZERO);
+ GPU_blend(GPU_BLEND_MULTIPLY);
}
GPU_matrix_push();
@@ -753,12 +750,12 @@ 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();
if (overlay_mode != MASK_OVERLAY_ALPHACHANNEL) {
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
MEM_freeN(buffer);
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..1b5e374b2a7 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,14 @@ 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
+ if (RNA_enum_get(op->ptr, "solver") == ISECT_SOLVER_EXACT) {
+ BKE_report(op->reports, RPT_WARNING, "Compiled without GMP, using fast solver");
+ }
+ const bool exact = false;
+#endif
bool use_self;
bool has_isect;
@@ -186,19 +203,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 +243,34 @@ 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);
+
+ bool use_exact = RNA_enum_get(&ptr, "solver") == ISECT_SOLVER_EXACT;
+
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ row = uiLayoutRow(layout, false);
+ uiItemR(row, &ptr, "mode", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemS(layout);
+ row = uiLayoutRow(layout, false);
+ uiItemR(row, &ptr, "separate_mode", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemS(layout);
+
+ row = uiLayoutRow(layout, false);
+ uiItemR(row, &ptr, "solver", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemS(layout);
+
+ 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 +294,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 +308,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 +316,12 @@ 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);
+ RNA_def_enum(ot->srna,
+ "solver",
+ isect_intersect_solver_items,
+ ISECT_SOLVER_EXACT,
+ "Solver",
+ "Which Intersect solver to use");
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -280,6 +344,15 @@ 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
+ const bool use_exact = RNA_enum_get(op->ptr, "solver") == ISECT_SOLVER_EXACT;
+#else
+ if (RNA_enum_get(op->ptr, "solver") == ISECT_SOLVER_EXACT) {
+ BKE_report(op->reports, RPT_WARNING, "Compiled without GMP, using fast solver");
+ }
+ const 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,34 @@ 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);
+
+ bool use_exact = RNA_enum_get(&ptr, "solver") == ISECT_SOLVER_EXACT;
+
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+
+ row = uiLayoutRow(layout, false);
+ uiItemR(row, &ptr, "operation", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemS(layout);
+
+ row = uiLayoutRow(layout, false);
+ uiItemR(row, &ptr, "solver", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemS(layout);
+
+ uiItemR(layout, &ptr, "use_swap", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "use_self", 0, NULL, ICON_NONE);
+ 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[] = {
@@ -335,6 +442,12 @@ void MESH_OT_intersect_boolean(struct wmOperatorType *ot)
{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)";
ot->description = "Cut solid geometry from selected to unselected";
@@ -343,21 +456,29 @@ 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);
+ RNA_def_enum(ot->srna,
+ "solver",
+ isect_boolean_solver_items,
+ ISECT_SOLVER_EXACT,
+ "Solver",
+ "Which Boolean solver to use");
/* 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 6f4f75e802a..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);
@@ -1128,9 +1128,7 @@ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), v
int i, snapped_verts_count, other_verts_count;
float fcol[4];
- GPU_blend(true);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
GPUVertBuf *vert = GPU_vertbuf_create_with_format(format);
GPU_vertbuf_data_alloc(vert, kcd->totlinehit);
@@ -1147,16 +1145,13 @@ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), v
GPUBatch *batch = GPU_batch_create_ex(GPU_PRIM_POINTS, vert, NULL, GPU_BATCH_OWNS_VBO);
GPU_batch_program_set_builtin(batch, GPU_SHADER_3D_UNIFORM_COLOR);
- GPU_batch_bind(batch);
/* draw any snapped verts first */
rgba_uchar_to_float(fcol, kcd->colors.point_a);
GPU_batch_uniform_4fv(batch, "color", fcol);
- GPU_matrix_bind(batch->interface);
- GPU_shader_set_srgb_uniform(batch->interface);
GPU_point_size(11);
if (snapped_verts_count > 0) {
- GPU_batch_draw_advanced(batch, 0, snapped_verts_count, 0, 0);
+ GPU_batch_draw_range(batch, 0, snapped_verts_count);
}
/* now draw the rest */
@@ -1164,13 +1159,12 @@ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), v
GPU_batch_uniform_4fv(batch, "color", fcol);
GPU_point_size(7);
if (other_verts_count > 0) {
- GPU_batch_draw_advanced(batch, snapped_verts_count, other_verts_count, 0, 0);
+ GPU_batch_draw_range(batch, snapped_verts_count, other_verts_count);
}
- GPU_batch_program_use_end(batch);
GPU_batch_discard(batch);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
if (kcd->totkedge > 0) {
@@ -1228,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_mask_extract.c b/source/blender/editors/mesh/editmesh_mask_extract.c
index 8eeba5007e1..34fcee779de 100644
--- a/source/blender/editors/mesh/editmesh_mask_extract.c
+++ b/source/blender/editors/mesh/editmesh_mask_extract.c
@@ -354,10 +354,6 @@ static int paint_mask_slice_exec(bContext *C, wmOperator *op)
if (ob->mode == OB_MODE_SCULPT) {
ED_sculpt_undo_geometry_begin(ob, "mask slice");
- /* TODO: The ideal functionality would be to preserve the current face sets and add a new one
- * for the new triangles, but this data-layer needs to be rebuild in order to make sculpt mode
- * not crash when modifying the geometry. */
- CustomData_free_layers(&mesh->pdata, CD_SCULPT_FACE_SETS, mesh->totpoly);
}
BMesh *bm;
@@ -429,14 +425,14 @@ static int paint_mask_slice_exec(bContext *C, wmOperator *op)
BKE_mesh_calc_normals(ob->data);
if (ob->mode == OB_MODE_SCULPT) {
- ED_sculpt_undo_geometry_end(ob);
SculptSession *ss = ob->sculpt;
- /* Rebuild a new valid Face Set layer for the object. */
- ss->face_sets = CustomData_add_layer(
- &mesh->pdata, CD_SCULPT_FACE_SETS, CD_CALLOC, NULL, mesh->totpoly);
- for (int i = 0; i < mesh->totpoly; i++) {
- ss->face_sets[i] = 1;
+ ss->face_sets = CustomData_get_layer(&((Mesh *)ob->data)->pdata, CD_SCULPT_FACE_SETS);
+ if (ss->face_sets) {
+ /* Assign a new Face Set ID to the new faces created by the slice operation. */
+ const int next_face_set_id = ED_sculpt_face_sets_find_next_available_id(ob->data);
+ ED_sculpt_face_sets_initialize_none_to_id(ob->data, next_face_set_id);
}
+ ED_sculpt_undo_geometry_end(ob);
}
BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL);
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..52c109b3854 100644
--- a/source/blender/editors/mesh/editmesh_select.c
+++ b/source/blender/editors/mesh/editmesh_select.c
@@ -110,7 +110,7 @@ void EDBM_select_mirrored(BMEditMesh *em,
}
}
- EDBM_verts_mirror_cache_begin(em, axis, true, true, use_topology);
+ EDBM_verts_mirror_cache_begin(em, axis, true, true, false, use_topology);
if (!extend) {
EDBM_flag_disable_all(em, BM_ELEM_SELECT);
@@ -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_tools.c b/source/blender/editors/mesh/editmesh_tools.c
index 1e4144db47e..4e1a56b3b55 100644
--- a/source/blender/editors/mesh/editmesh_tools.c
+++ b/source/blender/editors/mesh/editmesh_tools.c
@@ -2513,7 +2513,7 @@ static int edbm_do_smooth_vertex_exec(bContext *C, wmOperator *op)
/* mirror before smooth */
if (((Mesh *)obedit->data)->editflag & ME_EDIT_MIRROR_X) {
- EDBM_verts_mirror_cache_begin(em, 0, false, true, use_topology);
+ EDBM_verts_mirror_cache_begin(em, 0, false, true, false, use_topology);
}
/* if there is a mirror modifier with clipping, flag the verts that
@@ -2658,7 +2658,7 @@ static int edbm_do_smooth_laplacian_vertex_exec(bContext *C, wmOperator *op)
/* Mirror before smooth. */
if (((Mesh *)obedit->data)->editflag & ME_EDIT_MIRROR_X) {
- EDBM_verts_mirror_cache_begin(em, 0, false, true, use_topology);
+ EDBM_verts_mirror_cache_begin(em, 0, false, true, false, use_topology);
}
bool failed_repeat_loop = false;
@@ -7600,7 +7600,7 @@ static int mesh_symmetry_snap_exec(bContext *C, wmOperator *op)
BMVert *v;
int i;
- EDBM_verts_mirror_cache_begin_ex(em, axis, true, true, use_topology, thresh, index);
+ EDBM_verts_mirror_cache_begin_ex(em, axis, true, true, false, use_topology, thresh, index);
BM_mesh_elem_table_ensure(bm, BM_VERT);
diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c
index 46c63d2e057..a90c8dea87b 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)
{
@@ -1059,6 +1059,7 @@ static BMVert *cache_mirr_intptr_as_bmvert(const intptr_t *index_lookup, int ind
* \param em: Editmesh.
* \param use_self: Allow a vertex to point to its self (middle verts).
* \param use_select: Restrict to selected verts.
+ * \param respecthide: Skip hidden vertices.
* \param use_topology: Use topology mirror.
* \param maxdist: Distance for close point test.
* \param r_index: Optional array to write into, as an alternative to a customdata layer
@@ -1068,6 +1069,7 @@ void EDBM_verts_mirror_cache_begin_ex(BMEditMesh *em,
const int axis,
const bool use_self,
const bool use_select,
+ const bool respecthide,
/* extra args */
const bool use_topology,
float maxdist,
@@ -1110,6 +1112,10 @@ void EDBM_verts_mirror_cache_begin_ex(BMEditMesh *em,
else {
tree = BLI_kdtree_3d_new(bm->totvert);
BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
+ if (respecthide && BM_elem_flag_test(v, BM_ELEM_HIDDEN)) {
+ continue;
+ }
+
BLI_kdtree_3d_insert(tree, i, v->co);
}
BLI_kdtree_3d_balance(tree);
@@ -1118,44 +1124,45 @@ void EDBM_verts_mirror_cache_begin_ex(BMEditMesh *em,
#define VERT_INTPTR(_v, _i) (r_index ? &r_index[_i] : BM_ELEM_CD_GET_VOID_P(_v, cd_vmirr_offset))
BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
- BLI_assert(BM_elem_index_get(v) == i);
+ if (respecthide && BM_elem_flag_test(v, BM_ELEM_HIDDEN)) {
+ continue;
+ }
- /* temporary for testing, check for selection */
if (use_select && !BM_elem_flag_test(v, BM_ELEM_SELECT)) {
- /* do nothing */
+ continue;
}
- else {
- BMVert *v_mirr;
- int *idx = VERT_INTPTR(v, i);
- if (use_topology) {
- v_mirr = cache_mirr_intptr_as_bmvert(mesh_topo_store.index_lookup, i);
- }
- else {
- int i_mirr;
- float co[3];
- copy_v3_v3(co, v->co);
- co[axis] *= -1.0f;
-
- v_mirr = NULL;
- i_mirr = BLI_kdtree_3d_find_nearest(tree, co, NULL);
- if (i_mirr != -1) {
- BMVert *v_test = BM_vert_at_index(bm, i_mirr);
- if (len_squared_v3v3(co, v_test->co) < maxdist_sq) {
- v_mirr = v_test;
- }
+ BLI_assert(BM_elem_index_get(v) == i);
+ BMVert *v_mirr;
+ int *idx = VERT_INTPTR(v, i);
+
+ if (use_topology) {
+ v_mirr = cache_mirr_intptr_as_bmvert(mesh_topo_store.index_lookup, i);
+ }
+ else {
+ int i_mirr;
+ float co[3];
+ copy_v3_v3(co, v->co);
+ co[axis] *= -1.0f;
+
+ v_mirr = NULL;
+ i_mirr = BLI_kdtree_3d_find_nearest(tree, co, NULL);
+ if (i_mirr != -1) {
+ BMVert *v_test = BM_vert_at_index(bm, i_mirr);
+ if (len_squared_v3v3(co, v_test->co) < maxdist_sq) {
+ v_mirr = v_test;
}
}
+ }
- if (v_mirr && (use_self || (v_mirr != v))) {
- const int i_mirr = BM_elem_index_get(v_mirr);
- *idx = i_mirr;
- idx = VERT_INTPTR(v_mirr, i_mirr);
- *idx = i;
- }
- else {
- *idx = -1;
- }
+ if (v_mirr && (use_self || (v_mirr != v))) {
+ const int i_mirr = BM_elem_index_get(v_mirr);
+ *idx = i_mirr;
+ idx = VERT_INTPTR(v_mirr, i_mirr);
+ *idx = i;
+ }
+ else {
+ *idx = -1;
}
}
@@ -1173,12 +1180,14 @@ void EDBM_verts_mirror_cache_begin(BMEditMesh *em,
const int axis,
const bool use_self,
const bool use_select,
+ const bool respecthide,
const bool use_topology)
{
EDBM_verts_mirror_cache_begin_ex(em,
axis,
use_self,
use_select,
+ respecthide,
/* extra args */
use_topology,
BM_SEARCH_MAXDIST_MIRR,
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 81b8cd70353..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;
}
@@ -2647,7 +2648,7 @@ static int object_convert_exec(bContext *C, wmOperator *op)
ob_gpencil = ED_gpencil_add_object(C, ob->loc, local_view_bits);
copy_v3_v3(ob_gpencil->rot, ob->rot);
copy_v3_v3(ob_gpencil->scale, ob->scale);
- BKE_gpencil_convert_curve(bmain, scene, ob_gpencil, ob, false, false, true);
+ BKE_gpencil_convert_curve(bmain, scene, ob_gpencil, ob, false, 1.0f, 0.0f);
gpencilConverted = true;
}
}
@@ -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_bake_api.c b/source/blender/editors/object/object_bake_api.c
index cb92fab3cb0..4d55aff1d1f 100644
--- a/source/blender/editors/object/object_bake_api.c
+++ b/source/blender/editors/object/object_bake_api.c
@@ -745,7 +745,7 @@ static int bake(Render *re,
/* We build a depsgraph for the baking,
* so we don't need to change the original data to adjust visibility and modifiers. */
Depsgraph *depsgraph = DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_RENDER);
- DEG_graph_build_from_view_layer(depsgraph, bmain, scene, view_layer);
+ DEG_graph_build_from_view_layer(depsgraph);
int op_result = OPERATOR_CANCELLED;
bool ok = false;
@@ -1596,9 +1596,8 @@ static void bake_set_props(wmOperator *op, Scene *scene)
prop = RNA_struct_find_property(op->ptr, "cage_object");
if (!RNA_property_is_set(op->ptr, prop)) {
- if (bake->cage_object) {
- RNA_property_string_set(op->ptr, prop, bake->cage_object->id.name + 2);
- }
+ RNA_property_string_set(
+ op->ptr, prop, (bake->cage_object) ? bake->cage_object->id.name + 2 : "");
}
prop = RNA_struct_find_property(op->ptr, "normal_space");
diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c
index bcb1b8afbdd..70404af6433 100644
--- a/source/blender/editors/object/object_constraint.c
+++ b/source/blender/editors/object/object_constraint.c
@@ -663,10 +663,13 @@ static const EnumPropertyItem constraint_owner_items[] = {
{0, NULL, 0, NULL, NULL},
};
-static bool edit_constraint_poll_generic(bContext *C, StructRNA *rna_type)
+static bool edit_constraint_poll_generic(bContext *C,
+ StructRNA *rna_type,
+ const bool is_liboverride_allowed)
{
PointerRNA ptr = CTX_data_pointer_get_type(C, "constraint", rna_type);
Object *ob = (ptr.owner_id) ? (Object *)ptr.owner_id : ED_object_active_context(C);
+ bConstraint *con = ptr.data;
if (!ob) {
CTX_wm_operator_poll_msg_set(C, "Context missing active object");
@@ -678,9 +681,11 @@ static bool edit_constraint_poll_generic(bContext *C, StructRNA *rna_type)
return false;
}
- if (ID_IS_OVERRIDE_LIBRARY(ob) && ptr.data != NULL) {
- CTX_wm_operator_poll_msg_set(C, "Cannot edit constraints coming from library override");
- return (((bConstraint *)ptr.data)->flag & CONSTRAINT_OVERRIDE_LIBRARY_LOCAL) != 0;
+ if (ID_IS_OVERRIDE_LIBRARY(ob) && !is_liboverride_allowed) {
+ if ((con == NULL) || (con->flag & CONSTRAINT_OVERRIDE_LIBRARY_LOCAL) != 0) {
+ CTX_wm_operator_poll_msg_set(C, "Cannot edit constraints coming from library override");
+ return false;
+ }
}
return true;
@@ -688,7 +693,14 @@ static bool edit_constraint_poll_generic(bContext *C, StructRNA *rna_type)
static bool edit_constraint_poll(bContext *C)
{
- return edit_constraint_poll_generic(C, &RNA_Constraint);
+ return edit_constraint_poll_generic(C, &RNA_Constraint, false);
+}
+
+/* Used by operators performing actions allowed also on constraints from the overridden linked
+ * object (not only from added 'local' ones). */
+static bool edit_constraint_liboverride_allowed_poll(bContext *C)
+{
+ return edit_constraint_poll_generic(C, &RNA_Constraint, true);
}
static void edit_constraint_properties(wmOperatorType *ot)
@@ -864,7 +876,7 @@ void CONSTRAINT_OT_stretchto_reset(wmOperatorType *ot)
/* callbacks */
ot->invoke = stretchto_reset_invoke;
ot->exec = stretchto_reset_exec;
- ot->poll = edit_constraint_poll;
+ ot->poll = edit_constraint_liboverride_allowed_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -919,7 +931,7 @@ void CONSTRAINT_OT_limitdistance_reset(wmOperatorType *ot)
/* callbacks */
ot->invoke = limitdistance_reset_invoke;
ot->exec = limitdistance_reset_exec;
- ot->poll = edit_constraint_poll;
+ ot->poll = edit_constraint_liboverride_allowed_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -997,7 +1009,7 @@ void CONSTRAINT_OT_childof_set_inverse(wmOperatorType *ot)
/* callbacks */
ot->invoke = childof_set_inverse_invoke;
ot->exec = childof_set_inverse_exec;
- ot->poll = edit_constraint_poll;
+ ot->poll = edit_constraint_liboverride_allowed_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -1046,7 +1058,7 @@ void CONSTRAINT_OT_childof_clear_inverse(wmOperatorType *ot)
/* callbacks */
ot->invoke = childof_clear_inverse_invoke;
ot->exec = childof_clear_inverse_exec;
- ot->poll = edit_constraint_poll;
+ ot->poll = edit_constraint_liboverride_allowed_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -1693,8 +1705,8 @@ void POSE_OT_constraints_clear(wmOperatorType *ot)
/* callbacks */
ot->exec = pose_constraints_clear_exec;
- ot->poll =
- ED_operator_posemode_exclusive; // XXX - do we want to ensure there are selected bones too?
+ ot->poll = ED_operator_posemode_exclusive; // XXX - do we want to ensure there are selected
+ // bones too?
}
static int object_constraints_clear_exec(bContext *C, wmOperator *UNUSED(op))
@@ -1942,7 +1954,8 @@ static bool get_new_constraint_target(
/* perform some special operations on the target */
if (only_curve) {
- /* Curve-Path option must be enabled for follow-path constraints to be able to work */
+ /* Curve-Path option must be enabled for follow-path constraints to be able to work
+ */
Curve *cu = (Curve *)ob->data;
cu->flag |= CU_PATH;
}
@@ -2214,8 +2227,8 @@ void OBJECT_OT_constraint_add_with_targets(wmOperatorType *ot)
/* identifiers */
ot->name = "Add Constraint (with Targets)";
ot->description =
- "Add a constraint to the active object, with target (where applicable) set to the selected "
- "Objects/Bones";
+ "Add a constraint to the active object, with target (where applicable) set to the "
+ "selected Objects/Bones";
ot->idname = "OBJECT_OT_constraint_add_with_targets";
/* api callbacks */
diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c
index 04113f70e52..966aeed75ab 100644
--- a/source/blender/editors/object/object_edit.c
+++ b/source/blender/editors/object/object_edit.c
@@ -145,6 +145,72 @@ Object *ED_object_active_context(const bContext *C)
return ob;
}
+/**
+ * Return an array of objects:
+ * - When in the property space, return the pinned or active object.
+ * - When in edit-mode/pose-mode, return an array of objects in the mode.
+ * - Otherwise return selected objects,
+ * the callers \a filter_fn needs to check of they are editable
+ * (assuming they need to be modified).
+ */
+Object **ED_object_array_in_mode_or_selected(bContext *C,
+ bool (*filter_fn)(Object *ob, void *user_data),
+ void *filter_user_data,
+ uint *r_objects_len)
+{
+ ScrArea *area = CTX_wm_area(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ Object *ob_active = OBACT(view_layer);
+ Object **objects;
+
+ Object *ob = NULL;
+ bool use_ob = true;
+ if (area && (area->spacetype == SPACE_PROPERTIES)) {
+ /* May return pinned object. */
+ ob = ED_object_context(C);
+ }
+ else if (ob_active && (ob_active->mode &
+ (OB_MODE_ALL_PAINT | OB_MODE_ALL_SCULPT | OB_MODE_ALL_PAINT_GPENCIL))) {
+ /* When painting, limit to active. */
+ ob = ob_active;
+ }
+ else {
+ /* Otherwise use full selection. */
+ use_ob = false;
+ }
+
+ if (use_ob) {
+ if ((ob != NULL) && !filter_fn(ob, filter_user_data)) {
+ ob = NULL;
+ }
+ *r_objects_len = (ob != NULL) ? 1 : 0;
+ objects = MEM_mallocN(sizeof(*objects) * *r_objects_len, __func__);
+ if (ob != NULL) {
+ objects[0] = ob;
+ }
+ }
+ else {
+ const View3D *v3d = (area && area->spacetype == SPACE_VIEW3D) ? area->spacedata.first : NULL;
+ /* When in a mode that supports multiple active objects, use "objects in mode"
+ * instead of the object's selection. */
+ if ((ob_active != NULL) && (ob_active->mode & (OB_MODE_EDIT | OB_MODE_POSE))) {
+ objects = BKE_view_layer_array_from_objects_in_mode(
+ view_layer,
+ v3d,
+ r_objects_len,
+ {.no_dup_data = true, .filter_fn = filter_fn, .filter_userdata = filter_user_data});
+ }
+ else {
+ objects = BKE_view_layer_array_selected_objects(
+ view_layer,
+ v3d,
+ r_objects_len,
+ {.no_dup_data = true, .filter_fn = filter_fn, .filter_userdata = filter_user_data});
+ }
+ }
+ return objects;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/editors/object/object_facemap_ops.c b/source/blender/editors/object/object_facemap_ops.c
index 6e0376358bb..32668598df1 100644
--- a/source/blender/editors/object/object_facemap_ops.c
+++ b/source/blender/editors/object/object_facemap_ops.c
@@ -26,6 +26,7 @@
#include "MEM_guardedalloc.h"
#include "BLI_listbase.h"
+#include "BLI_math.h"
#include "BLI_utildefines.h"
#include "DNA_mesh_types.h"
@@ -99,71 +100,61 @@ void ED_object_facemap_face_remove(Object *ob, bFaceMap *fmap, int facenum)
}
}
-static void object_fmap_swap_edit_mode(Object *ob, int num1, int num2)
+static void object_fmap_remap_edit_mode(Object *ob, const int *remap)
{
- if (ob->type == OB_MESH) {
- Mesh *me = ob->data;
+ if (ob->type != OB_MESH) {
+ return;
+ }
+
+ Mesh *me = ob->data;
+ if (me->edit_mesh) {
+ BMEditMesh *em = me->edit_mesh;
+ const int cd_fmap_offset = CustomData_get_offset(&em->bm->pdata, CD_FACEMAP);
- if (me->edit_mesh) {
- BMEditMesh *em = me->edit_mesh;
- const int cd_fmap_offset = CustomData_get_offset(&em->bm->pdata, CD_FACEMAP);
-
- if (cd_fmap_offset != -1) {
- BMFace *efa;
- BMIter iter;
- int *map;
-
- BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
- map = BM_ELEM_CD_GET_VOID_P(efa, cd_fmap_offset);
-
- if (map) {
- if (num1 != -1) {
- if (*map == num1) {
- *map = num2;
- }
- else if (*map == num2) {
- *map = num1;
- }
- }
- }
+ if (cd_fmap_offset != -1) {
+ BMFace *efa;
+ BMIter iter;
+ int *map;
+
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ map = BM_ELEM_CD_GET_VOID_P(efa, cd_fmap_offset);
+
+ if (map && *map != -1) {
+ *map = remap[*map];
}
}
}
}
}
-static void object_fmap_swap_object_mode(Object *ob, int num1, int num2)
+static void object_fmap_remap_object_mode(Object *ob, const int *remap)
{
- if (ob->type == OB_MESH) {
- Mesh *me = ob->data;
+ if (ob->type != OB_MESH) {
+ return;
+ }
- if (CustomData_has_layer(&me->pdata, CD_FACEMAP)) {
- int *map = CustomData_get_layer(&me->pdata, CD_FACEMAP);
- int i;
-
- if (map) {
- for (i = 0; i < me->totpoly; i++) {
- if (num1 != -1) {
- if (map[i] == num1) {
- map[i] = num2;
- }
- else if (map[i] == num2) {
- map[i] = num1;
- }
- }
+ Mesh *me = ob->data;
+ if (CustomData_has_layer(&me->pdata, CD_FACEMAP)) {
+ int *map = CustomData_get_layer(&me->pdata, CD_FACEMAP);
+ int i;
+
+ if (map) {
+ for (i = 0; i < me->totpoly; i++) {
+ if (map[i] != -1) {
+ map[i] = remap[map[i]];
}
}
}
}
}
-static void object_facemap_swap(Object *ob, int num1, int num2)
+static void object_facemap_remap(Object *ob, const int *remap)
{
if (BKE_object_is_in_editmode(ob)) {
- object_fmap_swap_edit_mode(ob, num1, num2);
+ object_fmap_remap_edit_mode(ob, remap);
}
else {
- object_fmap_swap_object_mode(ob, num1, num2);
+ object_fmap_remap_object_mode(ob, remap);
}
}
@@ -432,45 +423,46 @@ static int face_map_move_exec(bContext *C, wmOperator *op)
Object *ob = ED_object_context(C);
bFaceMap *fmap;
int dir = RNA_enum_get(op->ptr, "direction");
- int pos1, pos2 = -1, count;
fmap = BLI_findlink(&ob->fmaps, ob->actfmap - 1);
if (!fmap) {
return OPERATOR_CANCELLED;
}
- count = BLI_listbase_count(&ob->fmaps);
- pos1 = BLI_findindex(&ob->fmaps, fmap);
+ if (!fmap->prev && !fmap->next) {
+ return OPERATOR_CANCELLED;
+ }
- if (dir == 1) { /*up*/
- void *prev = fmap->prev;
+ int pos1 = BLI_findindex(&ob->fmaps, fmap);
+ int pos2 = pos1 - dir;
+ int len = BLI_listbase_count(&ob->fmaps);
+ int *map = MEM_mallocN(len * sizeof(*map), __func__);
- if (prev) {
- pos2 = pos1 - 1;
- }
- else {
- pos2 = count - 1;
+ if (!IN_RANGE(pos2, -1, len)) {
+ const int offset = len - dir;
+ for (int i = 0; i < len; i++) {
+ map[i] = (i + offset) % len;
}
+ pos2 = map[pos1];
+ }
+ else {
+ range_vn_i(map, len, 0);
+ SWAP(int, map[pos1], map[pos2]);
+ }
- BLI_remlink(&ob->fmaps, fmap);
+ void *prev = fmap->prev;
+ void *next = fmap->next;
+ BLI_remlink(&ob->fmaps, fmap);
+ if (dir == 1) { /*up*/
BLI_insertlinkbefore(&ob->fmaps, prev, fmap);
}
else { /*down*/
- void *next = fmap->next;
-
- if (next) {
- pos2 = pos1 + 1;
- }
- else {
- pos2 = 0;
- }
-
- BLI_remlink(&ob->fmaps, fmap);
BLI_insertlinkafter(&ob->fmaps, next, fmap);
}
- /* iterate through mesh and substitute the indices as necessary */
- object_facemap_swap(ob, pos2, pos1);
+ /* Iterate through mesh and substitute the indices as necessary. */
+ object_facemap_remap(ob, map);
+ MEM_freeN(map);
ob->actfmap = pos2 + 1;
diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c
index ceb6553bdf6..14882ab8ffc 100644
--- a/source/blender/editors/object/object_modifier.c
+++ b/source/blender/editors/object/object_modifier.c
@@ -107,7 +107,7 @@ static void object_force_modifier_update_for_bind(Depsgraph *depsgraph, Object *
Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob);
BKE_object_eval_reset(ob_eval);
if (ob->type == OB_MESH) {
- Mesh *me_eval = mesh_create_eval_final_view(depsgraph, scene_eval, ob_eval, &CD_MASK_BAREMESH);
+ Mesh *me_eval = mesh_create_eval_final(depsgraph, scene_eval, ob_eval, &CD_MASK_BAREMESH);
BKE_mesh_eval_delete(me_eval);
}
else if (ob->type == OB_LATTICE) {
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 8d8f01dd61a..f14c734da4e 100644
--- a/source/blender/editors/object/object_remesh.c
+++ b/source/blender/editors/object/object_remesh.c
@@ -294,7 +294,7 @@ static void voxel_size_edit_draw(const bContext *UNUSED(C), ARegion *UNUSED(ar),
{
VoxelSizeEditCustomData *cd = arg;
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
GPU_line_smooth(true);
uint pos3d = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
@@ -363,7 +363,7 @@ static void voxel_size_edit_draw(const bContext *UNUSED(C), ARegion *UNUSED(ar),
GPU_matrix_pop();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
GPU_line_smooth(false);
}
diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c
index 253287c382e..de9cca38a6e 100644
--- a/source/blender/editors/object/object_vgroup.c
+++ b/source/blender/editors/object/object_vgroup.c
@@ -72,15 +72,32 @@
#include "ED_mesh.h"
#include "ED_object.h"
+#include "ED_screen.h"
#include "UI_resources.h"
#include "object_intern.h"
+static bool vertex_group_supported_poll_ex(bContext *C, const Object *ob);
+
/* -------------------------------------------------------------------- */
-/** \name Public Utility Functions
+/** \name Local Utility Functions
* \{ */
+static bool object_array_for_wpaint_filter(Object *ob, void *user_data)
+{
+ bContext *C = user_data;
+ if (vertex_group_supported_poll_ex(C, ob)) {
+ return true;
+ }
+ return false;
+}
+
+static Object **object_array_for_wpaint(bContext *C, uint *r_objects_len)
+{
+ return ED_object_array_in_mode_or_selected(C, object_array_for_wpaint_filter, C, r_objects_len);
+}
+
static bool vertex_group_use_vert_sel(Object *ob)
{
if (ob->mode == OB_MODE_EDIT) {
@@ -100,6 +117,12 @@ static Lattice *vgroup_edit_lattice(Object *ob)
return (lt->editlatt) ? lt->editlatt->latt : lt;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Public Utility Functions
+ * \{ */
+
bool ED_vgroup_sync_from_pose(Object *ob)
{
Object *armobj = BKE_object_pose_armature_get(ob);
@@ -2407,7 +2430,7 @@ void ED_vgroup_mirror(Object *ob,
goto cleanup;
}
- EDBM_verts_mirror_cache_begin(em, 0, true, false, use_topology);
+ EDBM_verts_mirror_cache_begin(em, 0, true, false, false, use_topology);
BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false);
@@ -2659,14 +2682,21 @@ static void vgroup_assign_verts(Object *ob, const float weight)
/** \name Shared Operator Poll Functions
* \{ */
+static bool vertex_group_supported_poll_ex(bContext *C, const Object *ob)
+{
+ if (!ED_operator_object_active_local_editable_ex(C, ob)) {
+ return false;
+ }
+ const ID *data = ob->data;
+ return (OB_TYPE_SUPPORT_VGROUP(ob->type) &&
+ /* Data checks. */
+ (data != NULL) && !ID_IS_LINKED(data) && !ID_IS_OVERRIDE_LIBRARY(data));
+}
+
static bool vertex_group_supported_poll(bContext *C)
{
Object *ob = ED_object_context(C);
- ID *data = (ob) ? ob->data : NULL;
-
- return (ob && !ID_IS_LINKED(ob) && OB_TYPE_SUPPORT_VGROUP(ob->type) &&
- !ID_IS_OVERRIDE_LIBRARY(ob) && data && !ID_IS_LINKED(data) &&
- !ID_IS_OVERRIDE_LIBRARY(data));
+ return vertex_group_supported_poll_ex(C, ob);
}
static bool vertex_group_poll(bContext *C)
@@ -3077,6 +3107,12 @@ void OBJECT_OT_vertex_group_deselect(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex Group Copy Operator
+ * \{ */
+
static int vertex_group_copy_exec(bContext *C, wmOperator *UNUSED(op))
{
Object *ob = ED_object_context(C);
@@ -3090,12 +3126,6 @@ static int vertex_group_copy_exec(bContext *C, wmOperator *UNUSED(op))
return OPERATOR_FINISHED;
}
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Vertex Group Copy Operator
- * \{ */
-
void OBJECT_OT_vertex_group_copy(wmOperatorType *ot)
{
/* identifiers */
@@ -3111,6 +3141,12 @@ void OBJECT_OT_vertex_group_copy(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex Group Levels Operator
+ * \{ */
+
static int vertex_group_levels_exec(bContext *C, wmOperator *op)
{
Object *ob = ED_object_context(C);
@@ -3133,12 +3169,6 @@ static int vertex_group_levels_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Vertex Group Levels Operator
- * \{ */
-
void OBJECT_OT_vertex_group_levels(wmOperatorType *ot)
{
/* identifiers */
@@ -3161,6 +3191,12 @@ void OBJECT_OT_vertex_group_levels(wmOperatorType *ot)
ot->srna, "gain", 1.f, 0.f, FLT_MAX, "Gain", "Value to multiply weights by", 0.0f, 10.f);
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex Group Normalize Operator
+ * \{ */
+
static int vertex_group_normalize_exec(bContext *C, wmOperator *UNUSED(op))
{
Object *ob = ED_object_context(C);
@@ -3178,12 +3214,6 @@ static int vertex_group_normalize_exec(bContext *C, wmOperator *UNUSED(op))
return OPERATOR_CANCELLED;
}
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Vertex Group Normalize Operator
- * \{ */
-
void OBJECT_OT_vertex_group_normalize(wmOperatorType *ot)
{
/* identifiers */
@@ -3200,6 +3230,12 @@ void OBJECT_OT_vertex_group_normalize(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex Group Normalize All Operator
+ * \{ */
+
static int vertex_group_normalize_all_exec(bContext *C, wmOperator *op)
{
Object *ob = ED_object_context(C);
@@ -3226,12 +3262,6 @@ static int vertex_group_normalize_all_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Vertex Group Normalize All Operator
- * \{ */
-
void OBJECT_OT_vertex_group_normalize_all(wmOperatorType *ot)
{
/* identifiers */
@@ -3512,22 +3542,11 @@ static int vertex_group_smooth_exec(bContext *C, wmOperator *op)
{
const float fac = RNA_float_get(op->ptr, "factor");
const int repeat = RNA_int_get(op->ptr, "repeat");
- eVGroupSelect subset_type = RNA_enum_get(op->ptr, "group_select_mode");
+ const eVGroupSelect subset_type = RNA_enum_get(op->ptr, "group_select_mode");
const float fac_expand = RNA_float_get(op->ptr, "expand");
- ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob_ctx = ED_object_context(C);
uint objects_len;
- Object **objects;
- if (ob_ctx->mode == OB_MODE_WEIGHT_PAINT) {
- /* Until weight paint supports multi-edit, use only the active. */
- objects_len = 1;
- objects = &ob_ctx;
- }
- else {
- objects = BKE_view_layer_array_from_objects_in_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len, ob_ctx->mode);
- }
+ Object **objects = object_array_for_wpaint(C, &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = objects[ob_index];
@@ -3544,9 +3563,7 @@ static int vertex_group_smooth_exec(bContext *C, wmOperator *op)
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
}
- if (objects != &ob_ctx) {
- MEM_freeN(objects);
- }
+ MEM_freeN(objects);
return OPERATOR_FINISHED;
}
@@ -3588,22 +3605,29 @@ void OBJECT_OT_vertex_group_smooth(wmOperatorType *ot)
static int vertex_group_clean_exec(bContext *C, wmOperator *op)
{
- Object *ob = ED_object_context(C);
+ const float limit = RNA_float_get(op->ptr, "limit");
+ const bool keep_single = RNA_boolean_get(op->ptr, "keep_single");
+ const eVGroupSelect subset_type = RNA_enum_get(op->ptr, "group_select_mode");
- float limit = RNA_float_get(op->ptr, "limit");
- bool keep_single = RNA_boolean_get(op->ptr, "keep_single");
- eVGroupSelect subset_type = RNA_enum_get(op->ptr, "group_select_mode");
+ uint objects_len;
+ Object **objects = object_array_for_wpaint(C, &objects_len);
- int subset_count, vgroup_tot;
+ for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
+ Object *ob = objects[ob_index];
- const bool *vgroup_validmap = BKE_object_defgroup_subset_from_select_type(
- ob, subset_type, &vgroup_tot, &subset_count);
- vgroup_clean_subset(ob, vgroup_validmap, vgroup_tot, subset_count, limit, keep_single);
- MEM_freeN((void *)vgroup_validmap);
+ int subset_count, vgroup_tot;
- DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
- WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
+ const bool *vgroup_validmap = BKE_object_defgroup_subset_from_select_type(
+ ob, subset_type, &vgroup_tot, &subset_count);
+
+ vgroup_clean_subset(ob, vgroup_validmap, vgroup_tot, subset_count, limit, keep_single);
+ MEM_freeN((void *)vgroup_validmap);
+
+ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
+ WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
+ }
+ MEM_freeN(objects);
return OPERATOR_FINISHED;
}
@@ -3611,7 +3635,7 @@ static int vertex_group_clean_exec(bContext *C, wmOperator *op)
void OBJECT_OT_vertex_group_clean(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Clean Vertex Group";
+ ot->name = "Clean Vertex Group Weights";
ot->idname = "OBJECT_OT_vertex_group_clean";
ot->description = "Remove vertex group assignments which are not required";
@@ -3692,25 +3716,36 @@ void OBJECT_OT_vertex_group_quantize(wmOperatorType *ot)
static int vertex_group_limit_total_exec(bContext *C, wmOperator *op)
{
- Object *ob = ED_object_context(C);
-
const int limit = RNA_int_get(op->ptr, "limit");
- eVGroupSelect subset_type = RNA_enum_get(op->ptr, "group_select_mode");
+ const eVGroupSelect subset_type = RNA_enum_get(op->ptr, "group_select_mode");
+ int remove_multi_count = 0;
- int subset_count, vgroup_tot;
+ uint objects_len;
+ Object **objects = object_array_for_wpaint(C, &objects_len);
+ for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
+ Object *ob = objects[ob_index];
- const bool *vgroup_validmap = BKE_object_defgroup_subset_from_select_type(
- ob, subset_type, &vgroup_tot, &subset_count);
- int remove_tot = vgroup_limit_total_subset(ob, vgroup_validmap, vgroup_tot, subset_count, limit);
- MEM_freeN((void *)vgroup_validmap);
+ int subset_count, vgroup_tot;
+ const bool *vgroup_validmap = BKE_object_defgroup_subset_from_select_type(
+ ob, subset_type, &vgroup_tot, &subset_count);
+ const int remove_count = vgroup_limit_total_subset(
+ ob, vgroup_validmap, vgroup_tot, subset_count, limit);
+ MEM_freeN((void *)vgroup_validmap);
- BKE_reportf(
- op->reports, remove_tot ? RPT_INFO : RPT_WARNING, "%d vertex weights limited", remove_tot);
+ if (remove_count != 0) {
+ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
+ WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
+ }
+ remove_multi_count += remove_count;
+ }
+ MEM_freeN(objects);
- if (remove_tot) {
- DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
- WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
+ if (remove_multi_count) {
+ BKE_reportf(op->reports,
+ remove_multi_count ? RPT_INFO : RPT_WARNING,
+ "%d vertex weights limited",
+ remove_multi_count);
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c
index 488bb7121a2..eb7ddfefb9c 100644
--- a/source/blender/editors/physics/particle_edit.c
+++ b/source/blender/editors/physics/particle_edit.c
@@ -3213,11 +3213,11 @@ static void brush_drawcursor(bContext *C, int x, int y, void *UNUSED(customdata)
immUniformColor4ub(255, 255, 255, 128);
GPU_line_smooth(true);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
imm_draw_circle_wire_2d(pos, (float)x, (float)y, pe_brush_size_get(scene, brush), 40);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
GPU_line_smooth(false);
immUnbindProgram();
diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c
index f75dd428968..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);
@@ -879,7 +879,6 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op)
static void screen_opengl_render_end(bContext *C, OGLRender *oglrender)
{
- Main *bmain = CTX_data_main(C);
Scene *scene = oglrender->scene;
int i;
@@ -929,7 +928,7 @@ static void screen_opengl_render_end(bContext *C, OGLRender *oglrender)
if (oglrender->timer) { /* exec will not have a timer */
Depsgraph *depsgraph = oglrender->depsgraph;
scene->r.cfra = oglrender->cfrao;
- BKE_scene_graph_update_for_newframe(depsgraph, bmain);
+ BKE_scene_graph_update_for_newframe(depsgraph);
WM_event_remove_timer(oglrender->wm, oglrender->win, oglrender->timer);
}
@@ -1119,7 +1118,6 @@ static bool schedule_write_result(OGLRender *oglrender, RenderResult *rr)
static bool screen_opengl_render_anim_step(bContext *C, wmOperator *op)
{
- Main *bmain = CTX_data_main(C);
OGLRender *oglrender = op->customdata;
Scene *scene = oglrender->scene;
Depsgraph *depsgraph = oglrender->depsgraph;
@@ -1134,7 +1132,7 @@ static bool screen_opengl_render_anim_step(bContext *C, wmOperator *op)
CFRA++;
}
while (CFRA < oglrender->nfra) {
- BKE_scene_graph_update_for_newframe(depsgraph, bmain);
+ BKE_scene_graph_update_for_newframe(depsgraph);
CFRA++;
}
@@ -1161,7 +1159,7 @@ static bool screen_opengl_render_anim_step(bContext *C, wmOperator *op)
WM_cursor_time(oglrender->win, scene->r.cfra);
- BKE_scene_graph_update_for_newframe(depsgraph, bmain);
+ BKE_scene_graph_update_for_newframe(depsgraph);
if (view_context) {
if (oglrender->rv3d->persp == RV3D_CAMOB && oglrender->v3d->camera &&
diff --git a/source/blender/editors/render/render_shading.c b/source/blender/editors/render/render_shading.c
index 711f89b9fda..2b52ae117fc 100644
--- a/source/blender/editors/render/render_shading.c
+++ b/source/blender/editors/render/render_shading.c
@@ -56,6 +56,7 @@
#include "BKE_linestyle.h"
#include "BKE_main.h"
#include "BKE_material.h"
+#include "BKE_object.h"
#include "BKE_paint.h"
#include "BKE_report.h"
#include "BKE_scene.h"
@@ -95,39 +96,74 @@
#include "render_intern.h" // own include
-/**
- * Object list for material operations.
- * has exception for pinned object.
- */
-static Object **object_array_for_shading(bContext *C, uint *r_objects_len)
-{
- ScrArea *area = CTX_wm_area(C);
- SpaceProperties *sbuts = NULL;
- View3D *v3d = NULL;
- if (area != NULL) {
- if (area->spacetype == SPACE_PROPERTIES) {
- sbuts = area->spacedata.first;
- }
- else if (area->spacetype == SPACE_VIEW3D) {
- v3d = area->spacedata.first;
+static bool object_materials_supported_poll_ex(bContext *C, const Object *ob);
+
+/* -------------------------------------------------------------------- */
+/** \name Local Utilities
+ * \{ */
+
+static bool object_array_for_shading_edit_mode_enabled_filter(Object *ob, void *user_data)
+{
+ bContext *C = user_data;
+ if (object_materials_supported_poll_ex(C, ob)) {
+ if (BKE_object_is_in_editmode(ob) == true) {
+ return true;
}
}
+ return false;
+}
- Object **objects;
- if (sbuts != NULL && sbuts->pinid && GS(sbuts->pinid->name) == ID_OB) {
- objects = MEM_mallocN(sizeof(*objects), __func__);
- objects[0] = (Object *)sbuts->pinid;
- *r_objects_len = 1;
+static Object **object_array_for_shading_edit_mode_enabled(bContext *C, uint *r_objects_len)
+{
+ return ED_object_array_in_mode_or_selected(
+ C, object_array_for_shading_edit_mode_enabled_filter, C, r_objects_len);
+}
+
+static bool object_array_for_shading_edit_mode_disabled_filter(Object *ob, void *user_data)
+{
+ bContext *C = user_data;
+ if (object_materials_supported_poll_ex(C, ob)) {
+ if (BKE_object_is_in_editmode(ob) == false) {
+ return true;
+ }
}
- else {
- ViewLayer *view_layer = CTX_data_view_layer(C);
- objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, v3d, r_objects_len);
+ return false;
+}
+
+static Object **object_array_for_shading_edit_mode_disabled(bContext *C, uint *r_objects_len)
+{
+ return ED_object_array_in_mode_or_selected(
+ C, object_array_for_shading_edit_mode_disabled_filter, C, r_objects_len);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Shared Operator Poll Functions
+ * \{ */
+
+static bool object_materials_supported_poll_ex(bContext *C, const Object *ob)
+{
+ if (!ED_operator_object_active_local_editable_ex(C, ob)) {
+ return false;
}
- return objects;
+ const ID *data = ob->data;
+ return (OB_TYPE_SUPPORT_MATERIAL(ob->type) &&
+ /* Object data checks. */
+ data && !ID_IS_LINKED(data) && !ID_IS_OVERRIDE_LIBRARY(data));
}
-/********************** material slot operators *********************/
+static bool object_materials_supported_poll(bContext *C)
+{
+ Object *ob = ED_object_context(C);
+ return object_materials_supported_poll_ex(C, ob);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Material Slot Add Operator
+ * \{ */
static int material_slot_add_exec(bContext *C, wmOperator *UNUSED(op))
{
@@ -162,12 +198,18 @@ void OBJECT_OT_material_slot_add(wmOperatorType *ot)
/* api callbacks */
ot->exec = material_slot_add_exec;
- ot->poll = ED_operator_object_active_local_editable;
+ ot->poll = object_materials_supported_poll;
/* flags */
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);
@@ -207,12 +249,18 @@ void OBJECT_OT_material_slot_remove(wmOperatorType *ot)
/* api callbacks */
ot->exec = material_slot_remove_exec;
- ot->poll = ED_operator_object_active_local_editable;
+ ot->poll = object_materials_supported_poll;
/* flags */
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);
@@ -222,7 +270,7 @@ static int material_slot_assign_exec(bContext *C, wmOperator *UNUSED(op))
const Material *mat_active = obact ? BKE_object_material_get(obact, obact->actcol) : NULL;
uint objects_len = 0;
- Object **objects = object_array_for_shading(C, &objects_len);
+ Object **objects = object_array_for_shading_edit_mode_enabled(C, &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = objects[ob_index];
short mat_nr_active = -1;
@@ -310,12 +358,18 @@ void OBJECT_OT_material_slot_assign(wmOperatorType *ot)
/* api callbacks */
ot->exec = material_slot_assign_exec;
- ot->poll = ED_operator_object_active_local_editable;
+ ot->poll = object_materials_supported_poll;
/* flags */
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;
@@ -323,7 +377,7 @@ static int material_slot_de_select(bContext *C, bool select)
const Material *mat_active = obact ? BKE_object_material_get(obact, obact->actcol) : NULL;
uint objects_len = 0;
- Object **objects = object_array_for_shading(C, &objects_len);
+ Object **objects = object_array_for_shading_edit_mode_enabled(C, &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = objects[ob_index];
short mat_nr_active = -1;
@@ -461,6 +515,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 +575,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);
@@ -576,8 +642,8 @@ void OBJECT_OT_material_slot_move(wmOperatorType *ot)
ot->description = "Move the active material up/down in the list";
/* api callbacks */
- ot->poll = ED_operator_object_active_local_editable;
ot->exec = material_slot_move_exec;
+ ot->poll = object_materials_supported_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -590,37 +656,46 @@ void OBJECT_OT_material_slot_move(wmOperatorType *ot)
"Direction to move the active material towards");
}
-static int material_slot_remove_unused_exec(bContext *C, wmOperator *op)
-{
- Object *ob = CTX_data_active_object(C);
+/** \} */
- if (!ob) {
- return OPERATOR_CANCELLED;
- }
+/* -------------------------------------------------------------------- */
+/** \name Material Slot Remove Unused Operator
+ * \{ */
+static int material_slot_remove_unused_exec(bContext *C, wmOperator *op)
+{
/* Removing material slots in edit mode screws things up, see bug #21822.*/
- if (ob == CTX_data_edit_object(C)) {
+ Object *ob_active = CTX_data_active_object(C);
+ if (ob_active && BKE_object_is_in_editmode(ob_active)) {
BKE_report(op->reports, RPT_ERROR, "Unable to remove material slot in edit mode");
return OPERATOR_CANCELLED;
}
- int actcol = ob->actcol;
-
+ Main *bmain = CTX_data_main(C);
int removed = 0;
- for (int slot = 1; slot <= ob->totcol; slot++) {
- while (slot <= ob->totcol && !BKE_object_material_slot_used(ob->data, slot)) {
- ob->actcol = slot;
- BKE_object_material_slot_remove(CTX_data_main(C), ob);
- if (actcol >= slot) {
- actcol--;
- }
+ uint objects_len = 0;
+ Object **objects = object_array_for_shading_edit_mode_disabled(C, &objects_len);
+ for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
+ Object *ob = objects[ob_index];
+ int actcol = ob->actcol;
+ for (int slot = 1; slot <= ob->totcol; slot++) {
+ while (slot <= ob->totcol && !BKE_object_material_slot_used(ob->data, slot)) {
+ ob->actcol = slot;
+ BKE_object_material_slot_remove(bmain, ob);
+
+ if (actcol >= slot) {
+ actcol--;
+ }
- removed++;
+ removed++;
+ }
}
- }
+ ob->actcol = actcol;
- ob->actcol = actcol;
+ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
+ }
+ MEM_freeN(objects);
if (!removed) {
return OPERATOR_CANCELLED;
@@ -628,16 +703,15 @@ static int material_slot_remove_unused_exec(bContext *C, wmOperator *op)
BKE_reportf(op->reports, RPT_INFO, "Removed %d slots", removed);
- if (ob->mode & OB_MODE_TEXTURE_PAINT) {
+ if (ob_active->mode & OB_MODE_TEXTURE_PAINT) {
Scene *scene = CTX_data_scene(C);
- BKE_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL);
+ BKE_paint_proj_mesh_data_check(scene, ob_active, NULL, NULL, NULL, NULL);
WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL);
}
- DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
- WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, ob);
- WM_event_add_notifier(C, NC_MATERIAL | ND_SHADING_PREVIEW, ob);
+ WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob_active);
+ WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, ob_active);
+ WM_event_add_notifier(C, NC_MATERIAL | ND_SHADING_PREVIEW, ob_active);
return OPERATOR_FINISHED;
}
@@ -651,13 +725,17 @@ void OBJECT_OT_material_slot_remove_unused(wmOperatorType *ot)
/* api callbacks */
ot->exec = material_slot_remove_unused_exec;
- ot->poll = ED_operator_object_active_local_editable;
+ ot->poll = object_materials_supported_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/********************** new material operator *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name New Material Operator
+ * \{ */
static int new_material_exec(bContext *C, wmOperator *UNUSED(op))
{
@@ -720,14 +798,18 @@ void MATERIAL_OT_new(wmOperatorType *ot)
ot->description = "Add a new material";
/* api callbacks */
- ot->poll = ED_operator_object_active_local_editable;
ot->exec = new_material_exec;
+ ot->poll = object_materials_supported_poll;
/* flags */
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 +858,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 +913,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 +967,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 +1009,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 +1180,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 +1229,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 +1269,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 +1304,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 +1364,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 +1416,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 +1451,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 +1485,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 +1526,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 +1560,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 +1594,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 +1643,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 +1687,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 +1734,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 +1781,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 +1828,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 +1875,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 +1948,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 +2004,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 +2079,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 +2117,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 +2191,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 +2226,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 +2262,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 +2334,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 +2378,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 +2438,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 fa63a890de7..47edb322701 100644
--- a/source/blender/editors/scene/scene_edit.c
+++ b/source/blender/editors/scene/scene_edit.c
@@ -116,10 +116,10 @@ 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, bmain, scene, layer);
+ DEG_graph_relations_update(depsgraph);
DEG_on_visible_update(bmain, false);
ED_render_engine_changed(bmain, false);
diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c
index 38bac3afef6..921cc92299e 100644
--- a/source/blender/editors/screen/area.c
+++ b/source/blender/editors/screen/area.c
@@ -93,9 +93,7 @@ static void region_draw_emboss(const ARegion *region, const rcti *scirct, int si
rect.ymax = scirct->ymax - region->winrct.ymin;
/* set transp line */
- GPU_blend(true);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
float color[4] = {0.0f, 0.0f, 0.0f, 0.25f};
UI_GetThemeColor3fv(TH_EDITOR_OUTLINE, color);
@@ -134,8 +132,7 @@ static void region_draw_emboss(const ARegion *region, const rcti *scirct, int si
immEnd();
immUnbindProgram();
- GPU_blend(false);
- GPU_blend_set_func(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_NONE);
}
void ED_region_pixelspace(ARegion *region)
@@ -248,7 +245,7 @@ static void draw_azone_arrow(float x1, float y1, float x2, float y2, AZEdge edge
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
/* NOTE(fclem): There is something strange going on with Mesa and GPU_SHADER_2D_UNIFORM_COLOR
* that causes a crash on some GPUs (see T76113). Using 3D variant avoid the issue. */
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
@@ -266,12 +263,12 @@ static void draw_azone_arrow(float x1, float y1, float x2, float y2, AZEdge edge
immEnd();
immUnbindProgram();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
static void region_draw_azone_tab_arrow(ScrArea *area, ARegion *region, AZone *az)
{
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
/* add code to draw region hidden as 'too small' */
switch (az->edge) {
@@ -305,21 +302,17 @@ static void area_azone_tag_update(ScrArea *area)
static void region_draw_azones(ScrArea *area, ARegion *region)
{
- AZone *az;
-
if (!area) {
return;
}
GPU_line_width(1.0f);
- GPU_blend(true);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
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);
@@ -349,7 +342,7 @@ static void region_draw_azones(ScrArea *area, ARegion *region)
GPU_matrix_pop();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
static void region_draw_status_text(ScrArea *area, ARegion *region)
@@ -358,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();
@@ -378,8 +369,7 @@ static void region_draw_status_text(ScrArea *area, ARegion *region)
const float y1 = pad;
const float y2 = region->winy - pad;
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
float color[4] = {0.0f, 0.0f, 0.0f, 0.5f};
UI_GetThemeColor3fv(TH_BACK, color);
@@ -527,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? */
@@ -548,7 +537,7 @@ void ED_region_do_draw(bContext *C, ARegion *region)
/* for debugging unneeded area redraws and partial redraw */
if (G.debug_value == 888) {
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
@@ -559,7 +548,7 @@ void ED_region_do_draw(bContext *C, ARegion *region)
region->drawrct.xmax - region->winrct.xmin,
region->drawrct.ymax - region->winrct.ymin);
immUnbindProgram();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
memset(&region->drawrct, 0, sizeof(region->drawrct));
@@ -711,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);
}
}
@@ -722,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);
}
}
@@ -733,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);
}
@@ -756,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) {
@@ -948,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 */
@@ -956,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++;
}
@@ -1852,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;
@@ -1869,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);
}
@@ -1893,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) {
@@ -2012,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;
@@ -2032,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);
}
@@ -2053,6 +2031,241 @@ void ED_area_data_swap(ScrArea *area_dst, ScrArea *area_src)
SWAP(ListBase, area_dst->regionbase, area_src->regionbase);
}
+/* -------------------------------------------------------------------- */
+/** \name Region Alignment Syncing for Space Switching
+ * \{ */
+
+/**
+ * Store the alignment & other info per region type
+ * (use as a region-type aligned array).
+ *
+ * \note Currently this is only done for headers,
+ * we might want to do this with the tool-bar in the future too.
+ */
+struct RegionTypeAlignInfo {
+ struct {
+ /**
+ * Values match #ARegion.alignment without flags (see #RGN_ALIGN_ENUM_FROM_MASK).
+ * store all so we can sync alignment without adding extra checks.
+ */
+ short alignment;
+ /**
+ * Needed for detecting which header displays the space-type switcher.
+ */
+ bool hidden;
+ } by_type[RGN_TYPE_LEN];
+};
+
+static void region_align_info_from_area(ScrArea *area, struct RegionTypeAlignInfo *r_align_info)
+{
+ for (int index = 0; index < RGN_TYPE_LEN; index++) {
+ r_align_info->by_type[index].alignment = -1;
+ /* Default to true, when it doesn't exist - it's effectively hidden. */
+ r_align_info->by_type[index].hidden = true;
+ }
+
+ LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
+ const int index = region->regiontype;
+ if ((uint)index < RGN_TYPE_LEN) {
+ r_align_info->by_type[index].alignment = RGN_ALIGN_ENUM_FROM_MASK(region->alignment);
+ r_align_info->by_type[index].hidden = (region->flag & RGN_FLAG_HIDDEN) != 0;
+ }
+ }
+}
+
+/**
+ * Keeping alignment between headers keep the space-type selector button in the same place.
+ * This is complicated by the editor-type selector being placed on the header
+ * closest to the screen edge which changes based on hidden state.
+ *
+ * The tool-header is used when visible, otherwise the header is used.
+ */
+static short region_alignment_from_header_and_tool_header_state(
+ const struct RegionTypeAlignInfo *region_align_info, const short fallback)
+{
+ const short header_alignment = region_align_info->by_type[RGN_TYPE_HEADER].alignment;
+ const short tool_header_alignment = region_align_info->by_type[RGN_TYPE_TOOL_HEADER].alignment;
+
+ const bool header_hidden = region_align_info->by_type[RGN_TYPE_HEADER].hidden;
+ const bool tool_header_hidden = region_align_info->by_type[RGN_TYPE_TOOL_HEADER].hidden;
+
+ if ((tool_header_alignment != -1) &&
+ /* If tool-header is hidden, use header alignment. */
+ ((tool_header_hidden == false) ||
+ /* Don't prioritize the tool-header if both are hidden (behave as if both are visible).
+ * Without this, switching to a space with headers hidden will flip the alignment
+ * upon switching to a space with visible headers. */
+ (header_hidden && tool_header_hidden))) {
+ return tool_header_alignment;
+ }
+ if (header_alignment != -1) {
+ return header_alignment;
+ }
+ return fallback;
+}
+
+/**
+ * Notes on header alignment syncing.
+ *
+ * This is as involved as it is because:
+ *
+ * - There are currently 3 kinds of headers.
+ * - All headers can independently visible & flipped to another side
+ * (except for the tool-header that depends on the header visibility).
+ * - We don't want the space-switching button to flip when switching spaces.
+ * From the user perspective it feels like a bug to move the button you click on
+ * to the opposite side of the area.
+ * - The space-switcher may be on either the header or the tool-header
+ * depending on the tool-header visibility.
+ *
+ * How this works:
+ *
+ * - When headers match on both spaces, we copy the alignment
+ * from the previous regions to the next regions when syncing.
+ * - Otherwise detect the _primary_ header (the one that shows the space type)
+ * and use this to set alignment for the headers in the destination area.
+ * - Header & tool-header/footer may be on opposite sides, this is preserved when syncing.
+ */
+static void region_align_info_to_area_for_headers(
+ const struct RegionTypeAlignInfo *region_align_info_src,
+ const struct RegionTypeAlignInfo *region_align_info_dst,
+ ARegion *region_by_type[RGN_TYPE_LEN])
+{
+ /* Abbreviate access. */
+ const short header_alignment_src = region_align_info_src->by_type[RGN_TYPE_HEADER].alignment;
+ const short tool_header_alignment_src =
+ region_align_info_src->by_type[RGN_TYPE_TOOL_HEADER].alignment;
+
+ const bool tool_header_hidden_src = region_align_info_src->by_type[RGN_TYPE_TOOL_HEADER].hidden;
+
+ const short primary_header_alignment_src = region_alignment_from_header_and_tool_header_state(
+ region_align_info_src, -1);
+
+ /* Neither alignments are usable, don't sync. */
+ if (primary_header_alignment_src == -1) {
+ return;
+ }
+
+ const short header_alignment_dst = region_align_info_dst->by_type[RGN_TYPE_HEADER].alignment;
+ const short tool_header_alignment_dst =
+ region_align_info_dst->by_type[RGN_TYPE_TOOL_HEADER].alignment;
+ const short footer_alignment_dst = region_align_info_dst->by_type[RGN_TYPE_FOOTER].alignment;
+
+ const bool tool_header_hidden_dst = region_align_info_dst->by_type[RGN_TYPE_TOOL_HEADER].hidden;
+
+ /* New synchronized alignments to set (or ignore when left as -1). */
+ short header_alignment_sync = -1;
+ short tool_header_alignment_sync = -1;
+ short footer_alignment_sync = -1;
+
+ /* Both source/destination areas have same region configurations regarding headers.
+ * Simply copy the values. */
+ if (((header_alignment_src != -1) == (header_alignment_dst != -1)) &&
+ ((tool_header_alignment_src != -1) == (tool_header_alignment_dst != -1)) &&
+ (tool_header_hidden_src == tool_header_hidden_dst)) {
+ if (header_alignment_dst != -1) {
+ header_alignment_sync = header_alignment_src;
+ }
+ if (tool_header_alignment_dst != -1) {
+ tool_header_alignment_sync = tool_header_alignment_src;
+ }
+ }
+ else {
+ /* Not an exact match, check the space selector isn't moving. */
+ const short primary_header_alignment_dst = region_alignment_from_header_and_tool_header_state(
+ region_align_info_dst, -1);
+
+ if (primary_header_alignment_src != primary_header_alignment_dst) {
+ if ((header_alignment_dst != -1) && (tool_header_alignment_dst != -1)) {
+ if (header_alignment_dst == tool_header_alignment_dst) {
+ /* Apply to both. */
+ tool_header_alignment_sync = primary_header_alignment_src;
+ header_alignment_sync = primary_header_alignment_src;
+ }
+ else {
+ /* Keep on opposite sides. */
+ tool_header_alignment_sync = primary_header_alignment_src;
+ header_alignment_sync = (tool_header_alignment_sync == RGN_ALIGN_BOTTOM) ?
+ RGN_ALIGN_TOP :
+ RGN_ALIGN_BOTTOM;
+ }
+ }
+ else {
+ /* Apply what we can to regions that exist. */
+ if (header_alignment_dst != -1) {
+ header_alignment_sync = primary_header_alignment_src;
+ }
+ if (tool_header_alignment_dst != -1) {
+ tool_header_alignment_sync = primary_header_alignment_src;
+ }
+ }
+ }
+ }
+
+ if (footer_alignment_dst != -1) {
+ if ((header_alignment_dst != -1) && (header_alignment_dst == footer_alignment_dst)) {
+ /* Apply to both. */
+ footer_alignment_sync = primary_header_alignment_src;
+ }
+ else {
+ /* Keep on opposite sides. */
+ footer_alignment_sync = (primary_header_alignment_src == RGN_ALIGN_BOTTOM) ?
+ RGN_ALIGN_TOP :
+ RGN_ALIGN_BOTTOM;
+ }
+ }
+
+ /* Finally apply synchronized flags. */
+ if (header_alignment_sync != -1) {
+ ARegion *region = region_by_type[RGN_TYPE_HEADER];
+ if (region != NULL) {
+ region->alignment = RGN_ALIGN_ENUM_FROM_MASK(header_alignment_sync) |
+ RGN_ALIGN_FLAG_FROM_MASK(region->alignment);
+ }
+ }
+
+ if (tool_header_alignment_sync != -1) {
+ ARegion *region = region_by_type[RGN_TYPE_TOOL_HEADER];
+ if (region != NULL) {
+ region->alignment = RGN_ALIGN_ENUM_FROM_MASK(tool_header_alignment_sync) |
+ RGN_ALIGN_FLAG_FROM_MASK(region->alignment);
+ }
+ }
+
+ if (footer_alignment_sync != -1) {
+ ARegion *region = region_by_type[RGN_TYPE_FOOTER];
+ if (region != NULL) {
+ region->alignment = RGN_ALIGN_ENUM_FROM_MASK(footer_alignment_sync) |
+ RGN_ALIGN_FLAG_FROM_MASK(region->alignment);
+ }
+ }
+}
+
+static void region_align_info_to_area(
+ ScrArea *area, const struct RegionTypeAlignInfo region_align_info_src[RGN_TYPE_LEN])
+{
+ ARegion *region_by_type[RGN_TYPE_LEN] = {NULL};
+ LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
+ const int index = region->regiontype;
+ if ((uint)index < RGN_TYPE_LEN) {
+ region_by_type[index] = region;
+ }
+ }
+
+ struct RegionTypeAlignInfo region_align_info_dst;
+ region_align_info_from_area(area, &region_align_info_dst);
+
+ if ((region_by_type[RGN_TYPE_HEADER] != NULL) ||
+ (region_by_type[RGN_TYPE_TOOL_HEADER] != NULL)) {
+ region_align_info_to_area_for_headers(
+ region_align_info_src, &region_align_info_dst, region_by_type);
+ }
+
+ /* Note that we could support other region types. */
+}
+
+/** \} */
+
/* *********** Space switching code *********** */
void ED_area_swapspace(bContext *C, ScrArea *sa1, ScrArea *sa2)
@@ -2091,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,
@@ -2104,9 +2316,13 @@ void ED_area_newspace(bContext *C, ScrArea *area, int type, const bool skip_regi
* the space type defaults to in this case instead
* (needed for preferences to have space-type on bottom).
*/
- int header_alignment = ED_area_header_alignment_or_fallback(area, -1);
- const bool sync_header_alignment = ((header_alignment != -1) &&
- ((slold->link_flag & SPACE_FLAG_TYPE_TEMPORARY) == 0));
+
+ bool sync_header_alignment = false;
+ struct RegionTypeAlignInfo region_align_info[RGN_TYPE_LEN];
+ if ((slold != NULL) && (slold->link_flag & SPACE_FLAG_TYPE_TEMPORARY) == 0) {
+ region_align_info_from_area(area, region_align_info);
+ sync_header_alignment = true;
+ }
/* in some cases (opening temp space) we don't want to
* call area exit callback, so we temporarily unset it */
@@ -2131,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;
}
}
@@ -2180,28 +2398,7 @@ void ED_area_newspace(bContext *C, ScrArea *area, int type, const bool skip_regi
/* Sync header alignment. */
if (sync_header_alignment) {
- /* Spaces with footer. */
- if (st->spaceid == SPACE_TEXT) {
- LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
- if (ELEM(region->regiontype, RGN_TYPE_HEADER, RGN_TYPE_TOOL_HEADER)) {
- region->alignment = header_alignment;
- }
- if (region->regiontype == RGN_TYPE_FOOTER) {
- int footer_alignment = (header_alignment == RGN_ALIGN_BOTTOM) ? RGN_ALIGN_TOP :
- RGN_ALIGN_BOTTOM;
- region->alignment = footer_alignment;
- break;
- }
- }
- }
- else {
- LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
- if (ELEM(region->regiontype, RGN_TYPE_HEADER, RGN_TYPE_TOOL_HEADER)) {
- region->alignment = header_alignment;
- break;
- }
- }
- }
+ region_align_info_to_area(area, region_align_info);
}
ED_area_init(CTX_wm_manager(C), win, area);
@@ -2329,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);
}
}
@@ -2357,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();
@@ -2380,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,
@@ -2405,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);
@@ -2482,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);
}
/**
@@ -2508,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;
@@ -2560,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) {
@@ -2603,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;
@@ -2643,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);
}
@@ -2678,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);
}
}
@@ -2713,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) {
@@ -2728,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' */
@@ -2754,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)
@@ -2799,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);
}
@@ -2830,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;
@@ -2853,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;
}
@@ -2947,43 +3090,11 @@ int ED_area_headersize(void)
return U.widget_unit + (int)(UI_DPI_FAC * HEADER_PADDING_Y);
}
-int ED_area_header_alignment_or_fallback(const ScrArea *area, int fallback)
-{
- LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
- if (region->regiontype == RGN_TYPE_HEADER) {
- return region->alignment;
- }
- }
- return fallback;
-}
-
-int ED_area_header_alignment(const ScrArea *area)
-{
- return ED_area_header_alignment_or_fallback(
- area, (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_BOTTOM : RGN_ALIGN_TOP);
-}
-
int ED_area_footersize(void)
{
return ED_area_headersize();
}
-int ED_area_footer_alignment_or_fallback(const ScrArea *area, int fallback)
-{
- LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
- if (region->regiontype == RGN_TYPE_FOOTER) {
- return region->alignment;
- }
- }
- return fallback;
-}
-
-int ED_area_footer_alignment(const ScrArea *area)
-{
- return ED_area_footer_alignment_or_fallback(
- area, (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_TOP : RGN_ALIGN_BOTTOM);
-}
-
/**
* \return the final height of a global \a area, accounting for DPI.
*/
@@ -3084,19 +3195,17 @@ void ED_region_info_draw_multiline(ARegion *region,
rect.ymin = rect.ymax - header_height * num_lines;
/* setup scissor */
- GPU_scissor_get_i(scissor);
+ GPU_scissor_get(scissor);
GPU_scissor(rect.xmin, rect.ymin, BLI_rcti_size_x(&rect) + 1, BLI_rcti_size_y(&rect) + 1);
- GPU_blend(true);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
immUniformColor4fv(fill_color);
immRecti(pos, rect.xmin, rect.ymin, rect.xmax + 1, rect.ymax + 1);
immUnbindProgram();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
/* text */
UI_FontThemeColor(fontid, TH_TEXT_HI);
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 40a452a5363..8ded845b008 100644
--- a/source/blender/editors/screen/screen_draw.c
+++ b/source/blender/editors/screen/screen_draw.c
@@ -296,8 +296,7 @@ static GPUBatch *batch_screen_edges_get(int *corner_len)
*/
static void scrarea_draw_shape_dark(ScrArea *area, char dir, uint pos)
{
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
immUniformColor4ub(0, 0, 0, 50);
draw_join_shape(area, dir, pos);
@@ -308,10 +307,8 @@ static void scrarea_draw_shape_dark(ScrArea *area, char dir, uint pos)
*/
static void scrarea_draw_shape_light(ScrArea *area, char UNUSED(dir), uint pos)
{
- GPU_blend_set_func(GPU_DST_COLOR, GPU_SRC_ALPHA);
- /* value 181 was hardly computed: 181~105 */
- immUniformColor4ub(255, 255, 255, 50);
- /* draw_join_shape(area, dir); */
+ GPU_blend(GPU_BLEND_ALPHA);
+ immUniformColor4ub(255, 255, 255, 25);
immRectf(pos, area->v1->vec.x, area->v1->vec.y, area->v3->vec.x, area->v3->vec.y);
}
@@ -343,6 +340,7 @@ static void drawscredge_area_draw(
}
GPUBatch *batch = batch_screen_edges_get(NULL);
+ GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_AREA_EDGES);
GPU_batch_uniform_4fv(batch, "rect", (float *)&rect);
GPU_batch_draw(batch);
}
@@ -381,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});
}
@@ -412,9 +408,7 @@ void ED_screen_draw_edges(wmWindow *win)
corner_scale = U.pixelsize * 8.0f;
edge_thickness = corner_scale * 0.21f;
- GPU_blend(true);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
GPUBatch *batch = batch_screen_edges_get(&verts_per_corner);
GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_AREA_EDGES);
@@ -422,11 +416,11 @@ 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);
}
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
if (U.pixelsize <= 1.0f) {
GPU_scissor_test(false);
@@ -469,12 +463,12 @@ void ED_screen_draw_join_shape(ScrArea *sa1, ScrArea *sa2)
break;
}
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
scrarea_draw_shape_dark(sa2, dir, pos);
scrarea_draw_shape_light(sa1, dira, pos);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
immUnbindProgram();
@@ -486,9 +480,7 @@ void ED_screen_draw_split_preview(ScrArea *area, const int dir, const float fac)
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
/* splitpoint */
- GPU_blend(true);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
immUniformColor4ub(255, 255, 255, 100);
@@ -530,7 +522,7 @@ void ED_screen_draw_split_preview(ScrArea *area, const int dir, const float fac)
immEnd();
}
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
immUnbindProgram();
}
@@ -614,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 06e800433b1..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);
@@ -1594,7 +1583,7 @@ void ED_update_for_newframe(Main *bmain, Depsgraph *depsgraph)
ED_clip_update_frame(bmain, scene->r.cfra);
/* this function applies the changes too */
- BKE_scene_graph_update_for_newframe(depsgraph, bmain);
+ BKE_scene_graph_update_for_newframe(depsgraph);
}
/*
@@ -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..5730d8b2d03 100644
--- a/source/blender/editors/screen/screen_ops.c
+++ b/source/blender/editors/screen/screen_ops.c
@@ -363,6 +363,11 @@ bool ED_operator_object_active_editable(bContext *C)
}
/** Object must be editable and fully local (i.e. not an override). */
+bool ED_operator_object_active_local_editable_ex(bContext *C, const Object *ob)
+{
+ return ED_operator_object_active_editable_ex(C, ob) && !ID_IS_OVERRIDE_LIBRARY(ob);
+}
+
bool ED_operator_object_active_local_editable(bContext *C)
{
Object *ob = ED_object_active_context(C);
@@ -692,10 +697,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 +3072,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 +3173,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 +3623,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 +3856,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 +3879,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 +4437,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 +4587,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 +4865,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 88998d5063d..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();
@@ -634,8 +634,7 @@ static bool paint_draw_tex_overlay(UnifiedPaintSettings *ups,
uint texCoord = GPU_vertformat_attr_add(format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
/* Premultiplied alpha blending. */
- GPU_blend_set_func(GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA_PREMULT);
immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_COLOR);
@@ -670,8 +669,6 @@ static bool paint_draw_tex_overlay(UnifiedPaintSettings *ups,
GPU_texture_unbind(texture);
- GPU_blend_set_func(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA);
-
if (ELEM(mtex->brush_map_mode, MTEX_MAP_MODE_STENCIL, MTEX_MAP_MODE_VIEW)) {
GPU_matrix_pop();
}
@@ -696,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);
@@ -729,8 +726,7 @@ static bool paint_draw_cursor_overlay(
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
uint texCoord = GPU_vertformat_attr_add(format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- GPU_blend_set_func(GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA_PREMULT);
immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_COLOR);
@@ -757,8 +753,6 @@ static bool paint_draw_cursor_overlay(
immUnbindProgram();
- GPU_blend_set_func(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA);
-
if (do_pop) {
GPU_matrix_pop();
}
@@ -781,7 +775,8 @@ static bool paint_draw_alpha_overlay(UnifiedPaintSettings *ups,
bool alpha_overlay_active = false;
ePaintOverlayControlFlags flags = BKE_paint_get_overlay_flags();
- gpuPushAttr(GPU_DEPTH_BUFFER_BIT | GPU_BLEND_BIT);
+ eGPUBlend blend_state = GPU_blend_get();
+ eGPUDepthTest depth_test = GPU_depth_test_get();
/* Translate to region. */
GPU_matrix_push();
@@ -811,7 +806,8 @@ static bool paint_draw_alpha_overlay(UnifiedPaintSettings *ups,
}
GPU_matrix_pop();
- gpuPopAttr();
+ GPU_blend(blend_state);
+ GPU_depth_test(depth_test);
return alpha_overlay_active;
}
@@ -922,7 +918,7 @@ static void paint_draw_curve_cursor(Brush *brush, ViewContext *vc)
PaintCurvePoint *cp = pc->points;
GPU_line_smooth(true);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
/* Draw the bezier handles and the curve segment between the current and next point. */
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
@@ -983,7 +979,7 @@ static void paint_draw_curve_cursor(Brush *brush, ViewContext *vc)
draw_rect_point(
pos, selec_col, handle_col, &cp->bez.vec[2][0], 8.0f, cp->bez.f3 || cp->bez.f2);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
GPU_line_smooth(false);
immUnbindProgram();
@@ -1151,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);
@@ -1167,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);
}
}
@@ -1542,7 +1538,7 @@ static void paint_cursor_preview_boundary_data_update(PaintCursorContext *pconte
}
ss->boundary_preview = SCULPT_boundary_data_init(
- pcontext->vc.obact, ss->active_vertex_index, pcontext->radius);
+ pcontext->vc.obact, pcontext->brush, ss->active_vertex_index, pcontext->radius);
}
static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext *pcontext)
@@ -1762,9 +1758,6 @@ static void paint_cursor_cursor_draw_3d_view_brush_cursor_active(PaintCursorCont
GPU_matrix_pop();
- /* This Cloth brush cursor overlay always works in cursor space. */
- paint_cursor_drawing_setup_cursor_space(pcontext);
-
GPU_matrix_pop_projection();
wmWindowViewport(pcontext->win);
}
@@ -1842,7 +1835,7 @@ static void paint_cursor_update_anchored_location(PaintCursorContext *pcontext)
static void paint_cursor_setup_2D_drawing(PaintCursorContext *pcontext)
{
GPU_line_width(2.0f);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
GPU_line_smooth(true);
pcontext->pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
@@ -1852,7 +1845,7 @@ static void paint_cursor_setup_2D_drawing(PaintCursorContext *pcontext)
static void paint_cursor_setup_3D_drawing(PaintCursorContext *pcontext)
{
GPU_line_width(2.0f);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
GPU_line_smooth(true);
pcontext->pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
@@ -1862,7 +1855,7 @@ static void paint_cursor_setup_3D_drawing(PaintCursorContext *pcontext)
static void paint_cursor_restore_drawing_state(void)
{
immUnbindProgram();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
GPU_line_smooth(false);
}
diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c
index 431ab998f62..d2ae6912fc3 100644
--- a/source/blender/editors/sculpt_paint/paint_image.c
+++ b/source/blender/editors/sculpt_paint/paint_image.c
@@ -438,7 +438,7 @@ static void gradient_draw_line(bContext *UNUSED(C), int x, int y, void *customda
if (pop) {
GPU_line_smooth(true);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
@@ -467,7 +467,7 @@ static void gradient_draw_line(bContext *UNUSED(C), int x, int y, void *customda
immUnbindProgram();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
GPU_line_smooth(false);
}
}
diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c
index db7de01bee5..456c1f61cb1 100644
--- a/source/blender/editors/sculpt_paint/paint_image_proj.c
+++ b/source/blender/editors/sculpt_paint/paint_image_proj.c
@@ -407,7 +407,6 @@ typedef struct ProjPaintState {
SpinLock *tile_lock;
Mesh *me_eval;
- bool me_eval_free;
int totlooptri_eval;
int totloop_eval;
int totpoly_eval;
@@ -4033,27 +4032,14 @@ static bool proj_paint_state_mesh_eval_init(const bContext *C, ProjPaintState *p
CustomData_MeshMasks cddata_masks = scene_eval->customdata_mask;
cddata_masks.fmask |= CD_MASK_MTFACE;
cddata_masks.lmask |= CD_MASK_MLOOPUV;
-
- /* Workaround for subsurf selection, try the display mesh first */
- if (ps->source == PROJ_SRC_IMAGE_CAM) {
- /* using render mesh, assume only camera was rendered from */
- ps->me_eval = mesh_create_eval_final_render(depsgraph, scene_eval, ob_eval, &cddata_masks);
- ps->me_eval_free = true;
- }
- else {
- if (ps->do_face_sel) {
- cddata_masks.vmask |= CD_MASK_ORIGINDEX;
- cddata_masks.emask |= CD_MASK_ORIGINDEX;
- cddata_masks.pmask |= CD_MASK_ORIGINDEX;
- }
- ps->me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_eval, &cddata_masks);
- ps->me_eval_free = false;
+ if (ps->do_face_sel) {
+ cddata_masks.vmask |= CD_MASK_ORIGINDEX;
+ cddata_masks.emask |= CD_MASK_ORIGINDEX;
+ cddata_masks.pmask |= CD_MASK_ORIGINDEX;
}
+ ps->me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_eval, &cddata_masks);
if (!CustomData_has_layer(&ps->me_eval->ldata, CD_MLOOPUV)) {
- if (ps->me_eval_free) {
- BKE_id_free(NULL, ps->me_eval);
- }
ps->me_eval = NULL;
return false;
}
@@ -4636,9 +4622,6 @@ static void project_paint_end(ProjPaintState *ps)
MEM_freeN(ps->cavities);
}
- if (ps->me_eval_free) {
- BKE_id_free(NULL, ps->me_eval);
- }
ps->me_eval = NULL;
}
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 90b0f017bd6..e709224f370 100644
--- a/source/blender/editors/sculpt_paint/paint_stroke.c
+++ b/source/blender/editors/sculpt_paint/paint_stroke.c
@@ -143,7 +143,7 @@ static void paint_draw_smooth_cursor(bContext *C, int x, int y, void *customdata
if (stroke && brush) {
GPU_line_smooth(true);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
ARegion *region = stroke->vc.region;
@@ -161,7 +161,7 @@ static void paint_draw_smooth_cursor(bContext *C, int x, int y, void *customdata
immUnbindProgram();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
GPU_line_smooth(false);
}
}
@@ -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;
@@ -997,7 +1005,19 @@ static void stroke_done(bContext *C, wmOperator *op)
/* Returns zero if the stroke dots should not be spaced, non-zero otherwise */
bool paint_space_stroke_enabled(Brush *br, ePaintMode mode)
{
- return (br->flag & BRUSH_SPACE) && paint_supports_dynamic_size(br, mode);
+ if ((br->flag & BRUSH_SPACE) == 0) {
+ return false;
+ }
+
+ 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
+ * grab brushes. */
+ return true;
+ }
+
+ return paint_supports_dynamic_size(br, mode);
}
static bool sculpt_is_grab_tool(Brush *br)
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index d68b1226b40..7a066f35f23 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -275,6 +275,19 @@ void SCULPT_active_vertex_normal_get(SculptSession *ss, float normal[3])
SCULPT_vertex_normal_get(ss, SCULPT_active_vertex_get(ss), normal);
}
+float *SCULPT_brush_deform_target_vertex_co_get(SculptSession *ss,
+ const int deform_target,
+ PBVHVertexIter *iter)
+{
+ switch (deform_target) {
+ case BRUSH_DEFORM_TARGET_GEOMETRY:
+ return iter->co;
+ case BRUSH_DEFORM_TARGET_CLOTH_SIM:
+ return ss->cache->cloth_sim->deformation_pos[iter->index];
+ }
+ return iter->co;
+}
+
/* Sculpt Face Sets and Visibility. */
int SCULPT_active_face_set_get(SculptSession *ss)
@@ -2260,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;
}
@@ -2331,7 +2344,7 @@ static float brush_strength(const Sculpt *sd,
}
case SCULPT_TOOL_SMOOTH:
- return alpha * pressure * feather;
+ return flip * alpha * pressure * feather;
case SCULPT_TOOL_PINCH:
if (flip > 0.0f) {
@@ -5690,6 +5703,16 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
SCULPT_pose_brush_init(sd, ob, ss, brush);
}
+ if (brush->deform_target == BRUSH_DEFORM_TARGET_CLOTH_SIM) {
+ if (!ss->cache->cloth_sim) {
+ ss->cache->cloth_sim = SCULPT_cloth_brush_simulation_create(ss, brush, 1.0f, 0.0f, false);
+ SCULPT_cloth_brush_simulation_init(ss, ss->cache->cloth_sim);
+ SCULPT_cloth_brush_build_nodes_constraints(
+ sd, ob, nodes, totnode, ss->cache->cloth_sim, ss->cache->location, FLT_MAX);
+ }
+ SCULPT_cloth_brush_store_simulation_state(ss, ss->cache->cloth_sim);
+ }
+
bool invert = ss->cache->pen_flip || ss->cache->invert || brush->flag & BRUSH_DIR_IN;
/* Apply one type of brush action. */
@@ -5828,6 +5851,12 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
do_gravity(sd, ob, nodes, totnode, sd->gravity_factor);
}
+ if (brush->deform_target == BRUSH_DEFORM_TARGET_CLOTH_SIM) {
+ if (SCULPT_stroke_is_main_symmetry_pass(ss->cache)) {
+ SCULPT_cloth_brush_do_simulation_step(sd, ob, ss->cache->cloth_sim, nodes, totnode);
+ }
+ }
+
MEM_SAFE_FREE(nodes);
/* Update average stroke position. */
@@ -6365,14 +6394,15 @@ void SCULPT_cache_free(StrokeCache *cache)
MEM_SAFE_FREE(cache->surface_smooth_laplacian_disp);
MEM_SAFE_FREE(cache->layer_displacement_factor);
MEM_SAFE_FREE(cache->prev_colors);
+ MEM_SAFE_FREE(cache->detail_directions);
if (cache->pose_ik_chain) {
SCULPT_pose_ik_chain_free(cache->pose_ik_chain);
}
for (int i = 0; i < PAINT_SYMM_AREAS; i++) {
- if (cache->bdata[i]) {
- SCULPT_boundary_data_free(cache->bdata[i]);
+ if (cache->boundaries[i]) {
+ SCULPT_boundary_data_free(cache->boundaries[i]);
}
}
@@ -6604,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
@@ -6618,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,
@@ -6664,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);
}
@@ -7125,6 +7163,7 @@ bool SCULPT_cursor_geometry_info_update(bContext *C,
/* Update the active vertex of the SculptSession. */
ss->active_vertex_index = srd.active_vertex_index;
+ SCULPT_vertex_random_access_ensure(ss);
copy_v3_v3(out->active_vertex_co, SCULPT_active_vertex_co_get(ss));
switch (BKE_pbvh_type(ss->pbvh)) {
@@ -7306,7 +7345,8 @@ static void sculpt_brush_stroke_init(bContext *C, wmOperator *op)
need_mask = true;
}
- if (brush->sculpt_tool == SCULPT_TOOL_CLOTH) {
+ if (brush->sculpt_tool == SCULPT_TOOL_CLOTH ||
+ brush->deform_target == BRUSH_DEFORM_TARGET_CLOTH_SIM) {
need_mask = true;
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_boundary.c b/source/blender/editors/sculpt_paint/sculpt_boundary.c
index c86e922a347..5e01e034715 100644
--- a/source/blender/editors/sculpt_paint/sculpt_boundary.c
+++ b/source/blender/editors/sculpt_paint/sculpt_boundary.c
@@ -130,34 +130,39 @@ static int sculpt_boundary_get_closest_boundary_vertex(SculptSession *ss,
* deformations usually need in the boundary. */
static int BOUNDARY_INDICES_BLOCK_SIZE = 300;
-static void sculpt_boundary_index_add(SculptBoundary *bdata,
+static void sculpt_boundary_index_add(SculptBoundary *boundary,
const int new_index,
+ const float distance,
GSet *included_vertices)
{
- bdata->vertices[bdata->num_vertices] = new_index;
+ boundary->vertices[boundary->num_vertices] = new_index;
+ if (boundary->distance) {
+ boundary->distance[new_index] = distance;
+ }
if (included_vertices) {
BLI_gset_add(included_vertices, POINTER_FROM_INT(new_index));
}
- bdata->num_vertices++;
- if (bdata->num_vertices >= bdata->vertices_capacity) {
- bdata->vertices_capacity += BOUNDARY_INDICES_BLOCK_SIZE;
- bdata->vertices = MEM_reallocN_id(
- bdata->vertices, bdata->vertices_capacity * sizeof(int), "boundary indices");
+ boundary->num_vertices++;
+ if (boundary->num_vertices >= boundary->vertices_capacity) {
+ boundary->vertices_capacity += BOUNDARY_INDICES_BLOCK_SIZE;
+ boundary->vertices = MEM_reallocN_id(
+ boundary->vertices, boundary->vertices_capacity * sizeof(int), "boundary indices");
}
};
-static void sculpt_boundary_preview_edge_add(SculptBoundary *bdata, const int v1, const int v2)
+static void sculpt_boundary_preview_edge_add(SculptBoundary *boundary, const int v1, const int v2)
{
- bdata->edges[bdata->num_edges].v1 = v1;
- bdata->edges[bdata->num_edges].v2 = v2;
- bdata->num_edges++;
+ boundary->edges[boundary->num_edges].v1 = v1;
+ boundary->edges[boundary->num_edges].v2 = v2;
+ boundary->num_edges++;
- if (bdata->num_edges >= bdata->edges_capacity) {
- bdata->edges_capacity += BOUNDARY_INDICES_BLOCK_SIZE;
- bdata->edges = MEM_reallocN_id(
- bdata->edges, bdata->edges_capacity * sizeof(SculptBoundaryPreviewEdge), "boundary edges");
+ if (boundary->num_edges >= boundary->edges_capacity) {
+ boundary->edges_capacity += BOUNDARY_INDICES_BLOCK_SIZE;
+ boundary->edges = MEM_reallocN_id(boundary->edges,
+ boundary->edges_capacity * sizeof(SculptBoundaryPreviewEdge),
+ "boundary edges");
}
};
@@ -199,7 +204,7 @@ static bool sculpt_boundary_is_vertex_in_editable_boundary(SculptSession *ss,
*/
typedef struct BoundaryFloodFillData {
- SculptBoundary *bdata;
+ SculptBoundary *boundary;
GSet *included_vertices;
EdgeSet *preview_edges;
@@ -211,11 +216,16 @@ static bool boundary_floodfill_cb(
SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata)
{
BoundaryFloodFillData *data = userdata;
- SculptBoundary *bdata = data->bdata;
+ SculptBoundary *boundary = data->boundary;
if (SCULPT_vertex_is_boundary(ss, to_v)) {
- sculpt_boundary_index_add(bdata, to_v, data->included_vertices);
+ const float edge_len = len_v3v3(SCULPT_vertex_co_get(ss, from_v),
+ SCULPT_vertex_co_get(ss, to_v));
+ const float distance_boundary_to_dst = boundary->distance ?
+ boundary->distance[from_v] + edge_len :
+ 0.0f;
+ sculpt_boundary_index_add(boundary, to_v, distance_boundary_to_dst, data->included_vertices);
if (!is_duplicate) {
- sculpt_boundary_preview_edge_add(bdata, from_v, to_v);
+ sculpt_boundary_preview_edge_add(boundary, from_v, to_v);
}
return sculpt_boundary_is_vertex_in_editable_boundary(ss, to_v);
}
@@ -223,26 +233,32 @@ static bool boundary_floodfill_cb(
}
static void sculpt_boundary_indices_init(SculptSession *ss,
- SculptBoundary *bdata,
+ SculptBoundary *boundary,
+ const bool init_boundary_distances,
const int initial_boundary_index)
{
- bdata->vertices = MEM_malloc_arrayN(
+ const int totvert = SCULPT_vertex_count_get(ss);
+ boundary->vertices = MEM_malloc_arrayN(
BOUNDARY_INDICES_BLOCK_SIZE, sizeof(int), "boundary indices");
- bdata->edges = MEM_malloc_arrayN(
+ if (init_boundary_distances) {
+ boundary->distance = MEM_calloc_arrayN(totvert, sizeof(float), "boundary distances");
+ }
+ boundary->edges = MEM_malloc_arrayN(
BOUNDARY_INDICES_BLOCK_SIZE, sizeof(SculptBoundaryPreviewEdge), "boundary edges");
GSet *included_vertices = BLI_gset_int_new_ex("included vertices", BOUNDARY_INDICES_BLOCK_SIZE);
SculptFloodFill flood;
SCULPT_floodfill_init(ss, &flood);
- bdata->initial_vertex = initial_boundary_index;
- copy_v3_v3(bdata->initial_vertex_position, SCULPT_vertex_co_get(ss, bdata->initial_vertex));
- sculpt_boundary_index_add(bdata, initial_boundary_index, included_vertices);
+ boundary->initial_vertex = initial_boundary_index;
+ copy_v3_v3(boundary->initial_vertex_position,
+ SCULPT_vertex_co_get(ss, boundary->initial_vertex));
+ sculpt_boundary_index_add(boundary, initial_boundary_index, 0.0f, included_vertices);
SCULPT_floodfill_add_initial(&flood, initial_boundary_index);
BoundaryFloodFillData fdata = {
- .bdata = bdata,
+ .boundary = boundary,
.included_vertices = included_vertices,
.last_visited_vertex = BOUNDARY_VERTEX_NONE,
@@ -258,8 +274,8 @@ static void sculpt_boundary_indices_init(SculptSession *ss,
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, fdata.last_visited_vertex, ni) {
if (BLI_gset_haskey(included_vertices, POINTER_FROM_INT(ni.index)) &&
sculpt_boundary_is_vertex_in_editable_boundary(ss, ni.index)) {
- sculpt_boundary_preview_edge_add(bdata, fdata.last_visited_vertex, ni.index);
- bdata->forms_loop = true;
+ sculpt_boundary_preview_edge_add(boundary, fdata.last_visited_vertex, ni.index);
+ boundary->forms_loop = true;
}
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
@@ -275,7 +291,7 @@ static void sculpt_boundary_indices_init(SculptSession *ss,
* the closest one.
*/
static void sculpt_boundary_edit_data_init(SculptSession *ss,
- SculptBoundary *bdata,
+ SculptBoundary *boundary,
const int initial_vertex,
const float radius)
{
@@ -283,12 +299,12 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss,
const bool has_duplicates = BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS;
- bdata->edit_info = MEM_malloc_arrayN(
+ boundary->edit_info = MEM_malloc_arrayN(
totvert, sizeof(SculptBoundaryEditInfo), "Boundary edit info");
for (int i = 0; i < totvert; i++) {
- bdata->edit_info[i].original_vertex = BOUNDARY_VERTEX_NONE;
- bdata->edit_info[i].num_propagation_steps = BOUNDARY_STEPS_NONE;
+ boundary->edit_info[i].original_vertex = BOUNDARY_VERTEX_NONE;
+ boundary->edit_info[i].num_propagation_steps = BOUNDARY_STEPS_NONE;
}
GSQueue *current_iteration = BLI_gsqueue_new(sizeof(int));
@@ -297,23 +313,23 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss,
/* Initialized the first iteration with the vertices already in the boundary. This is propagation
* step 0. */
BLI_bitmap *visited_vertices = BLI_BITMAP_NEW(SCULPT_vertex_count_get(ss), "visited_vertices");
- for (int i = 0; i < bdata->num_vertices; i++) {
- bdata->edit_info[bdata->vertices[i]].original_vertex = bdata->vertices[i];
- bdata->edit_info[bdata->vertices[i]].num_propagation_steps = 0;
+ for (int i = 0; i < boundary->num_vertices; i++) {
+ boundary->edit_info[boundary->vertices[i]].original_vertex = boundary->vertices[i];
+ boundary->edit_info[boundary->vertices[i]].num_propagation_steps = 0;
/* This ensures that all duplicate vertices in the boundary have the same original_vertex
* index, so the deformation for them will be the same. */
if (has_duplicates) {
SculptVertexNeighborIter ni_duplis;
- SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, bdata->vertices[i], ni_duplis) {
+ SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, boundary->vertices[i], ni_duplis) {
if (ni_duplis.is_duplicate) {
- bdata->edit_info[ni_duplis.index].original_vertex = bdata->vertices[i];
+ boundary->edit_info[ni_duplis.index].original_vertex = boundary->vertices[i];
}
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni_duplis);
}
- BLI_gsqueue_push(current_iteration, &bdata->vertices[i]);
+ BLI_gsqueue_push(current_iteration, &boundary->vertices[i]);
}
int num_propagation_steps = 0;
@@ -323,7 +339,7 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss,
/* This steps is further away from the boundary than the brush radius, so stop adding more
* steps. */
if (accum_distance > radius) {
- bdata->max_propagation_steps = num_propagation_steps;
+ boundary->max_propagation_steps = num_propagation_steps;
break;
}
@@ -333,19 +349,20 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss,
SculptVertexNeighborIter ni;
SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, from_v, ni) {
- if (bdata->edit_info[ni.index].num_propagation_steps == BOUNDARY_STEPS_NONE) {
- bdata->edit_info[ni.index].original_vertex = bdata->edit_info[from_v].original_vertex;
+ if (boundary->edit_info[ni.index].num_propagation_steps == BOUNDARY_STEPS_NONE) {
+ boundary->edit_info[ni.index].original_vertex =
+ boundary->edit_info[from_v].original_vertex;
BLI_BITMAP_ENABLE(visited_vertices, ni.index);
if (ni.is_duplicate) {
/* Grids duplicates handling. */
- bdata->edit_info[ni.index].num_propagation_steps =
- bdata->edit_info[from_v].num_propagation_steps;
+ boundary->edit_info[ni.index].num_propagation_steps =
+ boundary->edit_info[from_v].num_propagation_steps;
}
else {
- bdata->edit_info[ni.index].num_propagation_steps =
- bdata->edit_info[from_v].num_propagation_steps + 1;
+ boundary->edit_info[ni.index].num_propagation_steps =
+ boundary->edit_info[from_v].num_propagation_steps + 1;
BLI_gsqueue_push(next_iteration, &ni.index);
@@ -357,10 +374,10 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss,
SculptVertexNeighborIter ni_duplis;
SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, ni.index, ni_duplis) {
if (ni_duplis.is_duplicate) {
- bdata->edit_info[ni_duplis.index].original_vertex =
- bdata->edit_info[from_v].original_vertex;
- bdata->edit_info[ni_duplis.index].num_propagation_steps =
- bdata->edit_info[from_v].num_propagation_steps + 1;
+ boundary->edit_info[ni_duplis.index].original_vertex =
+ boundary->edit_info[from_v].original_vertex;
+ boundary->edit_info[ni_duplis.index].num_propagation_steps =
+ boundary->edit_info[from_v].num_propagation_steps + 1;
}
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni_duplis);
@@ -368,9 +385,9 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss,
/* Check the distance using the vertex that was propagated from the initial vertex that
* was used to initialize the boundary. */
- if (bdata->edit_info[from_v].original_vertex == initial_vertex) {
- bdata->pivot_vertex = ni.index;
- copy_v3_v3(bdata->initial_pivot_position, SCULPT_vertex_co_get(ss, ni.index));
+ if (boundary->edit_info[from_v].original_vertex == initial_vertex) {
+ boundary->pivot_vertex = ni.index;
+ copy_v3_v3(boundary->initial_pivot_position, SCULPT_vertex_co_get(ss, ni.index));
accum_distance += len_v3v3(SCULPT_vertex_co_get(ss, from_v),
SCULPT_vertex_co_get(ss, ni.index));
}
@@ -406,23 +423,67 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss,
* on the brush curve and its propagation steps. The falloff goes from the boundary into the mesh.
*/
static void sculpt_boundary_falloff_factor_init(SculptSession *ss,
- SculptBoundary *bdata,
- Brush *brush)
+ SculptBoundary *boundary,
+ Brush *brush,
+ const float radius)
{
const int totvert = SCULPT_vertex_count_get(ss);
BKE_curvemapping_init(brush->curve);
for (int i = 0; i < totvert; i++) {
- if (bdata->edit_info[i].num_propagation_steps != -1) {
- bdata->edit_info[i].strength_factor = BKE_brush_curve_strength(
- brush, bdata->edit_info[i].num_propagation_steps, bdata->max_propagation_steps);
+ if (boundary->edit_info[i].num_propagation_steps != -1) {
+ boundary->edit_info[i].strength_factor = BKE_brush_curve_strength(
+ brush, boundary->edit_info[i].num_propagation_steps, boundary->max_propagation_steps);
+ }
+
+ if (boundary->edit_info[i].original_vertex == boundary->initial_vertex) {
+ /* All vertices that are propagated from the original vertex won't be affected by the
+ * boundary falloff, so there is no need to calculate anything else. */
+ continue;
+ }
+
+ if (!boundary->distance) {
+ /* There are falloff modes that do not require to modify the previously calculated falloff
+ * based on boundary distances. */
+ continue;
+ }
+
+ const float boundary_distance = boundary->distance[boundary->edit_info[i].original_vertex];
+ float falloff_distance = 0.0f;
+ float direction = 1.0f;
+
+ switch (brush->boundary_falloff_type) {
+ case BRUSH_BOUNDARY_FALLOFF_RADIUS:
+ falloff_distance = boundary_distance;
+ break;
+ case BRUSH_BOUNDARY_FALLOFF_LOOP: {
+ const int div = boundary_distance / radius;
+ const float mod = fmodf(boundary_distance, radius);
+ falloff_distance = div % 2 == 0 ? mod : radius - mod;
+ } break;
+ case BRUSH_BOUNDARY_FALLOFF_LOOP_INVERT: {
+ const int div = boundary_distance / radius;
+ const float mod = fmodf(boundary_distance, radius);
+ falloff_distance = div % 2 == 0 ? mod : radius - mod;
+ /* Inverts the faloff in the intervals 1 2 5 6 9 10 ... */
+ if (((div - 1) & 2) == 0) {
+ direction = -1.0f;
+ }
+ } break;
+ case BRUSH_BOUNDARY_FALLOFF_CONSTANT:
+ /* For constant falloff distances are not allocated, so this should never happen. */
+ BLI_assert(false);
}
+
+ boundary->edit_info[i].strength_factor *= direction * BKE_brush_curve_strength(
+ brush, falloff_distance, radius);
}
}
/* Main function to get SculptBoundary data both for brush deformation and viewport preview. Can
* return NULL if there is no boundary from the given vertex using the given radius. */
SculptBoundary *SCULPT_boundary_data_init(Object *object,
+ Brush *brush,
const int initial_vertex,
const float radius)
{
@@ -444,111 +505,118 @@ SculptBoundary *SCULPT_boundary_data_init(Object *object,
return NULL;
}
- SculptBoundary *bdata = MEM_callocN(sizeof(SculptBoundary), "Boundary edit data");
+ SculptBoundary *boundary = MEM_callocN(sizeof(SculptBoundary), "Boundary edit data");
+
+ const bool init_boundary_distances = brush->boundary_falloff_type !=
+ BRUSH_BOUNDARY_FALLOFF_CONSTANT;
+ sculpt_boundary_indices_init(ss, boundary, init_boundary_distances, boundary_initial_vertex);
- sculpt_boundary_indices_init(ss, bdata, boundary_initial_vertex);
- sculpt_boundary_edit_data_init(ss, bdata, boundary_initial_vertex, radius);
+ const float boundary_radius = radius * (1.0f + brush->boundary_offset);
+ sculpt_boundary_edit_data_init(ss, boundary, boundary_initial_vertex, boundary_radius);
- return bdata;
+ return boundary;
}
-void SCULPT_boundary_data_free(SculptBoundary *bdata)
+void SCULPT_boundary_data_free(SculptBoundary *boundary)
{
- MEM_SAFE_FREE(bdata->vertices);
- MEM_SAFE_FREE(bdata->edit_info);
- MEM_SAFE_FREE(bdata->bend.pivot_positions);
- MEM_SAFE_FREE(bdata->bend.pivot_rotation_axis);
- MEM_SAFE_FREE(bdata->slide.directions);
- MEM_SAFE_FREE(bdata);
+ MEM_SAFE_FREE(boundary->vertices);
+ MEM_SAFE_FREE(boundary->distance);
+ MEM_SAFE_FREE(boundary->edit_info);
+ MEM_SAFE_FREE(boundary->bend.pivot_positions);
+ MEM_SAFE_FREE(boundary->bend.pivot_rotation_axis);
+ MEM_SAFE_FREE(boundary->slide.directions);
+ MEM_SAFE_FREE(boundary);
}
/* These functions initialize the required vectors for the desired deformation using the
* SculptBoundaryEditInfo. They calculate the data using the vertices that have the
* max_propagation_steps value and them this data is copied to the rest of the vertices using the
* original vertex index. */
-static void sculpt_boundary_bend_data_init(SculptSession *ss, SculptBoundary *bdata)
+static void sculpt_boundary_bend_data_init(SculptSession *ss, SculptBoundary *boundary)
{
const int totvert = SCULPT_vertex_count_get(ss);
- bdata->bend.pivot_rotation_axis = MEM_calloc_arrayN(
+ boundary->bend.pivot_rotation_axis = MEM_calloc_arrayN(
totvert, 3 * sizeof(float), "pivot rotation axis");
- bdata->bend.pivot_positions = MEM_calloc_arrayN(totvert, 3 * sizeof(float), "pivot positions");
+ boundary->bend.pivot_positions = MEM_calloc_arrayN(
+ totvert, 3 * sizeof(float), "pivot positions");
for (int i = 0; i < totvert; i++) {
- if (bdata->edit_info[i].num_propagation_steps == bdata->max_propagation_steps) {
+ if (boundary->edit_info[i].num_propagation_steps == boundary->max_propagation_steps) {
float dir[3];
float normal[3];
SCULPT_vertex_normal_get(ss, i, normal);
sub_v3_v3v3(dir,
- SCULPT_vertex_co_get(ss, bdata->edit_info[i].original_vertex),
+ SCULPT_vertex_co_get(ss, boundary->edit_info[i].original_vertex),
SCULPT_vertex_co_get(ss, i));
cross_v3_v3v3(
- bdata->bend.pivot_rotation_axis[bdata->edit_info[i].original_vertex], dir, normal);
- normalize_v3(bdata->bend.pivot_rotation_axis[bdata->edit_info[i].original_vertex]);
- copy_v3_v3(bdata->bend.pivot_positions[bdata->edit_info[i].original_vertex],
+ boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex], dir, normal);
+ normalize_v3(boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex]);
+ copy_v3_v3(boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex],
SCULPT_vertex_co_get(ss, i));
}
}
for (int i = 0; i < totvert; i++) {
- if (bdata->edit_info[i].num_propagation_steps != BOUNDARY_STEPS_NONE) {
- copy_v3_v3(bdata->bend.pivot_positions[i],
- bdata->bend.pivot_positions[bdata->edit_info[i].original_vertex]);
- copy_v3_v3(bdata->bend.pivot_rotation_axis[i],
- bdata->bend.pivot_rotation_axis[bdata->edit_info[i].original_vertex]);
+ if (boundary->edit_info[i].num_propagation_steps != BOUNDARY_STEPS_NONE) {
+ copy_v3_v3(boundary->bend.pivot_positions[i],
+ boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex]);
+ copy_v3_v3(boundary->bend.pivot_rotation_axis[i],
+ boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex]);
}
}
}
-static void sculpt_boundary_slide_data_init(SculptSession *ss, SculptBoundary *bdata)
+static void sculpt_boundary_slide_data_init(SculptSession *ss, SculptBoundary *boundary)
{
const int totvert = SCULPT_vertex_count_get(ss);
- bdata->slide.directions = MEM_calloc_arrayN(totvert, 3 * sizeof(float), "slide directions");
+ boundary->slide.directions = MEM_calloc_arrayN(totvert, 3 * sizeof(float), "slide directions");
for (int i = 0; i < totvert; i++) {
- if (bdata->edit_info[i].num_propagation_steps == bdata->max_propagation_steps) {
- sub_v3_v3v3(bdata->slide.directions[bdata->edit_info[i].original_vertex],
- SCULPT_vertex_co_get(ss, bdata->edit_info[i].original_vertex),
+ if (boundary->edit_info[i].num_propagation_steps == boundary->max_propagation_steps) {
+ sub_v3_v3v3(boundary->slide.directions[boundary->edit_info[i].original_vertex],
+ SCULPT_vertex_co_get(ss, boundary->edit_info[i].original_vertex),
SCULPT_vertex_co_get(ss, i));
- normalize_v3(bdata->slide.directions[bdata->edit_info[i].original_vertex]);
+ normalize_v3(boundary->slide.directions[boundary->edit_info[i].original_vertex]);
}
}
for (int i = 0; i < totvert; i++) {
- if (bdata->edit_info[i].num_propagation_steps != BOUNDARY_STEPS_NONE) {
- copy_v3_v3(bdata->slide.directions[i],
- bdata->slide.directions[bdata->edit_info[i].original_vertex]);
+ if (boundary->edit_info[i].num_propagation_steps != BOUNDARY_STEPS_NONE) {
+ copy_v3_v3(boundary->slide.directions[i],
+ boundary->slide.directions[boundary->edit_info[i].original_vertex]);
}
}
}
-static void sculpt_boundary_twist_data_init(SculptSession *ss, SculptBoundary *bdata)
+static void sculpt_boundary_twist_data_init(SculptSession *ss, SculptBoundary *boundary)
{
- zero_v3(bdata->twist.pivot_position);
- float(*poly_verts)[3] = MEM_malloc_arrayN(bdata->num_vertices, sizeof(float) * 3, "poly verts");
- for (int i = 0; i < bdata->num_vertices; i++) {
- add_v3_v3(bdata->twist.pivot_position, SCULPT_vertex_co_get(ss, bdata->vertices[i]));
- copy_v3_v3(poly_verts[i], SCULPT_vertex_co_get(ss, bdata->vertices[i]));
+ zero_v3(boundary->twist.pivot_position);
+ float(*poly_verts)[3] = MEM_malloc_arrayN(
+ boundary->num_vertices, sizeof(float) * 3, "poly verts");
+ for (int i = 0; i < boundary->num_vertices; i++) {
+ add_v3_v3(boundary->twist.pivot_position, SCULPT_vertex_co_get(ss, boundary->vertices[i]));
+ copy_v3_v3(poly_verts[i], SCULPT_vertex_co_get(ss, boundary->vertices[i]));
}
- mul_v3_fl(bdata->twist.pivot_position, 1.0f / bdata->num_vertices);
- if (bdata->forms_loop) {
- normal_poly_v3(bdata->twist.rotation_axis, poly_verts, bdata->num_vertices);
+ mul_v3_fl(boundary->twist.pivot_position, 1.0f / boundary->num_vertices);
+ if (boundary->forms_loop) {
+ normal_poly_v3(boundary->twist.rotation_axis, poly_verts, boundary->num_vertices);
}
else {
- sub_v3_v3v3(bdata->twist.rotation_axis,
- SCULPT_vertex_co_get(ss, bdata->pivot_vertex),
- SCULPT_vertex_co_get(ss, bdata->initial_vertex));
- normalize_v3(bdata->twist.rotation_axis);
+ sub_v3_v3v3(boundary->twist.rotation_axis,
+ SCULPT_vertex_co_get(ss, boundary->pivot_vertex),
+ SCULPT_vertex_co_get(ss, boundary->initial_vertex));
+ normalize_v3(boundary->twist.rotation_axis);
}
MEM_freeN(poly_verts);
}
static float sculpt_boundary_displacement_from_grab_delta_get(SculptSession *ss,
- SculptBoundary *bdata)
+ SculptBoundary *boundary)
{
float plane[4];
float pos[3];
float normal[3];
- sub_v3_v3v3(normal, ss->cache->initial_location, bdata->initial_pivot_position);
+ sub_v3_v3v3(normal, ss->cache->initial_location, boundary->initial_pivot_position);
normalize_v3(normal);
plane_from_point_normal_v3(plane, ss->cache->initial_location, normal);
add_v3_v3v3(pos, ss->cache->initial_location, ss->cache->grab_delta_symmetry);
@@ -563,7 +631,9 @@ static void do_boundary_brush_bend_task_cb_ex(void *__restrict userdata,
SculptThreadedTaskData *data = userdata;
SculptSession *ss = data->ob->sculpt;
const int symm_area = ss->cache->mirror_symmetry_pass;
- SculptBoundary *bdata = ss->cache->bdata[symm_area];
+ SculptBoundary *boundary = ss->cache->boundaries[symm_area];
+ const ePaintSymmetryFlags symm = data->sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL;
+ const Brush *brush = data->brush;
const float strength = ss->cache->bstrength;
@@ -571,7 +641,7 @@ static void do_boundary_brush_bend_task_cb_ex(void *__restrict userdata,
SculptOrigVertData orig_data;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
- const float disp = strength * sculpt_boundary_displacement_from_grab_delta_get(ss, bdata);
+ const float disp = strength * sculpt_boundary_displacement_from_grab_delta_get(ss, boundary);
float angle_factor = disp / ss->cache->radius;
/* Angle Snapping when inverting the brush. */
if (ss->cache->invert) {
@@ -582,16 +652,20 @@ static void do_boundary_brush_bend_task_cb_ex(void *__restrict userdata,
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
- if (bdata->edit_info[vd.index].num_propagation_steps != -1) {
+ if (boundary->edit_info[vd.index].num_propagation_steps != -1) {
SCULPT_orig_vert_data_update(&orig_data, &vd);
- const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
- float t_orig_co[3];
- sub_v3_v3v3(t_orig_co, orig_data.co, bdata->bend.pivot_positions[vd.index]);
- rotate_v3_v3v3fl(vd.co,
- t_orig_co,
- bdata->bend.pivot_rotation_axis[vd.index],
- angle * bdata->edit_info[vd.index].strength_factor * mask);
- add_v3_v3(vd.co, bdata->bend.pivot_positions[vd.index]);
+ if (SCULPT_check_vertex_pivot_symmetry(
+ orig_data.co, boundary->initial_vertex_position, symm)) {
+ const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
+ float t_orig_co[3];
+ float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd);
+ sub_v3_v3v3(t_orig_co, orig_data.co, boundary->bend.pivot_positions[vd.index]);
+ rotate_v3_v3v3fl(target_co,
+ t_orig_co,
+ boundary->bend.pivot_rotation_axis[vd.index],
+ angle * boundary->edit_info[vd.index].strength_factor * mask);
+ add_v3_v3(target_co, boundary->bend.pivot_positions[vd.index]);
+ }
}
if (vd.mvert) {
@@ -608,7 +682,9 @@ static void do_boundary_brush_slide_task_cb_ex(void *__restrict userdata,
SculptThreadedTaskData *data = userdata;
SculptSession *ss = data->ob->sculpt;
const int symm_area = ss->cache->mirror_symmetry_pass;
- SculptBoundary *bdata = ss->cache->bdata[symm_area];
+ SculptBoundary *boundary = ss->cache->boundaries[symm_area];
+ const ePaintSymmetryFlags symm = data->sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL;
+ const Brush *brush = data->brush;
const float strength = ss->cache->bstrength;
@@ -616,18 +692,22 @@ static void do_boundary_brush_slide_task_cb_ex(void *__restrict userdata,
SculptOrigVertData orig_data;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
- const float disp = sculpt_boundary_displacement_from_grab_delta_get(ss, bdata);
+ const float disp = sculpt_boundary_displacement_from_grab_delta_get(ss, boundary);
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
- if (bdata->edit_info[vd.index].num_propagation_steps != -1) {
+ if (boundary->edit_info[vd.index].num_propagation_steps != -1) {
SCULPT_orig_vert_data_update(&orig_data, &vd);
- const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
- madd_v3_v3v3fl(vd.co,
- orig_data.co,
- bdata->slide.directions[vd.index],
- bdata->edit_info[vd.index].strength_factor * disp * mask * strength);
+ if (SCULPT_check_vertex_pivot_symmetry(
+ orig_data.co, boundary->initial_vertex_position, symm)) {
+ const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
+ float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd);
+ madd_v3_v3v3fl(target_co,
+ orig_data.co,
+ boundary->slide.directions[vd.index],
+ boundary->edit_info[vd.index].strength_factor * disp * mask * strength);
+ }
}
if (vd.mvert) {
@@ -644,7 +724,9 @@ static void do_boundary_brush_inflate_task_cb_ex(void *__restrict userdata,
SculptThreadedTaskData *data = userdata;
SculptSession *ss = data->ob->sculpt;
const int symm_area = ss->cache->mirror_symmetry_pass;
- SculptBoundary *bdata = ss->cache->bdata[symm_area];
+ SculptBoundary *boundary = ss->cache->boundaries[symm_area];
+ const ePaintSymmetryFlags symm = data->sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL;
+ const Brush *brush = data->brush;
const float strength = ss->cache->bstrength;
@@ -652,20 +734,24 @@ static void do_boundary_brush_inflate_task_cb_ex(void *__restrict userdata,
SculptOrigVertData orig_data;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
- const float disp = sculpt_boundary_displacement_from_grab_delta_get(ss, bdata);
+ const float disp = sculpt_boundary_displacement_from_grab_delta_get(ss, boundary);
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
- if (bdata->edit_info[vd.index].num_propagation_steps != -1) {
+ if (boundary->edit_info[vd.index].num_propagation_steps != -1) {
SCULPT_orig_vert_data_update(&orig_data, &vd);
- const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
- float normal[3];
- normal_short_to_float_v3(normal, orig_data.no);
- madd_v3_v3v3fl(vd.co,
- orig_data.co,
- normal,
- bdata->edit_info[vd.index].strength_factor * disp * mask * strength);
+ if (SCULPT_check_vertex_pivot_symmetry(
+ orig_data.co, boundary->initial_vertex_position, symm)) {
+ const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
+ float normal[3];
+ normal_short_to_float_v3(normal, orig_data.no);
+ float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd);
+ madd_v3_v3v3fl(target_co,
+ orig_data.co,
+ normal,
+ boundary->edit_info[vd.index].strength_factor * disp * mask * strength);
+ }
}
if (vd.mvert) {
@@ -682,7 +768,9 @@ static void do_boundary_brush_grab_task_cb_ex(void *__restrict userdata,
SculptThreadedTaskData *data = userdata;
SculptSession *ss = data->ob->sculpt;
const int symm_area = ss->cache->mirror_symmetry_pass;
- SculptBoundary *bdata = ss->cache->bdata[symm_area];
+ SculptBoundary *boundary = ss->cache->boundaries[symm_area];
+ const ePaintSymmetryFlags symm = data->sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL;
+ const Brush *brush = data->brush;
const float strength = ss->cache->bstrength;
@@ -693,13 +781,17 @@ static void do_boundary_brush_grab_task_cb_ex(void *__restrict userdata,
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
- if (bdata->edit_info[vd.index].num_propagation_steps != -1) {
+ if (boundary->edit_info[vd.index].num_propagation_steps != -1) {
SCULPT_orig_vert_data_update(&orig_data, &vd);
- const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
- madd_v3_v3v3fl(vd.co,
- orig_data.co,
- ss->cache->grab_delta_symmetry,
- bdata->edit_info[vd.index].strength_factor * mask * strength);
+ if (SCULPT_check_vertex_pivot_symmetry(
+ orig_data.co, boundary->initial_vertex_position, symm)) {
+ const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
+ float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd);
+ madd_v3_v3v3fl(target_co,
+ orig_data.co,
+ ss->cache->grab_delta_symmetry,
+ boundary->edit_info[vd.index].strength_factor * mask * strength);
+ }
}
if (vd.mvert) {
@@ -716,7 +808,9 @@ static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata,
SculptThreadedTaskData *data = userdata;
SculptSession *ss = data->ob->sculpt;
const int symm_area = ss->cache->mirror_symmetry_pass;
- SculptBoundary *bdata = ss->cache->bdata[symm_area];
+ SculptBoundary *boundary = ss->cache->boundaries[symm_area];
+ const ePaintSymmetryFlags symm = data->sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL;
+ const Brush *brush = data->brush;
const float strength = ss->cache->bstrength;
@@ -724,7 +818,7 @@ static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata,
SculptOrigVertData orig_data;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
- const float disp = strength * sculpt_boundary_displacement_from_grab_delta_get(ss, bdata);
+ const float disp = strength * sculpt_boundary_displacement_from_grab_delta_get(ss, boundary);
float angle_factor = disp / ss->cache->radius;
/* Angle Snapping when inverting the brush. */
if (ss->cache->invert) {
@@ -735,16 +829,20 @@ static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata,
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
- if (bdata->edit_info[vd.index].num_propagation_steps != -1) {
- const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
+ if (boundary->edit_info[vd.index].num_propagation_steps != -1) {
SCULPT_orig_vert_data_update(&orig_data, &vd);
- float t_orig_co[3];
- sub_v3_v3v3(t_orig_co, orig_data.co, bdata->twist.pivot_position);
- rotate_v3_v3v3fl(vd.co,
- t_orig_co,
- bdata->twist.rotation_axis,
- angle * mask * bdata->edit_info[vd.index].strength_factor);
- add_v3_v3(vd.co, bdata->twist.pivot_position);
+ if (SCULPT_check_vertex_pivot_symmetry(
+ orig_data.co, boundary->initial_vertex_position, symm)) {
+ const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
+ float t_orig_co[3];
+ float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd);
+ sub_v3_v3v3(t_orig_co, orig_data.co, boundary->twist.pivot_position);
+ rotate_v3_v3v3fl(target_co,
+ t_orig_co,
+ boundary->twist.rotation_axis,
+ angle * mask * boundary->edit_info[vd.index].strength_factor);
+ add_v3_v3(target_co, boundary->twist.pivot_position);
+ }
}
if (vd.mvert) {
@@ -774,20 +872,20 @@ void SCULPT_do_boundary_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totn
sd, ob, location, ss->cache->radius_squared, false);
}
- ss->cache->bdata[symm_area] = SCULPT_boundary_data_init(
- ob, initial_vertex, ss->cache->initial_radius);
+ ss->cache->boundaries[symm_area] = SCULPT_boundary_data_init(
+ ob, brush, initial_vertex, ss->cache->initial_radius);
- if (ss->cache->bdata[symm_area]) {
+ if (ss->cache->boundaries[symm_area]) {
switch (brush->boundary_deform_type) {
case BRUSH_BOUNDARY_DEFORM_BEND:
- sculpt_boundary_bend_data_init(ss, ss->cache->bdata[symm_area]);
+ sculpt_boundary_bend_data_init(ss, ss->cache->boundaries[symm_area]);
break;
case BRUSH_BOUNDARY_DEFORM_EXPAND:
- sculpt_boundary_slide_data_init(ss, ss->cache->bdata[symm_area]);
+ sculpt_boundary_slide_data_init(ss, ss->cache->boundaries[symm_area]);
break;
case BRUSH_BOUNDARY_DEFORM_TWIST:
- sculpt_boundary_twist_data_init(ss, ss->cache->bdata[symm_area]);
+ sculpt_boundary_twist_data_init(ss, ss->cache->boundaries[symm_area]);
break;
case BRUSH_BOUNDARY_DEFORM_INFLATE:
case BRUSH_BOUNDARY_DEFORM_GRAB:
@@ -795,12 +893,13 @@ void SCULPT_do_boundary_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totn
break;
}
- sculpt_boundary_falloff_factor_init(ss, ss->cache->bdata[symm_area], brush);
+ sculpt_boundary_falloff_factor_init(
+ ss, ss->cache->boundaries[symm_area], brush, ss->cache->initial_radius);
}
}
/* No active boundary under the cursor. */
- if (!ss->cache->bdata[symm_area]) {
+ if (!ss->cache->boundaries[symm_area]) {
return;
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c
index 2637cb45906..c3666c8aaad 100644
--- a/source/blender/editors/sculpt_paint/sculpt_cloth.c
+++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c
@@ -134,6 +134,7 @@ static float cloth_brush_simulation_falloff_get(const Brush *brush,
#define CLOTH_SIMULATION_ITERATIONS 5
#define CLOTH_MAX_CONSTRAINTS_PER_VERTEX 1024
#define CLOTH_SIMULATION_TIME_STEP 0.01f
+#define CLOTH_DEFORMATION_TARGET_STRENGTH 0.35f
static bool cloth_brush_sim_has_length_constraint(SculptClothSimulation *cloth_sim,
const int v1,
@@ -168,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));
@@ -200,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;
@@ -219,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];
@@ -297,15 +304,34 @@ static void do_cloth_brush_build_constraints_task_cb_ex(
}
}
- if (cloth_is_deform_brush && len_squared < radius_squared) {
- 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);
+ 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 (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
+ * their own code when modifying the deformation coordinates of the simulation, so
+ * deformation constraints are created with a fixed strength for all vertices. */
+ cloth_brush_add_deformation_constraint(
+ data->cloth_sim, vd.index, CLOTH_DEFORMATION_TARGET_STRENGTH);
}
if (pin_simulation_boundary) {
const float sim_falloff = cloth_brush_simulation_falloff_get(
brush, ss->cache->initial_radius, ss->cache->location, vd.co);
- /* Vertex is inside the area of the simulation without any falloff aplied. */
+ /* Vertex is inside the area of the simulation without any falloff applied. */
if (sim_falloff < 1.0f) {
/* Create constraints with more strength the closer the vertex is to the simulation
* boundary. */
@@ -383,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);
}
@@ -442,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);
@@ -505,49 +537,6 @@ static ListBase *cloth_brush_collider_cache_create(Depsgraph *depsgraph)
return cache;
}
-static SculptClothSimulation *cloth_brush_simulation_create(SculptSession *ss,
- Brush *brush,
- const float cloth_mass,
- const float cloth_damping,
- const bool use_collisions)
-{
- const int totverts = SCULPT_vertex_count_get(ss);
- SculptClothSimulation *cloth_sim;
-
- cloth_sim = MEM_callocN(sizeof(SculptClothSimulation), "cloth constraints");
-
- cloth_sim->length_constraints = MEM_callocN(sizeof(SculptClothLengthConstraint) *
- CLOTH_LENGTH_CONSTRAINTS_BLOCK,
- "cloth length constraints");
- cloth_sim->capacity_length_constraints = CLOTH_LENGTH_CONSTRAINTS_BLOCK;
-
- cloth_sim->acceleration = MEM_calloc_arrayN(
- totverts, sizeof(float[3]), "cloth sim acceleration");
- cloth_sim->pos = MEM_calloc_arrayN(totverts, sizeof(float[3]), "cloth sim pos");
- cloth_sim->prev_pos = MEM_calloc_arrayN(totverts, sizeof(float[3]), "cloth sim prev pos");
- cloth_sim->last_iteration_pos = MEM_calloc_arrayN(
- totverts, sizeof(float[3]), "cloth sim last iteration pos");
- cloth_sim->init_pos = MEM_calloc_arrayN(totverts, sizeof(float[3]), "cloth sim init pos");
- cloth_sim->length_constraint_tweak = MEM_calloc_arrayN(
- totverts, sizeof(float), "cloth sim length tweak");
-
- /* Brush can be NULL for tools that need the solver but don't rely on constraint to deformation
- * positions. */
- 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->mass = cloth_mass;
- cloth_sim->damping = cloth_damping;
-
- if (use_collisions) {
- cloth_sim->collider_list = cloth_brush_collider_cache_create(ss->depsgraph);
- }
-
- return cloth_sim;
-}
-
typedef struct ClothBrushCollision {
CollisionModifierData *col_data;
struct IsectRayPrecalc isect_precalc;
@@ -699,43 +688,6 @@ static void do_cloth_brush_solve_simulation_task_cb_ex(
BKE_pbvh_vertex_iter_end;
}
-static void cloth_brush_build_nodes_constraints(
- Sculpt *sd,
- Object *ob,
- PBVHNode **nodes,
- int totnode,
- SculptClothSimulation *cloth_sim,
- /* Cannot be const, because it is assigned to a non-const variable.
- * NOLINTNEXTLINE: readability-non-const-parameter. */
- float initial_location[3],
- const float radius)
-{
- Brush *brush = BKE_paint_brush(&sd->paint);
-
- /* TODO: Multi-threaded needs to be disabled for this task until implementing the optimization of
- * storing the constraints per node. */
- /* Currently all constrains are added to the same global array which can't be accessed from
- * different threads. */
- TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, false, totnode);
-
- cloth_sim->created_length_constraints = BLI_edgeset_new("created length constraints");
-
- SculptThreadedTaskData build_constraints_data = {
- .sd = sd,
- .ob = ob,
- .brush = brush,
- .nodes = nodes,
- .cloth_sim = cloth_sim,
- .cloth_sim_initial_location = initial_location,
- .cloth_sim_radius = radius,
- };
- BLI_task_parallel_range(
- 0, totnode, &build_constraints_data, do_cloth_brush_build_constraints_task_cb_ex, &settings);
-
- BLI_edgeset_free(cloth_sim->created_length_constraints);
-}
-
static void cloth_brush_satisfy_constraints(SculptSession *ss,
Brush *brush,
SculptClothSimulation *cloth_sim)
@@ -784,19 +736,27 @@ 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);
}
}
}
}
-static void cloth_brush_do_simulation_step(
+void SCULPT_cloth_brush_do_simulation_step(
Sculpt *sd, Object *ob, SculptClothSimulation *cloth_sim, PBVHNode **nodes, int totnode)
{
SculptSession *ss = ob->sculpt;
@@ -890,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(
@@ -897,13 +866,116 @@ static void cloth_brush_apply_brush_foces(Sculpt *sd, Object *ob, PBVHNode **nod
}
/* Public functions. */
+SculptClothSimulation *SCULPT_cloth_brush_simulation_create(SculptSession *ss,
+ Brush *brush,
+ const float cloth_mass,
+ const float cloth_damping,
+ const bool use_collisions)
+{
+ const int totverts = SCULPT_vertex_count_get(ss);
+ SculptClothSimulation *cloth_sim;
+
+ cloth_sim = MEM_callocN(sizeof(SculptClothSimulation), "cloth constraints");
+
+ cloth_sim->length_constraints = MEM_callocN(sizeof(SculptClothLengthConstraint) *
+ CLOTH_LENGTH_CONSTRAINTS_BLOCK,
+ "cloth length constraints");
+ cloth_sim->capacity_length_constraints = CLOTH_LENGTH_CONSTRAINTS_BLOCK;
+
+ cloth_sim->acceleration = MEM_calloc_arrayN(
+ totverts, sizeof(float[3]), "cloth sim acceleration");
+ cloth_sim->pos = MEM_calloc_arrayN(totverts, sizeof(float[3]), "cloth sim pos");
+ cloth_sim->prev_pos = MEM_calloc_arrayN(totverts, sizeof(float[3]), "cloth sim prev pos");
+ cloth_sim->last_iteration_pos = MEM_calloc_arrayN(
+ totverts, sizeof(float[3]), "cloth sim last iteration pos");
+ cloth_sim->init_pos = MEM_calloc_arrayN(totverts, sizeof(float[3]), "cloth sim init pos");
+ cloth_sim->length_constraint_tweak = MEM_calloc_arrayN(
+ totverts, sizeof(float), "cloth sim length tweak");
+
+ /* Brush can be NULL for tools that need the solver but don't rely on constraint to deformation
+ * positions. */
+ 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;
+ cloth_sim->damping = cloth_damping;
+
+ if (use_collisions) {
+ cloth_sim->collider_list = cloth_brush_collider_cache_create(ss->depsgraph);
+ }
+
+ return cloth_sim;
+}
+
+void SCULPT_cloth_brush_build_nodes_constraints(
+ Sculpt *sd,
+ Object *ob,
+ PBVHNode **nodes,
+ int totnode,
+ SculptClothSimulation *cloth_sim,
+ /* Cannot be const, because it is assigned to a non-const variable.
+ * NOLINTNEXTLINE: readability-non-const-parameter. */
+ float initial_location[3],
+ const float radius)
+{
+ Brush *brush = BKE_paint_brush(&sd->paint);
+
+ /* TODO: Multi-threaded needs to be disabled for this task until implementing the optimization of
+ * storing the constraints per node. */
+ /* Currently all constrains are added to the same global array which can't be accessed from
+ * different threads. */
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, false, totnode);
+
+ cloth_sim->created_length_constraints = BLI_edgeset_new("created length constraints");
+
+ SculptThreadedTaskData build_constraints_data = {
+ .sd = sd,
+ .ob = ob,
+ .brush = brush,
+ .nodes = nodes,
+ .cloth_sim = cloth_sim,
+ .cloth_sim_initial_location = initial_location,
+ .cloth_sim_radius = radius,
+ };
+ BLI_task_parallel_range(
+ 0, totnode, &build_constraints_data, do_cloth_brush_build_constraints_task_cb_ex, &settings);
+
+ BLI_edgeset_free(cloth_sim->created_length_constraints);
+}
+
+void SCULPT_cloth_brush_simulation_init(SculptSession *ss, SculptClothSimulation *cloth_sim)
+{
+ const int totverts = SCULPT_vertex_count_get(ss);
+ const bool has_deformation_pos = cloth_sim->deformation_pos != NULL;
+ for (int i = 0; i < totverts; i++) {
+ copy_v3_v3(cloth_sim->last_iteration_pos[i], SCULPT_vertex_co_get(ss, i));
+ copy_v3_v3(cloth_sim->init_pos[i], SCULPT_vertex_co_get(ss, i));
+ copy_v3_v3(cloth_sim->prev_pos[i], SCULPT_vertex_co_get(ss, i));
+ 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;
+ }
+ }
+}
+
+void SCULPT_cloth_brush_store_simulation_state(SculptSession *ss, SculptClothSimulation *cloth_sim)
+{
+ const int totverts = SCULPT_vertex_count_get(ss);
+ for (int i = 0; i < totverts; i++) {
+ copy_v3_v3(cloth_sim->pos[i], SCULPT_vertex_co_get(ss, i));
+ }
+}
/* Main Brush Function. */
void SCULPT_do_cloth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
- const int totverts = SCULPT_vertex_count_get(ss);
/* In the first brush step of each symmetry pass, build the constraints for the vertices in all
* nodes inside the simulation's limits. */
@@ -914,42 +986,32 @@ void SCULPT_do_cloth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
/* The simulation structure only needs to be created on the first symmetry pass. */
if (SCULPT_stroke_is_first_brush_step(ss->cache) || !ss->cache->cloth_sim) {
- const bool is_cloth_deform_brush = SCULPT_is_cloth_deform_brush(brush);
- ss->cache->cloth_sim = cloth_brush_simulation_create(
+ ss->cache->cloth_sim = SCULPT_cloth_brush_simulation_create(
ss,
brush,
brush->cloth_mass,
brush->cloth_damping,
(brush->flag2 & BRUSH_CLOTH_USE_COLLISION));
- for (int i = 0; i < totverts; i++) {
- copy_v3_v3(ss->cache->cloth_sim->last_iteration_pos[i], SCULPT_vertex_co_get(ss, i));
- copy_v3_v3(ss->cache->cloth_sim->init_pos[i], SCULPT_vertex_co_get(ss, i));
- copy_v3_v3(ss->cache->cloth_sim->prev_pos[i], SCULPT_vertex_co_get(ss, i));
- if (is_cloth_deform_brush) {
- copy_v3_v3(ss->cache->cloth_sim->deformation_pos[i], SCULPT_vertex_co_get(ss, i));
- }
- }
+ SCULPT_cloth_brush_simulation_init(ss, ss->cache->cloth_sim);
}
/* Build the constraints. */
const float radius = ss->cache->initial_radius;
const float limit = radius + (radius * brush->cloth_sim_limit);
- cloth_brush_build_nodes_constraints(
+ SCULPT_cloth_brush_build_nodes_constraints(
sd, ob, nodes, totnode, ss->cache->cloth_sim, ss->cache->location, limit);
return;
}
/* Store the initial state in the simulation. */
- for (int i = 0; i < totverts; i++) {
- copy_v3_v3(ss->cache->cloth_sim->pos[i], SCULPT_vertex_co_get(ss, i));
- }
+ SCULPT_cloth_brush_store_simulation_state(ss, ss->cache->cloth_sim);
/* Apply forces to the vertices. */
cloth_brush_apply_brush_foces(sd, ob, nodes, totnode);
/* Update and write the simulation to the nodes. */
- cloth_brush_do_simulation_step(sd, ob, ss->cache->cloth_sim, nodes, totnode);
+ SCULPT_cloth_brush_do_simulation_step(sd, ob, ss->cache->cloth_sim, nodes, totnode);
}
void SCULPT_cloth_simulation_free(struct SculptClothSimulation *cloth_sim)
@@ -962,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);
}
@@ -1043,11 +1106,39 @@ static EnumPropertyItem prop_cloth_filter_type[] = {
{CLOTH_FILTER_GRAVITY, "GRAVITY", 0, "Gravity", "Applies gravity to the simulation"},
{CLOTH_FILTER_INFLATE, "INFLATE", 0, "Inflate", "Inflates the cloth"},
{CLOTH_FILTER_EXPAND, "EXPAND", 0, "Expand", "Expands the cloth's dimensions"},
- {CLOTH_FILTER_PINCH,
- "PINCH",
+ {CLOTH_FILTER_PINCH, "PINCH", 0, "Pinch", "Pulls the cloth to the cursor's start position"},
+ {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,
- "Pinch",
- "Pinches the cloth to the point were the cursor was when the filter started"},
+ "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,
+ CLOTH_FILTER_FORCE_Z = 1 << 2,
+} eClothFilterForceAxis;
+
+static EnumPropertyItem prop_cloth_filter_force_axis_items[] = {
+ {CLOTH_FILTER_FORCE_X, "X", 0, "X", "Apply force in the X axis"},
+ {CLOTH_FILTER_FORCE_Y, "Y", 0, "Y", "Apply force in the Y axis"},
+ {CLOTH_FILTER_FORCE_Z, "Z", 0, "Z", "Apply force in the Z axis"},
{0, NULL, 0, NULL, NULL},
};
@@ -1087,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];
@@ -1105,6 +1204,14 @@ 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);
cloth_brush_apply_force_to_vertex(ss, cloth_sim, force, vd.index);
@@ -1161,7 +1268,7 @@ static int sculpt_cloth_filter_modal(bContext *C, wmOperator *op, const wmEvent
0, ss->filter_cache->totnode, &data, cloth_filter_apply_forces_task_cb, &settings);
/* Update and write the simulation to the nodes. */
- cloth_brush_do_simulation_step(
+ SCULPT_cloth_brush_do_simulation_step(
sd, ob, ss->filter_cache->cloth_sim, ss->filter_cache->nodes, ss->filter_cache->totnode);
if (ss->deform_modifiers_active || ss->shapekey_active) {
@@ -1191,31 +1298,26 @@ static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
SCULPT_undo_push_begin("Cloth filter");
- SCULPT_filter_cache_init(ob, sd, SCULPT_UNDO_COORDS);
+ SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS);
const float cloth_mass = RNA_float_get(op->ptr, "cloth_mass");
const float cloth_damping = RNA_float_get(op->ptr, "cloth_damping");
const bool use_collisions = RNA_boolean_get(op->ptr, "use_collisions");
- ss->filter_cache->cloth_sim = cloth_brush_simulation_create(
+ ss->filter_cache->cloth_sim = SCULPT_cloth_brush_simulation_create(
ss, NULL, cloth_mass, cloth_damping, use_collisions);
copy_v3_v3(ss->filter_cache->cloth_sim_pinch_point, SCULPT_active_vertex_co_get(ss));
- const int totverts = SCULPT_vertex_count_get(ss);
- for (int i = 0; i < totverts; i++) {
- copy_v3_v3(ss->filter_cache->cloth_sim->last_iteration_pos[i], SCULPT_vertex_co_get(ss, i));
- copy_v3_v3(ss->filter_cache->cloth_sim->prev_pos[i], SCULPT_vertex_co_get(ss, i));
- copy_v3_v3(ss->filter_cache->cloth_sim->init_pos[i], SCULPT_vertex_co_get(ss, i));
- }
+ SCULPT_cloth_brush_simulation_init(ss, ss->filter_cache->cloth_sim);
float origin[3] = {0.0f, 0.0f, 0.0f};
- cloth_brush_build_nodes_constraints(sd,
- ob,
- ss->filter_cache->nodes,
- ss->filter_cache->totnode,
- ss->filter_cache->cloth_sim,
- origin,
- FLT_MAX);
+ SCULPT_cloth_brush_build_nodes_constraints(sd,
+ ob,
+ ss->filter_cache->nodes,
+ ss->filter_cache->totnode,
+ ss->filter_cache->cloth_sim,
+ origin,
+ FLT_MAX);
const bool use_face_sets = RNA_boolean_get(op->ptr, "use_face_sets");
if (use_face_sets) {
@@ -1225,6 +1327,14 @@ static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent
ss->filter_cache->active_face_set = SCULPT_FACE_SET_NONE;
}
+ const int force_axis = RNA_enum_get(op->ptr, "force_axis");
+ ss->filter_cache->enabled_force_axis[0] = force_axis & CLOTH_FILTER_FORCE_X;
+ 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;
}
@@ -1252,6 +1362,18 @@ void SCULPT_OT_cloth_filter(struct wmOperatorType *ot)
"Operation that is going to be applied to the mesh");
RNA_def_float(
ot->srna, "strength", 1.0f, -10.0f, 10.0f, "Strength", "Filter Strength", -10.0f, 10.0f);
+ RNA_def_enum_flag(ot->srna,
+ "force_axis",
+ prop_cloth_filter_force_axis_items,
+ 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_face_set.c b/source/blender/editors/sculpt_paint/sculpt_face_set.c
index 2afa3556dd9..b9265380a35 100644
--- a/source/blender/editors/sculpt_paint/sculpt_face_set.c
+++ b/source/blender/editors/sculpt_paint/sculpt_face_set.c
@@ -71,6 +71,37 @@
#include <math.h>
#include <stdlib.h>
+/* Utils. */
+int ED_sculpt_face_sets_find_next_available_id(struct Mesh *mesh)
+{
+ int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS);
+ if (!face_sets) {
+ return SCULPT_FACE_SET_NONE;
+ }
+
+ int next_face_set_id = 0;
+ for (int i = 0; i < mesh->totpoly; i++) {
+ next_face_set_id = max_ii(next_face_set_id, abs(face_sets[i]));
+ }
+ next_face_set_id++;
+
+ return next_face_set_id;
+}
+
+void ED_sculpt_face_sets_initialize_none_to_id(struct Mesh *mesh, const int new_id)
+{
+ int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS);
+ if (!face_sets) {
+ return;
+ }
+
+ for (int i = 0; i < mesh->totpoly; i++) {
+ if (face_sets[i] == SCULPT_FACE_SET_NONE) {
+ face_sets[i] = new_id;
+ }
+ }
+}
+
/* Draw Face Sets Brush. */
static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata,
@@ -901,6 +932,25 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
+static int sculpt_face_sets_change_visibility_invoke(bContext *C,
+ wmOperator *op,
+ const wmEvent *event)
+{
+ Object *ob = CTX_data_active_object(C);
+ SculptSession *ss = ob->sculpt;
+
+ /* Update the active vertex and Face Set using the cursor position to avoid relying on the paint
+ * cursor updates. */
+ SculptCursorGeometryInfo sgi;
+ float mouse[2];
+ mouse[0] = event->mval[0];
+ mouse[1] = event->mval[1];
+ SCULPT_vertex_random_access_ensure(ss);
+ SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false);
+
+ return sculpt_face_sets_change_visibility_exec(C, op);
+}
+
void SCULPT_OT_face_sets_change_visibility(wmOperatorType *ot)
{
/* Identifiers. */
@@ -910,6 +960,7 @@ void SCULPT_OT_face_sets_change_visibility(wmOperatorType *ot)
/* Api callbacks. */
ot->exec = sculpt_face_sets_change_visibility_exec;
+ ot->invoke = sculpt_face_sets_change_visibility_invoke;
ot->poll = SCULPT_mode_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c
index 576536cac03..c5acf736f3e 100644
--- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c
+++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c
@@ -289,7 +289,7 @@ static int sculpt_color_filter_invoke(bContext *C, wmOperator *op, const wmEvent
return OPERATOR_CANCELLED;
}
- SCULPT_filter_cache_init(ob, sd, SCULPT_UNDO_COLOR);
+ SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COLOR);
WM_event_add_modal_handler(C, op);
return OPERATOR_RUNNING_MODAL;
diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
index f9ae91fce7f..619a1b975b6 100644
--- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
+++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
@@ -50,6 +50,7 @@
#include "ED_object.h"
#include "ED_screen.h"
#include "ED_sculpt.h"
+#include "ED_view3d.h"
#include "paint_intern.h"
#include "sculpt_intern.h"
@@ -63,6 +64,39 @@
#include <math.h>
#include <stdlib.h>
+/* Filter orientation utils. */
+void SCULPT_filter_to_orientation_space(float r_v[3], struct FilterCache *filter_cache)
+{
+ switch (filter_cache->orientation) {
+ case SCULPT_FILTER_ORIENTATION_LOCAL:
+ /* Do nothing, Sculpt Mode already works in object space. */
+ break;
+ case SCULPT_FILTER_ORIENTATION_WORLD:
+ mul_mat3_m4_v3(filter_cache->obmat, r_v);
+ break;
+ case SCULPT_FILTER_ORIENTATION_VIEW:
+ mul_mat3_m4_v3(filter_cache->obmat, r_v);
+ mul_mat3_m4_v3(filter_cache->viewmat, r_v);
+ break;
+ }
+}
+
+void SCULPT_filter_to_object_space(float r_v[3], struct FilterCache *filter_cache)
+{
+ switch (filter_cache->orientation) {
+ case SCULPT_FILTER_ORIENTATION_LOCAL:
+ /* Do nothing, Sculpt Mode already works in object space. */
+ break;
+ case SCULPT_FILTER_ORIENTATION_WORLD:
+ mul_mat3_m4_v3(filter_cache->obmat_inv, r_v);
+ break;
+ case SCULPT_FILTER_ORIENTATION_VIEW:
+ mul_mat3_m4_v3(filter_cache->viewmat_inv, r_v);
+ mul_mat3_m4_v3(filter_cache->obmat_inv, r_v);
+ break;
+ }
+}
+
static void filter_cache_init_task_cb(void *__restrict userdata,
const int i,
const TaskParallelTLS *__restrict UNUSED(tls))
@@ -73,7 +107,7 @@ static void filter_cache_init_task_cb(void *__restrict userdata,
SCULPT_undo_push_node(data->ob, node, data->filter_undo_type);
}
-void SCULPT_filter_cache_init(Object *ob, Sculpt *sd, const int undo_type)
+void SCULPT_filter_cache_init(bContext *C, Object *ob, Sculpt *sd, const int undo_type)
{
SculptSession *ss = ob->sculpt;
PBVH *pbvh = ob->sculpt->pbvh;
@@ -117,6 +151,16 @@ void SCULPT_filter_cache_init(Object *ob, Sculpt *sd, const int undo_type)
BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->totnode);
BLI_task_parallel_range(
0, ss->filter_cache->totnode, &data, filter_cache_init_task_cb, &settings);
+
+ /* Setup orientation matrices. */
+ copy_m4_m4(ss->filter_cache->obmat, ob->obmat);
+ invert_m4_m4(ss->filter_cache->obmat_inv, ob->obmat);
+
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ ViewContext vc;
+ ED_view3d_viewcontext_init(C, &vc, depsgraph);
+ copy_m4_m4(ss->filter_cache->viewmat, vc.rv3d->viewmat);
+ copy_m4_m4(ss->filter_cache->viewmat_inv, vc.rv3d->viewinv);
}
void SCULPT_filter_cache_free(SculptSession *ss)
@@ -132,11 +176,12 @@ void SCULPT_filter_cache_free(SculptSession *ss)
MEM_SAFE_FREE(ss->filter_cache->automask);
MEM_SAFE_FREE(ss->filter_cache->surface_smooth_laplacian_disp);
MEM_SAFE_FREE(ss->filter_cache->sharpen_factor);
- MEM_SAFE_FREE(ss->filter_cache->sharpen_detail_directions);
+ MEM_SAFE_FREE(ss->filter_cache->detail_directions);
+ MEM_SAFE_FREE(ss->filter_cache->limit_surface_co);
MEM_SAFE_FREE(ss->filter_cache);
}
-typedef enum eSculptMeshFilterTypes {
+typedef enum eSculptMeshFilterType {
MESH_FILTER_SMOOTH = 0,
MESH_FILTER_SCALE = 1,
MESH_FILTER_INFLATE = 2,
@@ -146,7 +191,9 @@ typedef enum eSculptMeshFilterTypes {
MESH_FILTER_RELAX_FACE_SETS = 6,
MESH_FILTER_SURFACE_SMOOTH = 7,
MESH_FILTER_SHARPEN = 8,
-} eSculptMeshFilterTypes;
+ MESH_FILTER_ENHANCE_DETAILS = 9,
+ MESH_FILTER_ERASE_DISPLACEMENT = 10,
+} eSculptMeshFilterType;
static EnumPropertyItem prop_mesh_filter_types[] = {
{MESH_FILTER_SMOOTH, "SMOOTH", 0, "Smooth", "Smooth mesh"},
@@ -166,6 +213,16 @@ static EnumPropertyItem prop_mesh_filter_types[] = {
"Surface Smooth",
"Smooth the surface of the mesh, preserving the volume"},
{MESH_FILTER_SHARPEN, "SHARPEN", 0, "Sharpen", "Sharpen the cavities of the mesh"},
+ {MESH_FILTER_ENHANCE_DETAILS,
+ "ENHANCE_DETAILS",
+ 0,
+ "Enhance Details",
+ "Enhance the high frequency surface detail"},
+ {MESH_FILTER_ERASE_DISPLACEMENT,
+ "ERASE_DISCPLACEMENT",
+ 0,
+ "Erase Displacement",
+ "Deletes the displacement of the Multires Modifier"},
{0, NULL, 0, NULL, NULL},
};
@@ -182,13 +239,33 @@ static EnumPropertyItem prop_mesh_filter_deform_axis_items[] = {
{0, NULL, 0, NULL, NULL},
};
-static bool sculpt_mesh_filter_needs_pmap(int filter_type, bool use_face_sets)
+static EnumPropertyItem prop_mesh_filter_orientation_items[] = {
+ {SCULPT_FILTER_ORIENTATION_LOCAL,
+ "LOCAL",
+ 0,
+ "Local",
+ "Use the local axis to limit the displacement"},
+ {SCULPT_FILTER_ORIENTATION_WORLD,
+ "WORLD",
+ 0,
+ "World",
+ "Use the global axis to limit the displacement"},
+ {SCULPT_FILTER_ORIENTATION_VIEW,
+ "VIEW",
+ 0,
+ "View",
+ "Use the view axis to limit the displacement"},
+ {0, NULL, 0, NULL, NULL},
+};
+
+static bool sculpt_mesh_filter_needs_pmap(eSculptMeshFilterType filter_type, bool use_face_sets)
{
return use_face_sets || ELEM(filter_type,
MESH_FILTER_SMOOTH,
MESH_FILTER_RELAX,
MESH_FILTER_RELAX_FACE_SETS,
MESH_FILTER_SURFACE_SMOOTH,
+ MESH_FILTER_ENHANCE_DETAILS,
MESH_FILTER_SHARPEN);
}
@@ -200,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]);
@@ -306,7 +383,7 @@ static void mesh_filter_task_cb(void *__restrict userdata,
const uint *hash_co = (const uint *)orig_co;
const uint hash = BLI_hash_int_2d(hash_co[0], hash_co[1]) ^
BLI_hash_int_2d(hash_co[2], ss->filter_cache->random_seed);
- mul_v3_fl(normal, hash * (1.0f / 0xFFFFFFFF) - 0.5f);
+ mul_v3_fl(normal, hash * (1.0f / (float)0xFFFFFFFF) - 0.5f);
mul_v3_v3fl(disp, normal, fade);
break;
}
@@ -362,7 +439,7 @@ static void mesh_filter_task_cb(void *__restrict userdata,
if (ss->filter_cache->sharpen_intensify_detail_strength > 0.0f) {
float detail_strength[3];
normal_short_to_float_v3(detail_strength, orig_data.no);
- copy_v3_v3(detail_strength, ss->filter_cache->sharpen_detail_directions[vd.index]);
+ copy_v3_v3(detail_strength, ss->filter_cache->detail_directions[vd.index]);
madd_v3_v3fl(disp,
detail_strength,
-ss->filter_cache->sharpen_intensify_detail_strength *
@@ -370,13 +447,25 @@ static void mesh_filter_task_cb(void *__restrict userdata,
}
break;
}
+
+ case MESH_FILTER_ENHANCE_DETAILS: {
+ mul_v3_v3fl(disp, ss->filter_cache->detail_directions[vd.index], -fabsf(fade));
+ } break;
+ case MESH_FILTER_ERASE_DISPLACEMENT: {
+ fade = clamp_f(fade, 0.0f, 1.0f);
+ sub_v3_v3v3(disp, ss->filter_cache->limit_surface_co[vd.index], orig_co);
+ mul_v3_fl(disp, fade);
+ break;
+ }
}
+ SCULPT_filter_to_orientation_space(disp, ss->filter_cache);
for (int it = 0; it < 3; it++) {
if (!ss->filter_cache->enabled_axis[it]) {
disp[it] = 0.0f;
}
}
+ SCULPT_filter_to_object_space(disp, ss->filter_cache);
if (ELEM(filter_type, MESH_FILTER_SURFACE_SMOOTH, MESH_FILTER_SHARPEN)) {
madd_v3_v3v3fl(final_pos, vd.co, disp, clamp_f(fade, 0.0f, 1.0f));
@@ -394,32 +483,83 @@ static void mesh_filter_task_cb(void *__restrict userdata,
BKE_pbvh_node_mark_update(node);
}
-static void mesh_filter_sharpen_init_factors(SculptSession *ss)
+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->sharpen_detail_directions[i], avg, SCULPT_vertex_co_get(ss, i));
- ss->filter_cache->sharpen_factor[i] = len_v3(ss->filter_cache->sharpen_detail_directions[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);
+ 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, filter_cache->limit_surface_co[i]);
+ }
+}
+
+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(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};
@@ -428,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->sharpen_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->sharpen_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;
}
}
}
@@ -481,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");
@@ -545,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];
@@ -563,63 +707,57 @@ 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_filter_cache_init(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");
- }
+ SCULPT_undo_push_begin("Mesh Filter");
- 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");
+ SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS);
- ss->filter_cache->sharpen_factor = MEM_mallocN(sizeof(float) * totvert, "sharpen factor");
- ss->filter_cache->sharpen_detail_directions = MEM_malloc_arrayN(
- totvert, sizeof(float[3]), "sharpen detail direction");
+ FilterCache *filter_cache = ss->filter_cache;
+ filter_cache->active_face_set = use_face_sets ? SCULPT_active_face_set_get(ss) :
+ SCULPT_FACE_SET_NONE;
- mesh_filter_sharpen_init_factors(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;
ss->filter_cache->enabled_axis[1] = deform_axis & MESH_FILTER_DEFORM_Y;
ss->filter_cache->enabled_axis[2] = deform_axis & MESH_FILTER_DEFORM_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;
}
@@ -653,6 +791,12 @@ void SCULPT_OT_mesh_filter(struct wmOperatorType *ot)
MESH_FILTER_DEFORM_X | MESH_FILTER_DEFORM_Y | MESH_FILTER_DEFORM_Z,
"Deform axis",
"Apply the deformation in the selected axis");
+ RNA_def_enum(ot->srna,
+ "orientation",
+ prop_mesh_filter_orientation_items,
+ SCULPT_FILTER_ORIENTATION_LOCAL,
+ "Orientation",
+ "Orientation of the axis to limit the filter displacement");
ot->prop = RNA_def_boolean(ot->srna,
"use_face_sets",
false,
diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h
index b11a7005fb5..47a375a2318 100644
--- a/source/blender/editors/sculpt_paint/sculpt_intern.h
+++ b/source/blender/editors/sculpt_paint/sculpt_intern.h
@@ -100,10 +100,16 @@ const float *SCULPT_vertex_color_get(SculptSession *ss, int index);
const float *SCULPT_vertex_persistent_co_get(SculptSession *ss, int index);
void SCULPT_vertex_persistent_normal_get(SculptSession *ss, int index, float no[3]);
-/* Returs the info of the limit surface when Multires is available, otherwise it returns the
+/* Returns the info of the limit surface when Multires is available, otherwise it returns the
* current coordinate of the vertex. */
void SCULPT_vertex_limit_surface_get(SculptSession *ss, int index, float r_co[3]);
+/* Returns the pointer to the coordinates that should be edited from a brush tool iterator
+ * depending on the given deformation target. */
+float *SCULPT_brush_deform_target_vertex_co_get(SculptSession *ss,
+ const int deform_target,
+ PBVHVertexIter *iter);
+
#define SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY 256
typedef struct SculptVertexNeighborIter {
/* Storage */
@@ -337,7 +343,7 @@ float *SCULPT_boundary_automasking_init(Object *ob,
float *automask_factor);
/* Filters. */
-void SCULPT_filter_cache_init(Object *ob, Sculpt *sd, const int undo_type);
+void SCULPT_filter_cache_init(struct bContext *C, Object *ob, Sculpt *sd, const int undo_type);
void SCULPT_filter_cache_free(SculptSession *ss);
void SCULPT_mask_filter_smooth_apply(
@@ -350,8 +356,33 @@ void SCULPT_do_cloth_brush(struct Sculpt *sd,
struct Object *ob,
struct PBVHNode **nodes,
int totnode);
+
void SCULPT_cloth_simulation_free(struct SculptClothSimulation *cloth_sim);
+struct SculptClothSimulation *SCULPT_cloth_brush_simulation_create(struct SculptSession *ss,
+ struct Brush *brush,
+ const float cloth_mass,
+ const float cloth_damping,
+ const bool use_collisions);
+void SCULPT_cloth_brush_simulation_init(struct SculptSession *ss,
+ struct SculptClothSimulation *cloth_sim);
+void SCULPT_cloth_brush_store_simulation_state(struct SculptSession *ss,
+ struct SculptClothSimulation *cloth_sim);
+
+void SCULPT_cloth_brush_do_simulation_step(struct Sculpt *sd,
+ struct Object *ob,
+ struct SculptClothSimulation *cloth_sim,
+ struct PBVHNode **nodes,
+ int totnode);
+
+void SCULPT_cloth_brush_build_nodes_constraints(struct Sculpt *sd,
+ struct Object *ob,
+ struct PBVHNode **nodes,
+ int totnode,
+ struct SculptClothSimulation *cloth_sim,
+ float initial_location[3],
+ const float radius);
+
void SCULPT_cloth_simulation_limits_draw(const uint gpuattr,
const struct Brush *brush,
const float location[3],
@@ -367,8 +398,13 @@ 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 &&
+ brush->deform_target == BRUSH_DEFORM_TARGET_CLOTH_SIM);
}
/* Pose Brush. */
@@ -398,9 +434,10 @@ void SCULPT_pose_ik_chain_free(struct SculptPoseIKChain *ik_chain);
/* Boundary Brush. */
struct SculptBoundary *SCULPT_boundary_data_init(Object *object,
+ Brush *brush,
const int initial_vertex,
const float radius);
-void SCULPT_boundary_data_free(struct SculptBoundary *bdata);
+void SCULPT_boundary_data_free(struct SculptBoundary *boundary);
void SCULPT_do_boundary_brush(struct Sculpt *sd,
struct Object *ob,
struct PBVHNode **nodes,
@@ -864,6 +901,9 @@ typedef struct StrokeCache {
/* Pose brush */
struct SculptPoseIKChain *pose_ik_chain;
+ /* Enhance Details. */
+ float (*detail_directions)[3];
+
/* Clay Thumb brush */
/* Angle of the front tilting plane of the brush to simulate clay accumulation. */
float clay_thumb_front_angle;
@@ -879,7 +919,7 @@ typedef struct StrokeCache {
float true_initial_normal[3];
/* Boundary brush */
- struct SculptBoundary *bdata[PAINT_SYMM_AREAS];
+ struct SculptBoundary *boundaries[PAINT_SYMM_AREAS];
/* Surface Smooth Brush */
/* Stores the displacement produced by the laplacian step of HC smooth. */
@@ -919,8 +959,19 @@ typedef struct StrokeCache {
} StrokeCache;
+/* Sculpt Filters */
+typedef enum SculptFilterOrientation {
+ SCULPT_FILTER_ORIENTATION_LOCAL = 0,
+ SCULPT_FILTER_ORIENTATION_WORLD = 1,
+ SCULPT_FILTER_ORIENTATION_VIEW = 2,
+} SculptFilterOrientation;
+
+void SCULPT_filter_to_orientation_space(float r_v[3], struct FilterCache *filter_cache);
+void SCULPT_filter_to_object_space(float r_v[3], struct FilterCache *filter_cache);
+
typedef struct FilterCache {
bool enabled_axis[3];
+ bool enabled_force_axis[3];
int random_seed;
/* Used for alternating between filter operations in filters that need to apply different ones to
@@ -937,7 +988,17 @@ typedef struct FilterCache {
float sharpen_intensify_detail_strength;
int sharpen_curvature_smooth_iterations;
float *sharpen_factor;
- float (*sharpen_detail_directions)[3];
+ float (*detail_directions)[3];
+
+ /* Filter orientaiton. */
+ SculptFilterOrientation orientation;
+ float obmat[4][4];
+ float obmat_inv[4][4];
+ float viewmat[4][4];
+ float viewmat_inv[4][4];
+
+ /* Displacement eraser. */
+ float (*limit_surface_co)[3];
/* unmasked nodes */
PBVHNode **nodes;
diff --git a/source/blender/editors/sculpt_paint/sculpt_pose.c b/source/blender/editors/sculpt_paint/sculpt_pose.c
index 4d41d069155..e53e33c1186 100644
--- a/source/blender/editors/sculpt_paint/sculpt_pose.c
+++ b/source/blender/editors/sculpt_paint/sculpt_pose.c
@@ -165,6 +165,7 @@ static void do_pose_brush_task_cb_ex(void *__restrict userdata,
SculptSession *ss = data->ob->sculpt;
SculptPoseIKChain *ik_chain = ss->cache->pose_ik_chain;
SculptPoseIKChainSegment *segments = ik_chain->segments;
+ const Brush *brush = data->brush;
PBVHVertexIter vd;
float disp[3], new_co[3];
@@ -206,7 +207,9 @@ static void do_pose_brush_task_cb_ex(void *__restrict userdata,
/* Apply the accumulated displacement to the vertex. */
add_v3_v3v3(final_pos, orig_data.co, total_disp);
- copy_v3_v3(vd.co, final_pos);
+
+ float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd);
+ copy_v3_v3(target_co, final_pos);
if (vd.mvert) {
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.c b/source/blender/editors/sculpt_paint/sculpt_smooth.c
index 2b93298ac4a..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.
@@ -195,6 +210,85 @@ void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], int index
}
}
+static void do_enhance_details_brush_task_cb_ex(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict tls)
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ Sculpt *sd = data->sd;
+ const Brush *brush = data->brush;
+
+ PBVHVertexIter vd;
+
+ float bstrength = ss->cache->bstrength;
+ CLAMP(bstrength, -1.0f, 1.0f);
+
+ SculptBrushTest test;
+ SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
+ ss, &test, data->brush->falloff_shape);
+
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+ BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
+ {
+ if (sculpt_brush_test_sq_fn(&test, vd.co)) {
+ const float fade = bstrength * SCULPT_brush_strength_factor(ss,
+ brush,
+ vd.co,
+ sqrtf(test.dist),
+ vd.no,
+ vd.fno,
+ vd.mask ? *vd.mask : 0.0f,
+ vd.index,
+ thread_id);
+
+ float disp[3];
+ madd_v3_v3v3fl(disp, vd.co, ss->cache->detail_directions[vd.index], fade);
+ SCULPT_clip(sd, ss, vd.co, disp);
+
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+}
+
+static void SCULPT_enhance_details_brush(Sculpt *sd,
+ Object *ob,
+ PBVHNode **nodes,
+ const int totnode)
+{
+ SculptSession *ss = ob->sculpt;
+ Brush *brush = BKE_paint_brush(&sd->paint);
+
+ SCULPT_vertex_random_access_ensure(ss);
+ SCULPT_boundary_info_ensure(ob);
+
+ if (SCULPT_stroke_is_first_brush_step(ss->cache)) {
+ const int totvert = SCULPT_vertex_count_get(ss);
+ ss->cache->detail_directions = MEM_malloc_arrayN(
+ totvert, 3 * sizeof(float), "details directions");
+
+ for (int i = 0; i < totvert; i++) {
+ float avg[3];
+ SCULPT_neighbor_coords_average(ss, avg, i);
+ sub_v3_v3v3(ss->cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, i));
+ }
+ }
+
+ SculptThreadedTaskData data = {
+ .sd = sd,
+ .ob = ob,
+ .brush = brush,
+ .nodes = nodes,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ BLI_task_parallel_range(0, totnode, &data, do_enhance_details_brush_task_cb_ex, &settings);
+}
+
static void do_smooth_brush_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
@@ -237,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);
@@ -300,7 +394,14 @@ void SCULPT_smooth(Sculpt *sd,
void SCULPT_do_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
SculptSession *ss = ob->sculpt;
- SCULPT_smooth(sd, ob, nodes, totnode, ss->cache->bstrength, false);
+ if (ss->cache->bstrength <= 0.0f) {
+ /* Invert mode, intensify details. */
+ SCULPT_enhance_details_brush(sd, ob, nodes, totnode);
+ }
+ else {
+ /* Regular mode, smooth. */
+ SCULPT_smooth(sd, ob, nodes, totnode, ss->cache->bstrength, false);
+ }
}
/* HC Smooth Algorithm. */
diff --git a/source/blender/editors/sculpt_paint/sculpt_transform.c b/source/blender/editors/sculpt_paint/sculpt_transform.c
index 4c54a0465b9..b52b04eba3a 100644
--- a/source/blender/editors/sculpt_paint/sculpt_transform.c
+++ b/source/blender/editors/sculpt_paint/sculpt_transform.c
@@ -76,7 +76,7 @@ void ED_sculpt_init_transform(struct bContext *C)
ss->pivot_rot[3] = 1.0f;
SCULPT_vertex_random_access_ensure(ss);
- SCULPT_filter_cache_init(ob, sd, SCULPT_UNDO_COORDS);
+ SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS);
}
static void sculpt_transform_task_cb(void *__restrict userdata,
@@ -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 050dca07c34..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) {
@@ -306,7 +306,6 @@ static void SOUND_OT_update_animation_flags(wmOperatorType *ot)
static int sound_bake_animation_exec(bContext *C, wmOperator *UNUSED(op))
{
- Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
/* NOTE: We will be forcefully evaluating dependency graph at every frame, so no need to ensure
* current scene state is evaluated as it will be lost anyway. */
@@ -318,11 +317,11 @@ static int sound_bake_animation_exec(bContext *C, wmOperator *UNUSED(op))
for (cfra = (scene->r.sfra > 0) ? (scene->r.sfra - 1) : 0; cfra <= scene->r.efra + 1; cfra++) {
scene->r.cfra = cfra;
- BKE_scene_graph_update_for_newframe(depsgraph, bmain);
+ BKE_scene_graph_update_for_newframe(depsgraph);
}
scene->r.cfra = oldfra;
- BKE_scene_graph_update_for_newframe(depsgraph, bmain);
+ BKE_scene_graph_update_for_newframe(depsgraph);
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/space_action/action_draw.c b/source/blender/editors/space_action/action_draw.c
index ada4243ab4e..8634e5a5f29 100644
--- a/source/blender/editors/space_action/action_draw.c
+++ b/source/blender/editors/space_action/action_draw.c
@@ -169,7 +169,7 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *region
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
/* first backdrop strips */
float ymax = ACHANNEL_FIRST_TOP(ac);
@@ -276,7 +276,7 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *region
}
}
}
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
/* black line marking 'current frame' for Time-Slide transform mode */
if (saction->flag & SACTION_MOVING) {
@@ -558,7 +558,7 @@ void timeline_draw_cache(SpaceAction *saction, Object *ob, Scene *scene)
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
/* Iterate over pointcaches on the active object, and draw each one's range. */
float y_offset = 0.0f;
@@ -577,7 +577,7 @@ void timeline_draw_cache(SpaceAction *saction, Object *ob, Scene *scene)
y_offset += cache_draw_height;
}
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
immUnbindProgram();
BLI_freelistN(&pidlist);
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/CMakeLists.txt b/source/blender/editors/space_buttons/CMakeLists.txt
index 25ff6bbd098..75d91174470 100644
--- a/source/blender/editors/space_buttons/CMakeLists.txt
+++ b/source/blender/editors/space_buttons/CMakeLists.txt
@@ -54,4 +54,9 @@ if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
+if(WITH_EXPERIMENTAL_FEATURES)
+ add_definitions(-DWITH_PARTICLE_NODES)
+ add_definitions(-DWITH_HAIR_NODES)
+endif()
+
blender_add_lib(bf_editor_space_buttons "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c
index 5885d3dcbb0..e567b3ca54c 100644
--- a/source/blender/editors/space_buttons/buttons_context.c
+++ b/source/blender/editors/space_buttons/buttons_context.c
@@ -249,12 +249,16 @@ static bool buttons_context_path_data(ButsContextPath *path, int type)
if (RNA_struct_is_a(ptr->type, &RNA_GreasePencil) && (type == -1 || type == OB_GPENCIL)) {
return true;
}
+#ifdef WITH_HAIR_NODES
if (RNA_struct_is_a(ptr->type, &RNA_Hair) && (type == -1 || type == OB_HAIR)) {
return true;
}
+#endif
+#ifdef WITH_PARTICLE_NODES
if (RNA_struct_is_a(ptr->type, &RNA_PointCloud) && (type == -1 || type == OB_POINTCLOUD)) {
return true;
}
+#endif
if (RNA_struct_is_a(ptr->type, &RNA_Volume) && (type == -1 || type == OB_VOLUME)) {
return true;
}
@@ -791,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,
};
@@ -871,14 +879,18 @@ int buttons_context(const bContext *C, const char *member, bContextDataResult *r
set_pointer_type(path, result, &RNA_LightProbe);
return 1;
}
+#ifdef WITH_HAIR_NODES
if (CTX_data_equals(member, "hair")) {
set_pointer_type(path, result, &RNA_Hair);
return 1;
}
+#endif
+#ifdef WITH_PARTICLE_NODES
if (CTX_data_equals(member, "pointcloud")) {
set_pointer_type(path, result, &RNA_PointCloud);
return 1;
}
+#endif
if (CTX_data_equals(member, "volume")) {
set_pointer_type(path, result, &RNA_Volume);
return 1;
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/clip_dopesheet_draw.c b/source/blender/editors/space_clip/clip_dopesheet_draw.c
index c7328ae9f8f..8aaf3faffec 100644
--- a/source/blender/editors/space_clip/clip_dopesheet_draw.c
+++ b/source/blender/editors/space_clip/clip_dopesheet_draw.c
@@ -142,7 +142,7 @@ void clip_draw_dopesheet_main(SpaceClip *sc, ARegion *region, Scene *scene)
strip[3] = 0.5f;
selected_strip[3] = 1.0f;
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
clip_draw_dopesheet_background(region, clip, pos_id);
@@ -288,7 +288,7 @@ void clip_draw_dopesheet_main(SpaceClip *sc, ARegion *region, Scene *scene)
immUnbindProgram();
}
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
}
@@ -389,7 +389,7 @@ void clip_draw_dopesheet_channels(const bContext *C, ARegion *region)
PropertyRNA *chan_prop_lock = RNA_struct_type_find_property(&RNA_MovieTrackingTrack, "lock");
BLI_assert(chan_prop_lock);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
for (channel = dopesheet->channels.first; channel; channel = channel->next) {
float yminc = (float)(y - CHANNEL_HEIGHT_HALF);
float ymaxc = (float)(y + CHANNEL_HEIGHT_HALF);
@@ -426,7 +426,7 @@ void clip_draw_dopesheet_channels(const bContext *C, ARegion *region)
/* adjust y-position for next one */
y -= CHANNEL_STEP;
}
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
UI_block_end(C, block);
UI_block_draw(C, block);
diff --git a/source/blender/editors/space_clip/clip_draw.c b/source/blender/editors/space_clip/clip_draw.c
index 07bdd337269..17539b2c423 100644
--- a/source/blender/editors/space_clip/clip_draw.c
+++ b/source/blender/editors/space_clip/clip_draw.c
@@ -153,9 +153,7 @@ static void draw_movieclip_cache(SpaceClip *sc, ARegion *region, MovieClip *clip
MovieTrackingPlaneTrack *act_plane_track = BKE_tracking_plane_track_get_active(&clip->tracking);
MovieTrackingReconstruction *reconstruction = BKE_tracking_get_active_reconstruction(tracking);
- GPU_blend(true);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
/* cache background */
ED_region_cache_draw_background(region);
@@ -245,7 +243,7 @@ static void draw_movieclip_cache(SpaceClip *sc, ARegion *region, MovieClip *clip
}
}
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
/* current frame */
x = (sc->user.framenr - sfra) / (efra - sfra + 1) * region->winx;
@@ -330,9 +328,7 @@ static void draw_movieclip_buffer(const bContext *C,
/* checkerboard for case alpha */
if (ibuf->planes == 32) {
- GPU_blend(true);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
imm_draw_box_checker_2d(x, y, x + zoomx * ibuf->x, y + zoomy * ibuf->y);
}
@@ -346,7 +342,7 @@ static void draw_movieclip_buffer(const bContext *C,
ED_draw_imbuf_ctx(C, ibuf, x, y, use_filter, zoomx * width / ibuf->x, zoomy * height / ibuf->y);
if (ibuf->planes == 32) {
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
if (sc->flag & SC_SHOW_METADATA) {
@@ -1212,9 +1208,7 @@ static void draw_plane_marker_image(Scene *scene,
if (plane_track->image_opacity != 1.0f || ibuf->planes == 32) {
transparent = true;
- GPU_blend(true);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
}
GPUTexture *texture = GPU_texture_create_nD(ibuf->x,
@@ -1266,7 +1260,7 @@ static void draw_plane_marker_image(Scene *scene,
GPU_texture_free(texture);
if (transparent) {
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
}
diff --git a/source/blender/editors/space_clip/clip_graph_draw.c b/source/blender/editors/space_clip/clip_graph_draw.c
index 277930495bd..4cf3e3e0798 100644
--- a/source/blender/editors/space_clip/clip_graph_draw.c
+++ b/source/blender/editors/space_clip/clip_graph_draw.c
@@ -192,7 +192,7 @@ static void draw_tracks_motion_and_error_curves(View2D *v2d, SpaceClip *sc, uint
}
/* Draw graph lines. */
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
clip_graph_tracking_values_iterate(sc,
(sc->flag & SC_SHOW_GRAPH_SEL_ONLY) != 0,
(sc->flag & SC_SHOW_GRAPH_HIDDEN) != 0,
@@ -200,7 +200,7 @@ static void draw_tracks_motion_and_error_curves(View2D *v2d, SpaceClip *sc, uint
tracking_segment_point_cb,
tracking_segment_start_cb,
tracking_segment_end_cb);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
/* Selected knot handles on top of curves. */
if (draw_knots) {
diff --git a/source/blender/editors/space_clip/clip_utils.c b/source/blender/editors/space_clip/clip_utils.c
index 03f791ad70d..bcbf843f51c 100644
--- a/source/blender/editors/space_clip/clip_utils.c
+++ b/source/blender/editors/space_clip/clip_utils.c
@@ -415,9 +415,7 @@ void clip_draw_sfra_efra(View2D *v2d, Scene *scene)
UI_view2d_view_ortho(v2d);
/* currently clip editor supposes that editing clip length is equal to scene frame range */
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
@@ -426,7 +424,7 @@ void clip_draw_sfra_efra(View2D *v2d, Scene *scene)
immRectf(pos, v2d->cur.xmin, v2d->cur.ymin, (float)SFRA, v2d->cur.ymax);
immRectf(pos, (float)EFRA, v2d->cur.ymin, v2d->cur.xmax, v2d->cur.ymax);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
immUniformThemeColorShade(TH_BACK, -60);
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 083d41747b3..7039eba7db1 100644
--- a/source/blender/editors/space_file/file_draw.c
+++ b/source/blender/editors/space_file/file_draw.c
@@ -267,7 +267,7 @@ static void file_draw_preview(uiBlock *block,
xco = sx + (int)dx;
yco = sy - layout->prv_h + (int)dy;
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
/* the large image */
@@ -290,8 +290,7 @@ static void file_draw_preview(uiBlock *block,
if (!is_icon && typeflags & FILE_TYPE_BLENDERLIB) {
/* Datablock preview images use premultiplied alpha. */
- GPU_blend_set_func_separate(
- GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA_PREMULT);
}
IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR);
@@ -309,8 +308,7 @@ static void file_draw_preview(uiBlock *block,
1.0f,
col);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
if (icon && is_icon) {
/* Small icon in the middle of large image, scaled to fit container and UI scale */
@@ -391,7 +389,7 @@ static void file_draw_preview(uiBlock *block,
UI_but_drag_set_image(but, BLI_strdup(path), icon, imb, scale, true);
}
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
static void renamebutton_cb(bContext *C, void *UNUSED(arg1), char *oldname)
@@ -443,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/graph_draw.c b/source/blender/editors/space_graph/graph_draw.c
index c358ba278e5..2a3985fdaa4 100644
--- a/source/blender/editors/space_graph/graph_draw.c
+++ b/source/blender/editors/space_graph/graph_draw.c
@@ -333,7 +333,7 @@ static void draw_fcurve_vertices(ARegion *region,
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
GPU_program_point_size(true);
/* draw the two handles first (if they're shown, the curve doesn't
@@ -346,7 +346,7 @@ static void draw_fcurve_vertices(ARegion *region,
draw_fcurve_keyframe_vertices(fcu, v2d, !(fcu->flag & FCURVE_PROTECTED), pos);
GPU_program_point_size(false);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
/* Handles ---------------- */
@@ -387,7 +387,7 @@ static void draw_fcurve_handles(SpaceGraph *sipo, FCurve *fcu)
if ((sipo->flag & SIPO_BEAUTYDRAW_OFF) == 0) {
GPU_line_smooth(true);
}
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
immBeginAtMost(GPU_PRIM_LINES, 4 * 2 * fcu->totvert);
@@ -464,7 +464,7 @@ static void draw_fcurve_handles(SpaceGraph *sipo, FCurve *fcu)
immEnd();
immUnbindProgram();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
if ((sipo->flag & SIPO_BEAUTYDRAW_OFF) == 0) {
GPU_line_smooth(false);
}
@@ -517,7 +517,7 @@ static void draw_fcurve_samples(SpaceGraph *sipo, ARegion *region, FCurve *fcu)
if ((sipo->flag & SIPO_BEAUTYDRAW_OFF) == 0) {
GPU_line_smooth(true);
}
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
@@ -529,7 +529,7 @@ static void draw_fcurve_samples(SpaceGraph *sipo, ARegion *region, FCurve *fcu)
immUnbindProgram();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
if ((sipo->flag & SIPO_BEAUTYDRAW_OFF) == 0) {
GPU_line_smooth(false);
}
@@ -945,7 +945,7 @@ static void draw_fcurve(bAnimContext *ac, SpaceGraph *sipo, ARegion *region, bAn
if ((sipo->flag & SIPO_BEAUTYDRAW_OFF) == 0) {
GPU_line_smooth(true);
}
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
const uint shdr_pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
@@ -1006,7 +1006,7 @@ static void draw_fcurve(bAnimContext *ac, SpaceGraph *sipo, ARegion *region, bAn
if ((sipo->flag & SIPO_BEAUTYDRAW_OFF) == 0) {
GPU_line_smooth(false);
}
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
/* 2) draw handles and vertices as appropriate based on active
@@ -1203,7 +1203,7 @@ void graph_draw_ghost_curves(bAnimContext *ac, SpaceGraph *sipo, ARegion *region
if ((sipo->flag & SIPO_BEAUTYDRAW_OFF) == 0) {
GPU_line_smooth(true);
}
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
const uint shdr_pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
@@ -1235,7 +1235,7 @@ void graph_draw_ghost_curves(bAnimContext *ac, SpaceGraph *sipo, ARegion *region
if ((sipo->flag & SIPO_BEAUTYDRAW_OFF) == 0) {
GPU_line_smooth(false);
}
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
/* This is called twice from space_graph.c -> graph_main_region_draw()
@@ -1322,9 +1322,7 @@ void graph_draw_channel_names(bContext *C, bAnimContext *ac, ARegion *region)
float ymax = ACHANNEL_FIRST_TOP(ac);
/* set blending again, as may not be set in previous step */
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
for (ale = anim_data.first; ale; ale = ale->next, ymax -= ACHANNEL_STEP(ac), channel_index++) {
float ymin = ymax - ACHANNEL_HEIGHT(ac);
@@ -1342,7 +1340,7 @@ void graph_draw_channel_names(bContext *C, bAnimContext *ac, ARegion *region)
UI_block_end(C, block);
UI_block_draw(C, block);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
/* free tempolary channels */
diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c
index a4f76384cc6..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);
@@ -249,7 +246,7 @@ static void graph_main_region_draw(const bContext *C, ARegion *region)
/* Draw a green line to indicate the cursor value */
immUniformThemeColorShadeAlpha(TH_CFRAME, -10, -50);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
GPU_line_width(2.0);
immBegin(GPU_PRIM_LINES, 2);
@@ -257,7 +254,7 @@ static void graph_main_region_draw(const bContext *C, ARegion *region)
immVertex2f(pos, v2d->cur.xmax, y);
immEnd();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
/* current frame or vertical component of vertical component of the cursor */
@@ -268,7 +265,7 @@ static void graph_main_region_draw(const bContext *C, ARegion *region)
/* to help differentiate this from the current frame,
* draw slightly darker like the horizontal one */
immUniformThemeColorShadeAlpha(TH_CFRAME, -40, -50);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
GPU_line_width(2.0);
immBegin(GPU_PRIM_LINES, 2);
@@ -276,7 +273,7 @@ static void graph_main_region_draw(const bContext *C, ARegion *region)
immVertex2f(pos, x, v2d->cur.ymax);
immEnd();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
immUnbindProgram();
@@ -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 f70589ac5f1..058436a46bf 100644
--- a/source/blender/editors/space_image/image_draw.c
+++ b/source/blender/editors/space_image/image_draw.c
@@ -175,9 +175,7 @@ void ED_image_draw_info(Scene *scene,
float hue = 0, sat = 0, val = 0, lum = 0, u = 0, v = 0;
float col[4], finalcol[4];
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
@@ -189,7 +187,7 @@ void ED_image_draw_info(Scene *scene,
immUnbindProgram();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
BLF_size(blf_mono_font, 11 * U.pixelsize, U.dpi);
@@ -350,7 +348,7 @@ void ED_image_draw_info(Scene *scene,
copy_v4_v4(finalcol, col);
}
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
dx += 0.25f * UI_UNIT_X;
BLI_rcti_init(&color_rect,
@@ -389,10 +387,10 @@ void ED_image_draw_info(Scene *scene,
immRecti(pos, color_quater_x, color_quater_y, color_rect_half.xmax, color_rect_half.ymax);
immRecti(pos, color_rect_half.xmin, color_rect_half.ymin, color_quater_x, color_quater_y);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
immUniformColor3fvAlpha(finalcol, fp ? fp[3] : (cp[3] / 255.0f));
immRecti(pos, color_rect.xmin, color_rect.ymin, color_rect.xmax, color_rect.ymax);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
else {
immUniformColor3fv(finalcol);
@@ -525,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);
}
@@ -540,9 +538,7 @@ static void draw_udim_label(ARegion *region, float fx, float fy, const char *lab
int x, y;
UI_view2d_view_to_region(&region->v2d, fx, fy, &x, &y);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
int textwidth = BLF_width(blf_mono_font, label, strlen(label)) + 10;
float stepx = BLI_rcti_size_x(&region->v2d.mask) / BLI_rctf_size_x(&region->v2d.cur);
@@ -560,7 +556,7 @@ static void draw_udim_label(ARegion *region, float fx, float fy, const char *lab
BLF_position(blf_mono_font, (int)(x + 10), (int)(y + 10), 0);
BLF_draw_ascii(blf_mono_font, label, strlen(label));
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
static void draw_image_buffer(const bContext *C,
@@ -602,9 +598,7 @@ static void draw_image_buffer(const bContext *C,
if (sima_flag & SI_USE_ALPHA) {
imm_draw_box_checker_2d(x, y, x + ibuf->x * zoomx, y + ibuf->y * zoomy);
- GPU_blend(true);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
}
/* If RGBA display with color management */
@@ -662,7 +656,7 @@ static void draw_image_buffer(const bContext *C,
}
if (sima_flag & SI_USE_ALPHA) {
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
}
@@ -771,15 +765,13 @@ static void draw_image_paint_helpers(
return;
}
- GPU_blend(true);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR);
immDrawPixelsTex(
&state, x, y, ibuf->x, ibuf->y, GPU_RGBA8, false, display_buffer, zoomx, zoomy, col);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
BKE_image_release_ibuf(brush->clone.image, ibuf, NULL);
IMB_display_buffer_release(cache_handle);
@@ -1058,9 +1050,7 @@ void draw_image_cache(const bContext *C, ARegion *region)
const rcti *rect_visible = ED_region_visible_rect(region);
const int region_bottom = rect_visible->ymin;
- GPU_blend(true);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
/* Draw cache background. */
ED_region_cache_draw_background(region);
@@ -1076,7 +1066,7 @@ void draw_image_cache(const bContext *C, ARegion *region)
region, num_segments, points, sfra + sima->iuser.offset, efra + sima->iuser.offset);
}
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
/* Draw current frame. */
x = (cfra - sfra) / (efra - sfra + 1) * region->winx;
diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c
index c01bc01588e..a806e3e25d1 100644
--- a/source/blender/editors/space_image/space_image.c
+++ b/source/blender/editors/space_image/space_image.c
@@ -642,7 +642,6 @@ static void image_main_region_draw(const bContext *C, ARegion *region)
// View2DScrollers *scrollers;
float col[3];
- GPU_batch_presets_reset();
GPUViewport *viewport = WM_draw_region_get_viewport(region);
GPUFrameBuffer *framebuffer_default, *framebuffer_overlay;
@@ -651,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);
@@ -662,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);
@@ -837,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 4e91da01cc9..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);
}
@@ -547,19 +547,20 @@ static void get_stats_string(
info + *ofs, len - *ofs, TIP_(" | Objects:%s/%s"), stats_fmt->totobjsel, stats_fmt->totobj);
}
-const char *ED_info_statusbar_string(Main *bmain, bScreen *screen, bContext *C)
+static const char *info_statusbar_string(Main *bmain,
+ Scene *scene,
+ ViewLayer *view_layer,
+ char statusbar_flag)
{
char formatted_mem[15];
size_t ofs = 0;
- char *info = screen->statusbar_info;
- int len = sizeof(screen->statusbar_info);
+ static char info[256];
+ int len = sizeof(info);
info[0] = '\0';
/* Scene statistics. */
- if (U.statusbar_flag & STATUSBAR_SHOW_STATS) {
- ViewLayer *view_layer = CTX_data_view_layer(C);
- Scene *scene = CTX_data_scene(C);
+ if (statusbar_flag & STATUSBAR_SHOW_STATS) {
SceneStatsFmt stats_fmt;
if (format_stats(bmain, scene, view_layer, &stats_fmt)) {
get_stats_string(info + ofs, len, &ofs, view_layer, &stats_fmt);
@@ -567,7 +568,7 @@ const char *ED_info_statusbar_string(Main *bmain, bScreen *screen, bContext *C)
}
/* Memory status. */
- if (U.statusbar_flag & STATUSBAR_SHOW_MEMORY) {
+ if (statusbar_flag & STATUSBAR_SHOW_MEMORY) {
if (info[0]) {
ofs += BLI_snprintf(info + ofs, len - ofs, " | ");
}
@@ -577,7 +578,7 @@ const char *ED_info_statusbar_string(Main *bmain, bScreen *screen, bContext *C)
}
/* GPU VRAM status. */
- if ((U.statusbar_flag & STATUSBAR_SHOW_VRAM) && (GPU_mem_stats_supported())) {
+ if ((statusbar_flag & STATUSBAR_SHOW_VRAM) && (GPU_mem_stats_supported())) {
int gpu_free_mem_kb, gpu_tot_mem_kb;
GPU_mem_stats_get(&gpu_tot_mem_kb, &gpu_free_mem_kb);
float gpu_total_gb = gpu_tot_mem_kb / 1048576.0f;
@@ -599,7 +600,7 @@ const char *ED_info_statusbar_string(Main *bmain, bScreen *screen, bContext *C)
}
/* Blender version. */
- if (U.statusbar_flag & STATUSBAR_SHOW_VERSION) {
+ if (statusbar_flag & STATUSBAR_SHOW_VERSION) {
if (info[0]) {
ofs += BLI_snprintf(info + ofs, len - ofs, " | ");
}
@@ -609,6 +610,20 @@ const char *ED_info_statusbar_string(Main *bmain, bScreen *screen, bContext *C)
return info;
}
+const char *ED_info_statusbar_string(Main *bmain, Scene *scene, ViewLayer *view_layer)
+{
+ return info_statusbar_string(bmain, scene, view_layer, U.statusbar_flag);
+}
+
+const char *ED_info_statistics_string(Main *bmain, Scene *scene, ViewLayer *view_layer)
+{
+ const eUserpref_StatusBar_Flag statistics_status_bar_flag = STATUSBAR_SHOW_STATS |
+ STATUSBAR_SHOW_MEMORY |
+ STATUSBAR_SHOW_VERSION;
+
+ return info_statusbar_string(bmain, scene, view_layer, statistics_status_bar_flag);
+}
+
static void stats_row(int col1,
const char *key,
int col2,
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_info/textview.c b/source/blender/editors/space_info/textview.c
index 93a79d9a2bc..4d9a4fb2706 100644
--- a/source/blender/editors/space_info/textview.c
+++ b/source/blender/editors/space_info/textview.c
@@ -84,9 +84,7 @@ static void textview_draw_sel(const char *str,
const int sta = BLI_str_utf8_offset_to_column(str, max_ii(sel[0], 0));
const int end = BLI_str_utf8_offset_to_column(str, min_ii(sel[1], str_len_draw));
- GPU_blend(true);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
@@ -97,7 +95,7 @@ static void textview_draw_sel(const char *str,
immUnbindProgram();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
}
@@ -240,7 +238,7 @@ static bool textview_draw_string(TextViewDrawState *tds,
int vpadding = (tds->lheight + (tds->row_vpadding * 2) - UI_DPI_ICON_SIZE) / 2;
int hpadding = tds->draw_rect->xmin - (UI_DPI_ICON_SIZE * 1.3f);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
UI_icon_draw_ex(hpadding,
line_top - UI_DPI_ICON_SIZE - vpadding,
icon,
@@ -249,7 +247,7 @@ static bool textview_draw_string(TextViewDrawState *tds,
0.0f,
icon_fg,
false);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
tds->xy[1] += tds->row_vpadding;
diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c
index 97939a93d01..b0d5360e29b 100644
--- a/source/blender/editors/space_nla/nla_draw.c
+++ b/source/blender/editors/space_nla/nla_draw.c
@@ -321,7 +321,7 @@ static void nla_draw_strip_curves(NlaStrip *strip, float yminc, float ymaxc, uin
/* draw with AA'd line */
GPU_line_smooth(true);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
/* influence -------------------------- */
if (strip->flag & NLASTRIP_FLAG_USR_INFLUENCE) {
@@ -374,7 +374,7 @@ static void nla_draw_strip_curves(NlaStrip *strip, float yminc, float ymaxc, uin
/* turn off AA'd lines */
GPU_line_smooth(false);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
/* helper call to setup dashed-lines for strip outlines */
@@ -434,9 +434,7 @@ static void nla_draw_strip(SpaceNla *snla,
*/
if ((strip->extendmode != NLASTRIP_EXTEND_NOTHING) && (non_solo == 0)) {
/* enable transparency... */
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
switch (strip->extendmode) {
/* since this does both sides,
@@ -468,7 +466,7 @@ static void nla_draw_strip(SpaceNla *snla,
break;
}
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
/* draw 'inside' of strip itself */
@@ -487,9 +485,9 @@ static void nla_draw_strip(SpaceNla *snla,
/* strip is in disabled track - make less visible */
immUniformColor3fvAlpha(color, 0.1f);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
immRectf(shdr_pos, strip->start, yminc, strip->end, ymaxc);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
/* draw strip's control 'curves'
@@ -746,9 +744,7 @@ void draw_nla_main_data(bAnimContext *ac, SpaceNla *snla, ARegion *region)
/* just draw a semi-shaded rect spanning the width of the viewable area if there's data,
* and a second darker rect within which we draw keyframe indicator dots if there's data
*/
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
/* get colors for drawing */
float color[4];
@@ -790,7 +786,7 @@ void draw_nla_main_data(bAnimContext *ac, SpaceNla *snla, ARegion *region)
nla_action_draw_keyframes(
v2d, adt, ale->data, ycenter, ymin + NLACHANNEL_SKIP, ymax - NLACHANNEL_SKIP);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
break;
}
}
@@ -854,9 +850,7 @@ void draw_nla_channel_list(const bContext *C, bAnimContext *ac, ARegion *region)
float ymax = NLACHANNEL_FIRST_TOP(ac);
/* set blending again, as may not be set in previous step */
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
/* loop through channels, and set up drawing depending on their type */
for (ale = anim_data.first; ale;
@@ -876,7 +870,7 @@ void draw_nla_channel_list(const bContext *C, bAnimContext *ac, ARegion *region)
UI_block_end(C, block);
UI_block_draw(C, block);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
/* free temporary channels */
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/drawnode.c b/source/blender/editors/space_node/drawnode.c
index 37daa881317..5866b5cc12f 100644
--- a/source/blender/editors/space_node/drawnode.c
+++ b/source/blender/editors/space_node/drawnode.c
@@ -456,7 +456,6 @@ static void node_draw_frame(const bContext *C,
bNodeInstanceKey UNUSED(key))
{
rctf *rct = &node->totr;
- int color_id = node_get_colorid(node);
float color[4];
float alpha;
@@ -499,8 +498,6 @@ static void node_draw_frame(const bContext *C,
/* label */
node_draw_frame_label(ntree, node, snode->aspect);
- UI_ThemeClearColor(color_id);
-
UI_block_end(C, node->block);
UI_block_draw(C, node->block);
node->block = NULL;
@@ -602,7 +599,7 @@ static void node_draw_reroute(const bContext *C,
/* outline active and selected emphasis */
if (node->flag & SELECT) {
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
GPU_line_smooth(true);
/* using different shades of TH_TEXT_HI for the empasis, like triangle */
if (node->flag & NODE_ACTIVE) {
@@ -614,7 +611,7 @@ static void node_draw_reroute(const bContext *C,
UI_draw_roundbox_4fv(false, rct->xmin, rct->ymin, rct->xmax, rct->ymax, size, debug_color);
GPU_line_smooth(false);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
#endif
@@ -3688,13 +3685,11 @@ void draw_nodespace_back_pix(const bContext *C,
GPU_shader_unbind();
}
else if (snode->flag & SNODE_USE_ALPHA) {
- GPU_blend(true);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
ED_draw_imbuf_ctx(C, ibuf, x, y, false, snode->zoom, snode->zoom);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
else {
ED_draw_imbuf_ctx(C, ibuf, x, y, false, snode->zoom, snode->zoom);
@@ -4013,7 +4008,7 @@ static void nodelink_batch_draw(SpaceNode *snode)
return;
}
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
float colors[6][4] = {{0.0f}};
UI_GetThemeColor4fv(TH_WIRE_INNER, colors[nodelink_get_color_id(TH_WIRE_INNER)]);
@@ -4026,14 +4021,14 @@ static void nodelink_batch_draw(SpaceNode *snode)
GPU_vertbuf_use(g_batch_link.inst_vbo); /* force update. */
GPU_batch_program_set_builtin(g_batch_link.batch, GPU_SHADER_2D_NODELINK_INST);
- GPU_batch_uniform_4fv_array(g_batch_link.batch, "colors", 6, (float *)colors);
+ GPU_batch_uniform_4fv_array(g_batch_link.batch, "colors", 6, colors);
GPU_batch_uniform_1f(g_batch_link.batch, "expandSize", snode->aspect * LINK_WIDTH);
GPU_batch_uniform_1f(g_batch_link.batch, "arrowSize", ARROW_SIZE);
GPU_batch_draw(g_batch_link.batch);
nodelink_batch_reset();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
void nodelink_batch_start(SpaceNode *UNUSED(snode))
@@ -4108,8 +4103,8 @@ void node_draw_link_bezier(
GPUBatch *batch = g_batch_link.batch_single;
GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_NODELINK);
- GPU_batch_uniform_2fv_array(batch, "bezierPts", 4, (float *)vec);
- GPU_batch_uniform_4fv_array(batch, "colors", 3, (float *)colors);
+ GPU_batch_uniform_2fv_array(batch, "bezierPts", 4, vec);
+ GPU_batch_uniform_4fv_array(batch, "colors", 3, colors);
GPU_batch_uniform_1f(batch, "expandSize", snode->aspect * LINK_WIDTH);
GPU_batch_uniform_1f(batch, "arrowSize", ARROW_SIZE);
GPU_batch_uniform_1i(batch, "doArrow", drawarrow);
diff --git a/source/blender/editors/space_node/node_draw.c b/source/blender/editors/space_node/node_draw.c
index 3fd0b0a5a58..56e53e79a8d 100644
--- a/source/blender/editors/space_node/node_draw.c
+++ b/source/blender/editors/space_node/node_draw.c
@@ -52,6 +52,7 @@
#include "GPU_immediate_util.h"
#include "GPU_matrix.h"
#include "GPU_state.h"
+#include "GPU_viewport.h"
#include "WM_api.h"
#include "WM_types.h"
@@ -690,13 +691,13 @@ static void node_draw_mute_line(View2D *v2d, SpaceNode *snode, bNode *node)
{
bNodeLink *link;
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
for (link = node->internal_links.first; link; link = link->next) {
node_draw_link_bezier(v2d, snode, link, TH_REDALERT, TH_REDALERT, -1);
}
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
/* flags used in gpu_shader_keyframe_diamond_frag.glsl */
@@ -834,8 +835,8 @@ void ED_node_socket_draw(bNodeSocket *sock, const rcti *rect, const float color[
uint outline_col_id = GPU_vertformat_attr_add(
format, "outlineColor", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
- gpuPushAttr(GPU_BLEND_BIT);
- GPU_blend(true);
+ eGPUBlend state = GPU_blend_get();
+ GPU_blend(GPU_BLEND_ALPHA);
GPU_program_point_size(true);
immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND);
@@ -859,53 +860,27 @@ void ED_node_socket_draw(bNodeSocket *sock, const rcti *rect, const float color[
immUnbindProgram();
GPU_program_point_size(false);
- gpuPopAttr();
+
+ /* Restore. */
+ GPU_blend(state);
}
/* ************** Socket callbacks *********** */
-static void node_draw_preview_background(float tile, rctf *rect)
+static void node_draw_preview_background(rctf *rect)
{
- float x, y;
-
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_2D_CHECKER);
- /* draw checkerboard backdrop to show alpha */
- immUniformColor3ub(120, 120, 120);
+ /* Drawing the checkerboard. */
+ const float checker_dark = UI_ALPHA_CHECKER_DARK / 255.0f;
+ const float checker_light = UI_ALPHA_CHECKER_LIGHT / 255.0f;
+ immUniform4f("color1", checker_dark, checker_dark, checker_dark, 1.0f);
+ immUniform4f("color2", checker_light, checker_light, checker_light, 1.0f);
+ immUniform1i("size", 8);
immRectf(pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax);
- immUniformColor3ub(160, 160, 160);
-
- for (y = rect->ymin; y < rect->ymax; y += tile * 2) {
- for (x = rect->xmin; x < rect->xmax; x += tile * 2) {
- float tilex = tile, tiley = tile;
-
- if (x + tile > rect->xmax) {
- tilex = rect->xmax - x;
- }
- if (y + tile > rect->ymax) {
- tiley = rect->ymax - y;
- }
-
- immRectf(pos, x, y, x + tilex, y + tiley);
- }
- }
- for (y = rect->ymin + tile; y < rect->ymax; y += tile * 2) {
- for (x = rect->xmin + tile; x < rect->xmax; x += tile * 2) {
- float tilex = tile, tiley = tile;
-
- if (x + tile > rect->xmax) {
- tilex = rect->xmax - x;
- }
- if (y + tile > rect->ymax) {
- tiley = rect->ymax - y;
- }
-
- immRectf(pos, x, y, x + tilex, y + tiley);
- }
- }
immUnbindProgram();
}
@@ -934,12 +909,11 @@ static void node_draw_preview(bNodePreview *preview, rctf *prv)
scale = yscale;
}
- node_draw_preview_background(BLI_rctf_size_x(prv) / 10.0f, &draw_rect);
+ node_draw_preview_background(&draw_rect);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
/* premul graphics */
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR);
immDrawPixelsTex(&state,
@@ -954,7 +928,7 @@ static void node_draw_preview(bNodePreview *preview, rctf *prv)
scale,
NULL);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
@@ -978,23 +952,8 @@ static void node_toggle_button_cb(struct bContext *C, void *node_argv, void *op_
void node_draw_shadow(SpaceNode *snode, bNode *node, float radius, float alpha)
{
rctf *rct = &node->totr;
-
UI_draw_roundbox_corner_set(UI_CNR_ALL);
- if (node->parent == NULL) {
- ui_draw_dropshadow(rct, radius, snode->aspect, alpha, node->flag & SELECT);
- }
- else {
- const float margin = 3.0f;
-
- const float color[4] = {0.0f, 0.0f, 0.0f, 0.33f};
- UI_draw_roundbox_aa(true,
- rct->xmin - margin,
- rct->ymin - margin,
- rct->xmax + margin,
- rct->ymax + margin,
- radius + margin,
- color);
- }
+ ui_draw_dropshadow(rct, radius, snode->aspect, alpha, node->flag & SELECT);
}
void node_draw_sockets(View2D *v2d,
@@ -1024,7 +983,7 @@ void node_draw_sockets(View2D *v2d,
uint outline_col_id = GPU_vertformat_attr_add(
format, "outlineColor", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
GPU_program_point_size(true);
immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND);
immUniform1f("outline_scale", 0.7f);
@@ -1158,7 +1117,7 @@ void node_draw_sockets(View2D *v2d,
immUnbindProgram();
GPU_program_point_size(false);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
static void node_draw_basis(const bContext *C,
@@ -1384,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;
@@ -1434,7 +1391,7 @@ static void node_draw_hidden(const bContext *C,
/* custom color inline */
if (node->flag & NODE_CUSTOM_COLOR) {
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
GPU_line_smooth(true);
UI_draw_roundbox_3fv_alpha(false,
@@ -1447,7 +1404,7 @@ static void node_draw_hidden(const bContext *C,
1.0f);
GPU_line_smooth(false);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
/* title */
@@ -1681,7 +1638,7 @@ void node_draw_nodetree(const bContext *C,
}
/* node lines */
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
nodelink_batch_start(snode);
for (link = ntree->links.first; link; link = link->next) {
if (!nodeLinkIsHidden(link)) {
@@ -1689,7 +1646,7 @@ void node_draw_nodetree(const bContext *C,
}
}
nodelink_batch_end(snode);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
/* draw foreground nodes, last nodes in front */
for (a = 0, node = ntree->nodes.first; node; node = node->next, a++) {
@@ -1750,12 +1707,12 @@ static void draw_group_overlay(const bContext *C, ARegion *region)
float color[4];
/* shade node groups to separate them visually */
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
UI_GetThemeColorShadeAlpha4fv(TH_NODE_GROUP, 0, 0, color);
UI_draw_roundbox_corner_set(UI_CNR_NONE);
UI_draw_roundbox_4fv(true, rect.xmin, rect.ymin, rect.xmax, rect.ymax, 0, color);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
/* set the block bounds to clip mouse events from underlying nodes */
block = UI_block_begin(C, region, "node tree bounds block", UI_EMBOSS);
@@ -1770,10 +1727,16 @@ void drawnodespace(const bContext *C, ARegion *region)
SpaceNode *snode = CTX_wm_space_node(C);
View2D *v2d = &region->v2d;
- UI_ThemeClearColor(TH_BACK);
- GPU_clear(GPU_COLOR_BIT);
+ /* Setup offscreen buffers. */
+ GPUViewport *viewport = WM_draw_region_get_viewport(region);
+
+ GPUFrameBuffer *framebuffer_overlay = GPU_viewport_framebuffer_overlay_get(viewport);
+ GPU_framebuffer_bind_no_srgb(framebuffer_overlay);
UI_view2d_view_ortho(v2d);
+ UI_ThemeClearColor(TH_BACK);
+ 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,
@@ -1789,8 +1752,7 @@ void drawnodespace(const bContext *C, ARegion *region)
ED_region_draw_cb_draw(C, region, REGION_DRAW_PRE_VIEW);
/* only set once */
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
/* nodes */
snode_set_context(C);
@@ -1876,7 +1838,7 @@ void drawnodespace(const bContext *C, ARegion *region)
}
/* temporary links */
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
GPU_line_smooth(true);
for (nldrag = snode->linkdrag.first; nldrag; nldrag = nldrag->next) {
for (linkdata = nldrag->links.first; linkdata; linkdata = linkdata->next) {
@@ -1884,7 +1846,7 @@ void drawnodespace(const bContext *C, ARegion *region)
}
}
GPU_line_smooth(false);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
if (snode->flag & SNODE_SHOW_GPENCIL) {
/* draw grease-pencil ('canvas' strokes) */
diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c
index c88b6a1b297..ef931dd9bb8 100644
--- a/source/blender/editors/space_node/node_edit.c
+++ b/source/blender/editors/space_node/node_edit.c
@@ -207,12 +207,11 @@ static void compo_initjob(void *cjv)
ViewLayer *view_layer = cj->view_layer;
cj->compositor_depsgraph = DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_RENDER);
- DEG_graph_build_for_compositor_preview(
- cj->compositor_depsgraph, bmain, scene, view_layer, cj->ntree);
+ DEG_graph_build_for_compositor_preview(cj->compositor_depsgraph, cj->ntree);
/* NOTE: Don't update animation to preserve unkeyed changes, this means can not use
* evaluate_on_framechange. */
- DEG_evaluate_on_refresh(bmain, cj->compositor_depsgraph);
+ DEG_evaluate_on_refresh(cj->compositor_depsgraph);
bNodeTree *ntree_eval = (bNodeTree *)DEG_get_evaluated_id(cj->compositor_depsgraph,
&cj->ntree->id);
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 28e233b6dd2..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;
@@ -2729,7 +2747,7 @@ static void outliner_draw_iconrow_number(const uiFontStyle *fstyle,
number_text,
text_col);
UI_fontstyle_set(fstyle);
- GPU_blend(true); /* Roundbox and text drawing disables. */
+ GPU_blend(GPU_BLEND_ALPHA); /* Roundbox and text drawing disables. */
}
static void outliner_icon_background_colors(float icon_color[4], float icon_border[4])
@@ -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(true); /* 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) {
@@ -2981,7 +3004,7 @@ static void outliner_draw_tree_element(bContext *C,
xmax -= restrict_column_width + UI_UNIT_X;
}
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
/* colors for active/selected data */
if (tselem->type == 0) {
@@ -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(true); /* 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. */
}
@@ -3116,7 +3128,7 @@ static void outliner_draw_tree_element(bContext *C,
offsx += UI_UNIT_X + 4 * ufac;
}
}
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
/* name */
if ((tselem->flag & TSE_TEXTBUT) == 0) {
@@ -3140,7 +3152,7 @@ static void outliner_draw_tree_element(bContext *C,
else if (tselem->type != TSE_R_LAYER) {
int tempx = startx + offsx;
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
MergedIconRow merged = {{0}};
outliner_draw_iconrow(C,
@@ -3156,7 +3168,7 @@ static void outliner_draw_tree_element(bContext *C,
alpha_fac,
&merged);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
}
}
@@ -3315,9 +3327,9 @@ static void outliner_draw_hierarchy_lines(SpaceOutliner *space_outliner,
UI_GetThemeColorBlend3ubv(TH_BACK, TH_TEXT, 0.4f, col);
col[3] = 255;
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
outliner_draw_hierarchy_lines_recursive(pos, space_outliner, lb, startx, col, false, starty);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
immUnbindProgram();
}
@@ -3464,7 +3476,7 @@ static void outliner_draw_highlights(ARegion *region,
UI_GetThemeColor4fv(TH_MATCH, col_searchmatch);
col_searchmatch[3] = 0.5f;
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
@@ -3479,7 +3491,7 @@ static void outliner_draw_highlights(ARegion *region,
startx,
starty);
immUnbindProgram();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
static void outliner_draw_tree(bContext *C,
@@ -3493,8 +3505,7 @@ static void outliner_draw_tree(bContext *C,
const uiFontStyle *fstyle = UI_FSTYLE_WIDGET;
int starty, startx;
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); /* Only once. */
+ GPU_blend(GPU_BLEND_ALPHA); /* Only once. */
if (space_outliner->outlinevis == SO_DATA_API) {
/* struct marks */
@@ -3508,12 +3519,12 @@ static void outliner_draw_tree(bContext *C,
outliner_draw_highlights(region, space_outliner, startx, &starty);
/* set scissor so tree elements or lines can't overlap restriction icons */
- float scissor[4] = {0};
+ int scissor[4] = {0};
if (restrict_column_width > 0.0f) {
int mask_x = BLI_rcti_size_x(&region->v2d.mask) - (int)restrict_column_width + 1;
CLAMP_MIN(mask_x, 0);
- GPU_scissor_get_f(scissor);
+ GPU_scissor_get(scissor);
GPU_scissor(0, 0, mask_x, region->winy);
}
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/script_edit.c b/source/blender/editors/space_script/script_edit.c
index e9ed1cec228..f739bfe367e 100644
--- a/source/blender/editors/space_script/script_edit.c
+++ b/source/blender/editors/space_script/script_edit.c
@@ -42,7 +42,7 @@
#include "script_intern.h" // own include
#ifdef WITH_PYTHON
-# include "BPY_extern.h" /* BPY_script_exec */
+# include "BPY_extern_run.h"
#endif
static int run_pyfile_exec(bContext *C, wmOperator *op)
@@ -50,7 +50,7 @@ static int run_pyfile_exec(bContext *C, wmOperator *op)
char path[512];
RNA_string_get(op->ptr, "filepath", path);
#ifdef WITH_PYTHON
- if (BPY_execute_filepath(C, path, op->reports)) {
+ if (BPY_run_filepath(C, path, op->reports)) {
ARegion *region = CTX_wm_region(C);
ED_region_tag_redraw(region);
return OPERATOR_FINISHED;
@@ -120,7 +120,7 @@ static int script_reload_exec(bContext *C, wmOperator *op)
/* TODO, this crashes on netrender and keying sets, need to look into why
* disable for now unless running in debug mode */
WM_cursor_wait(1);
- BPY_execute_string(
+ BPY_run_string_eval(
C, (const char *[]){"bpy", NULL}, "bpy.utils.load_scripts(reload_scripts=True)");
WM_cursor_wait(0);
WM_event_add_notifier(C, NC_WINDOW, NULL);
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_buttons.c b/source/blender/editors/space_sequencer/sequencer_buttons.c
index dce8aa16985..79f30423bf2 100644
--- a/source/blender/editors/space_sequencer/sequencer_buttons.c
+++ b/source/blender/editors/space_sequencer/sequencer_buttons.c
@@ -78,6 +78,7 @@ static void metadata_panel_context_draw(const bContext *C, Panel *panel)
struct Main *bmain = CTX_data_main(C);
struct Depsgraph *depsgraph = CTX_data_expect_evaluated_depsgraph(C);
struct Scene *scene = CTX_data_scene(C);
+ ARegion *region = CTX_wm_region(C);
SpaceSeq *space_sequencer = CTX_wm_space_seq(C);
/* NOTE: We can only reliably show metadata for the original (current)
* frame when split view is used. */
@@ -88,7 +89,8 @@ static void metadata_panel_context_draw(const bContext *C, Panel *panel)
}
/* NOTE: We disable multiview for drawing, since we don't know what is the
* from the panel (is kind of all the views?). */
- ImBuf *ibuf = sequencer_ibuf_get(bmain, depsgraph, scene, space_sequencer, scene->r.cfra, 0, "");
+ ImBuf *ibuf = sequencer_ibuf_get(
+ bmain, region, depsgraph, scene, space_sequencer, scene->r.cfra, 0, "");
if (ibuf != NULL) {
ED_region_image_metadata_panel_draw(ibuf, panel->layout);
IMB_freeImBuf(ibuf);
diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c
index 0f4690c11d5..cdc7ada8c84 100644
--- a/source/blender/editors/space_sequencer/sequencer_draw.c
+++ b/source/blender/editors/space_sequencer/sequencer_draw.c
@@ -56,6 +56,7 @@
#include "GPU_matrix.h"
#include "GPU_state.h"
#include "GPU_vertex_buffer.h"
+#include "GPU_viewport.h"
#include "ED_anim_api.h"
#include "ED_gpencil.h"
@@ -88,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;
@@ -294,7 +296,7 @@ static void draw_seq_waveform(View2D *v2d,
/* Fcurve lookup is quite expensive, so do this after precondition. */
FCurve *fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "volume", 0, NULL);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
@@ -350,13 +352,13 @@ static void draw_seq_waveform(View2D *v2d,
immEnd();
immUnbindProgram();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
}
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];
@@ -381,9 +383,7 @@ static void drawmeta_contents(Scene *scene, Sequence *seqm, float x1, float y1,
offset = 0;
}
- GPU_blend(true);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
for (seq = seqbase->first; seq; seq = seq->next) {
chan_min = min_ii(chan_min, seq->machine);
@@ -443,7 +443,7 @@ static void drawmeta_contents(Scene *scene, Sequence *seqm, float x1, float y1,
immUnbindProgram();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
/* Get handle width in pixels. */
@@ -489,10 +489,9 @@ static void draw_seq_handle(View2D *v2d,
}
if (!(seq->type & SEQ_TYPE_EFFECT) || BKE_sequence_effect_get_num_inputs(seq->type) == 0) {
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
if (seq->flag & whichsel) {
if (seq_active) {
@@ -511,7 +510,7 @@ static void draw_seq_handle(View2D *v2d,
}
immRectf(pos, rx1, y1, rx2, y2);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
/* Draw numbers for start and end of the strip next to its handles. */
@@ -753,9 +752,7 @@ static void draw_sequence_extensions(Scene *scene, Sequence *seq, uint pos, floa
y1 = seq->machine + SEQ_STRIP_OFSBOTTOM;
y2 = seq->machine + SEQ_STRIP_OFSTOP;
- GPU_blend(true);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
color3ubv_from_seq(scene, seq, col);
if (seq->flag & SELECT) {
@@ -781,7 +778,7 @@ static void draw_sequence_extensions(Scene *scene, Sequence *seq, uint pos, floa
imm_draw_box_wire_2d(
pos, x2, y2 + pixely, (float)(seq->start + seq->len), y2 + SEQ_STRIP_OFSBOTTOM);
}
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
static void draw_color_strip_band(Sequence *seq, uint pos, float text_margin_y, float y1)
@@ -791,7 +788,7 @@ static void draw_color_strip_band(Sequence *seq, uint pos, float text_margin_y,
rgb_float_to_uchar(col, colvars->col);
if (seq->flag & SEQ_MUTE) {
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
col[3] = MUTE_ALPHA;
}
else {
@@ -812,7 +809,7 @@ static void draw_color_strip_band(Sequence *seq, uint pos, float text_margin_y,
immEnd();
if (seq->flag & SEQ_MUTE) {
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
}
@@ -843,9 +840,7 @@ static void draw_seq_background(Scene *scene,
}
if (seq->flag & SEQ_MUTE) {
- GPU_blend(true);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
col[3] = MUTE_ALPHA;
}
@@ -910,13 +905,13 @@ static void draw_seq_background(Scene *scene,
}
if (seq->flag & SEQ_MUTE) {
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
}
static void draw_seq_locked(float x1, float y1, float x2, float y2)
{
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_DIAG_STRIPES);
@@ -930,12 +925,12 @@ static void draw_seq_locked(float x1, float y1, float x2, float y2)
immUnbindProgram();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
static void draw_seq_invalid(float x1, float x2, float y2, float text_margin_y)
{
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
@@ -943,7 +938,7 @@ static void draw_seq_invalid(float x1, float x2, float y2, float text_margin_y)
immRectf(pos, x1, y2, x2, text_margin_y);
immUnbindProgram();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
static void calculate_seq_text_offsets(
@@ -1056,13 +1051,13 @@ static void draw_seq_fcurve(
GPU_vertbuf_data_len_set(vbo, vert_count);
GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_UNIFORM_COLOR);
GPU_batch_uniform_4f(batch, "color", 0.0f, 0.0f, 0.0f, 0.15f);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
if (vert_count > 0) {
GPU_batch_draw(batch);
}
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
GPU_batch_discard(batch);
}
}
@@ -1180,7 +1175,7 @@ static void draw_effect_inputs_highlight(Sequence *seq)
Sequence *seq1 = seq->seq1;
Sequence *seq2 = seq->seq2;
Sequence *seq3 = seq->seq3;
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
@@ -1207,7 +1202,7 @@ static void draw_effect_inputs_highlight(Sequence *seq)
seq3->machine + SEQ_STRIP_OFSTOP);
}
immUnbindProgram();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
void sequencer_special_update_set(Sequence *seq)
@@ -1235,7 +1230,14 @@ void ED_sequencer_special_preview_clear(void)
sequencer_special_update_set(NULL);
}
+/**
+ * Rendering using opengl will change the current viewport/context.
+ * This is why we need the ARegion, to set back the render area.
+ * TODO do not rely on such hack and just update the ibuf ouside of
+ * the UI drawing code.
+ **/
ImBuf *sequencer_ibuf_get(struct Main *bmain,
+ ARegion *region,
struct Depsgraph *depsgraph,
Scene *scene,
SpaceSeq *sseq,
@@ -1271,9 +1273,16 @@ ImBuf *sequencer_ibuf_get(struct Main *bmain,
* by Escape pressed somewhere in the past. */
G.is_break = false;
- /* Rendering can change OGL context. Save & Restore framebuffer. */
+ GPUViewport *viewport = WM_draw_region_get_bound_viewport(region);
GPUFrameBuffer *fb = GPU_framebuffer_active_get();
- GPU_framebuffer_restore();
+ if (viewport) {
+ /* Unbind viewport to release the DRW context. */
+ GPU_viewport_unbind(viewport);
+ }
+ else {
+ /* Rendering can change OGL context. Save & Restore framebuffer. */
+ GPU_framebuffer_restore();
+ }
if (special_seq_update) {
ibuf = BKE_sequencer_give_ibuf_direct(&context, cfra + frame_ofs, special_seq_update);
@@ -1282,7 +1291,12 @@ ImBuf *sequencer_ibuf_get(struct Main *bmain,
ibuf = BKE_sequencer_give_ibuf(&context, cfra + frame_ofs, sseq->chanshown);
}
- if (fb) {
+ if (viewport) {
+ /* Follows same logic as wm_draw_window_offscreen to make sure to restore the same viewport. */
+ int view = (sseq->multiview_eye == STEREO_RIGHT_ID) ? 1 : 0;
+ GPU_viewport_bind(viewport, view, &region->winrct);
+ }
+ else if (fb) {
GPU_framebuffer_bind(fb);
}
@@ -1534,11 +1548,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], 0.0);
- GPU_clear(GPU_COLOR_BIT);
+ UI_ThemeClearColor(TH_SEQ_PREVIEW);
}
static void sequencer_preview_get_rect(rctf *preview,
@@ -1594,9 +1604,7 @@ static void sequencer_draw_display_buffer(const bContext *C,
void *display_buffer;
if (sseq->mainb == SEQ_DRAW_IMG_IMBUF && sseq->flag & SEQ_USE_ALPHA) {
- GPU_blend(true);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
}
/* Format needs to be created prior to any immBindShader call.
@@ -1681,7 +1689,7 @@ static void sequencer_draw_display_buffer(const bContext *C,
}
if (sseq->mainb == SEQ_DRAW_IMG_IMBUF && sseq->flag & SEQ_USE_ALPHA) {
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
if (draw_backdrop) {
@@ -1775,6 +1783,16 @@ void sequencer_draw_preview(const bContext *C,
return;
}
+ /* Get image. */
+ ibuf = sequencer_ibuf_get(
+ bmain, region, depsgraph, scene, sseq, cfra, frame_ofs, names[sseq->multiview_eye]);
+
+ /* Setup offscreen buffers. */
+ GPUViewport *viewport = WM_draw_region_get_viewport(region);
+ GPUFrameBuffer *framebuffer_overlay = GPU_viewport_framebuffer_overlay_get(viewport);
+ GPU_framebuffer_bind_no_srgb(framebuffer_overlay);
+ GPU_depth_test(GPU_DEPTH_NONE);
+
if (sseq->render_size == SEQ_PROXY_RENDER_SIZE_NONE) {
sequencer_preview_clear();
return;
@@ -1794,9 +1812,6 @@ void sequencer_draw_preview(const bContext *C,
imm_draw_box_checker_2d(v2d->tot.xmin, v2d->tot.ymin, v2d->tot.xmax, v2d->tot.ymax);
}
}
- /* Get image. */
- ibuf = sequencer_ibuf_get(
- bmain, depsgraph, scene, sseq, cfra, frame_ofs, names[sseq->multiview_eye]);
if (ibuf) {
scope = sequencer_get_scope(scene, sseq, ibuf, draw_backdrop);
@@ -1928,7 +1943,7 @@ static void draw_seq_strips(const bContext *C, Editing *ed, ARegion *region)
else if (last_seq->type == SEQ_TYPE_MULTICAM) {
int channel = last_seq->multicam_source;
if (channel != 0) {
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
@@ -1937,7 +1952,7 @@ static void draw_seq_strips(const bContext *C, Editing *ed, ARegion *region)
immRectf(pos, v2d->cur.xmin, channel, v2d->cur.xmax, channel + 1);
immUnbindProgram();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
}
}
@@ -1945,7 +1960,7 @@ static void draw_seq_strips(const bContext *C, Editing *ed, ARegion *region)
/* Draw highlight if "solo preview" is used. */
if (special_seq_update) {
const Sequence *seq = special_seq_update;
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
@@ -1959,7 +1974,7 @@ static void draw_seq_strips(const bContext *C, Editing *ed, ARegion *region)
immUnbindProgram();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
}
@@ -1969,7 +1984,7 @@ static void seq_draw_sfra_efra(Scene *scene, View2D *v2d)
const int frame_sta = scene->r.sfra;
const int frame_end = scene->r.efra + 1;
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
@@ -2030,7 +2045,7 @@ static void seq_draw_sfra_efra(Scene *scene, View2D *v2d)
immUnbindProgram();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
typedef struct CacheDrawData {
@@ -2154,7 +2169,7 @@ static void draw_cache_view(const bContext *C)
return;
}
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
@@ -2241,7 +2256,7 @@ static void draw_cache_view(const bContext *C)
draw_cache_view_batch(
userdata.final_out_vbo, userdata.final_out_vert_count, 1.0f, 0.4f, 0.2f, 0.4f);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
/* Draw sequencer timeline. */
@@ -2256,6 +2271,11 @@ void draw_timeline_seq(const bContext *C, ARegion *region)
seq_prefetch_wm_notify(C, scene);
+ GPUViewport *viewport = WM_draw_region_get_viewport(region);
+ GPUFrameBuffer *framebuffer_overlay = GPU_viewport_framebuffer_overlay_get(viewport);
+ GPU_framebuffer_bind_no_srgb(framebuffer_overlay);
+ GPU_depth_test(GPU_DEPTH_NONE);
+
UI_GetThemeColor3fv(TH_BACK, col);
if (ed && ed->metastack.first) {
GPU_clear_color(col[0], col[1], col[2] - 0.1f, 0.0f);
@@ -2263,7 +2283,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 99d4c2d9f1a..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);
@@ -2642,6 +2666,8 @@ static int sequencer_separate_images_exec(bContext *C, wmOperator *op)
seq = ed->seqbasep->first; /* Poll checks this is valid. */
+ BKE_sequencer_prefetch_stop(scene);
+
while (seq) {
if ((seq->flag & SELECT) && (seq->type == SEQ_TYPE_IMAGE) && (seq->len > 1)) {
Sequence *seq_next;
@@ -2837,6 +2863,8 @@ static int sequencer_meta_make_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
+ BKE_sequencer_prefetch_stop(scene);
+
/* Remove all selected from main list, and put in meta. */
seqm = BKE_sequence_alloc(ed->seqbasep, 1, 1, SEQ_TYPE_META); /* Channel number set later. */
@@ -2922,6 +2950,8 @@ static int sequencer_meta_separate_exec(bContext *C, wmOperator *UNUSED(op))
return OPERATOR_CANCELLED;
}
+ BKE_sequencer_prefetch_stop(scene);
+
for (seq = last_seq->seqbase.first; seq != NULL; seq = seq->next) {
BKE_sequence_invalidate_cache_composite(scene, seq);
}
@@ -2946,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;
@@ -3470,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;
@@ -3487,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);
@@ -3538,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);
@@ -3583,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);
@@ -3960,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_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h
index 1d168866e7c..df36453bd2e 100644
--- a/source/blender/editors/space_sequencer/sequencer_intern.h
+++ b/source/blender/editors/space_sequencer/sequencer_intern.h
@@ -60,6 +60,7 @@ float sequence_handle_size_get_clamped(struct Sequence *seq, const float pixelx)
/* void seq_reset_imageofs(struct SpaceSeq *sseq); */
struct ImBuf *sequencer_ibuf_get(struct Main *bmain,
+ struct ARegion *region,
struct Depsgraph *depsgraph,
struct Scene *scene,
struct SpaceSeq *sseq,
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_sequencer/sequencer_view.c b/source/blender/editors/space_sequencer/sequencer_view.c
index c1dac30bcb6..491c475b596 100644
--- a/source/blender/editors/space_sequencer/sequencer_view.c
+++ b/source/blender/editors/space_sequencer/sequencer_view.c
@@ -144,17 +144,17 @@ void SEQUENCER_OT_view_frame(wmOperatorType *ot)
static int sequencer_view_all_preview_exec(bContext *C, wmOperator *UNUSED(op))
{
+ SpaceSeq *sseq = CTX_wm_space_seq(C);
bScreen *screen = CTX_wm_screen(C);
ScrArea *area = CTX_wm_area(C);
#if 0
ARegion *region = CTX_wm_region(C);
- SpaceSeq *sseq = area->spacedata.first;
Scene *scene = CTX_data_scene(C);
#endif
View2D *v2d = UI_view2d_fromcontext(C);
v2d->cur = v2d->tot;
- UI_view2d_curRect_validate(v2d);
+ UI_view2d_curRect_changed(C, v2d);
UI_view2d_sync(screen, area, v2d, V2D_LOCK_COPY);
#if 0
@@ -186,6 +186,8 @@ static int sequencer_view_all_preview_exec(bContext *C, wmOperator *UNUSED(op))
}
#endif
+ sseq->flag |= SEQ_ZOOM_TO_FIT;
+
ED_area_tag_redraw(CTX_wm_area(C));
return OPERATOR_FINISHED;
}
@@ -228,6 +230,8 @@ static int sequencer_view_zoom_ratio_exec(bContext *C, wmOperator *op)
ED_region_tag_redraw(CTX_wm_region(C));
+ UI_view2d_curRect_changed(C, v2d);
+
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c
index b8bb3e4d43b..4a6bd0de60c 100644
--- a/source/blender/editors/space_sequencer/space_sequencer.c
+++ b/source/blender/editors/space_sequencer/space_sequencer.c
@@ -96,7 +96,8 @@ static SpaceLink *sequencer_create(const ScrArea *UNUSED(area), const Scene *sce
sseq->chanshown = 0;
sseq->view = SEQ_VIEW_SEQUENCE;
sseq->mainb = SEQ_DRAW_IMG_IMBUF;
- sseq->flag = SEQ_SHOW_GPENCIL | SEQ_USE_ALPHA | SEQ_SHOW_MARKERS | SEQ_SHOW_FCURVES;
+ sseq->flag = SEQ_SHOW_GPENCIL | SEQ_USE_ALPHA | SEQ_SHOW_MARKERS | SEQ_SHOW_FCURVES |
+ SEQ_ZOOM_TO_FIT;
/* Tool header. */
region = MEM_callocN(sizeof(ARegion), "tool header for sequencer");
@@ -679,6 +680,22 @@ static void sequencer_preview_region_init(wmWindowManager *wm, ARegion *region)
WM_event_add_keymap_handler_v2d_mask(&region->handlers, keymap);
}
+static void sequencer_preview_region_layout(const bContext *C, ARegion *region)
+{
+ SpaceSeq *sseq = CTX_wm_space_seq(C);
+
+ if (sseq->flag & SEQ_ZOOM_TO_FIT) {
+ View2D *v2d = &region->v2d;
+ v2d->cur = v2d->tot;
+ }
+}
+
+static void sequencer_preview_region_view2d_changed(const bContext *C, ARegion *UNUSED(region))
+{
+ SpaceSeq *sseq = CTX_wm_space_seq(C);
+ sseq->flag &= ~SEQ_ZOOM_TO_FIT;
+}
+
static void sequencer_preview_region_draw(const bContext *C, ARegion *region)
{
ScrArea *area = CTX_wm_area(C);
@@ -881,6 +898,8 @@ void ED_spacetype_sequencer(void)
art = MEM_callocN(sizeof(ARegionType), "spacetype sequencer region");
art->regionid = RGN_TYPE_PREVIEW;
art->init = sequencer_preview_region_init;
+ art->layout = sequencer_preview_region_layout;
+ art->on_view2d_changed = sequencer_preview_region_view2d_changed;
art->draw = sequencer_preview_region_draw;
art->listener = sequencer_preview_region_listener;
art->keymapflag = ED_KEYMAP_TOOL | ED_KEYMAP_GIZMO | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES |
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 e9ac6946032..ec5175eae51 100644
--- a/source/blender/editors/space_text/text_draw.c
+++ b/source/blender/editors/space_text/text_draw.c
@@ -1353,11 +1353,9 @@ static void draw_text_decoration(SpaceText *st, ARegion *region)
UI_GetThemeColor4fv(TH_TEXT, highlight_color);
highlight_color[3] = 0.1f;
immUniformColor4fv(highlight_color);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
immRecti(pos, 0, y1, region->winx, y2);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
}
@@ -1662,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) {
@@ -1708,9 +1697,9 @@ void draw_text_main(SpaceText *st, ARegion *region)
UI_GetThemeColor4fv(TH_TEXT, margin_color);
margin_color[3] = 0.2f;
immUniformColor4fv(margin_color);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
immRecti(pos, margin_column_x, 0, margin_column_x + U.pixelsize, region->winy);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
immUnbindProgram();
}
}
diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c
index 201f9dae5d5..688dce3c54e 100644
--- a/source/blender/editors/space_text/text_ops.c
+++ b/source/blender/editors/space_text/text_ops.c
@@ -55,6 +55,7 @@
#ifdef WITH_PYTHON
# include "BPY_extern.h"
+# include "BPY_extern_run.h"
#endif
#include "text_format.h"
@@ -756,7 +757,7 @@ static int text_run_script(bContext *C, ReportList *reports)
void *curl_prev = text->curl;
int curc_prev = text->curc;
- if (BPY_execute_text(C, text, reports, !is_live)) {
+ if (BPY_run_text(C, text, reports, !is_live)) {
if (is_live) {
/* for nice live updates */
WM_event_add_notifier(C, NC_WINDOW | NA_EDITED, NULL);
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/drawobject.c b/source/blender/editors/space_view3d/drawobject.c
index 1ffa653372c..6969ecf197e 100644
--- a/source/blender/editors/space_view3d/drawobject.c
+++ b/source/blender/editors/space_view3d/drawobject.c
@@ -131,9 +131,7 @@ void ED_draw_object_facemap(Depsgraph *depsgraph,
/* Just to create the data to pass to immediate mode, grr! */
const int *facemap_data = CustomData_get_layer(&me->pdata, CD_FACEMAP);
if (facemap_data) {
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
const MVert *mvert = me->mvert;
const MPoly *mpoly = me->mpoly;
@@ -209,6 +207,6 @@ void ED_draw_object_facemap(Depsgraph *depsgraph,
GPU_batch_discard(draw_batch);
GPU_vertbuf_discard(vbo_pos);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
}
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 0442a0f35c9..c4da39bca2f 100644
--- a/source/blender/editors/space_view3d/view3d_draw.c
+++ b/source/blender/editors/space_view3d/view3d_draw.c
@@ -588,9 +588,7 @@ static void drawviewborder(Scene *scene, Depsgraph *depsgraph, ARegion *region,
float alpha = 1.0f;
if (ca->passepartalpha != 1.0f) {
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
alpha = ca->passepartalpha;
}
@@ -609,7 +607,7 @@ static void drawviewborder(Scene *scene, Depsgraph *depsgraph, ARegion *region,
immRectf(shdr_pos, x1i, y1i, x2i, 0.0f);
}
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
immUniformThemeColor3(TH_BACK);
@@ -668,7 +666,7 @@ static void drawviewborder(Scene *scene, Depsgraph *depsgraph, ARegion *region,
/* safety border */
if (ca) {
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
immUniformThemeColorAlpha(TH_VIEW_OVERLAY, 0.75f);
if (ca->dtx & CAM_DTX_CENTER) {
@@ -780,7 +778,7 @@ static void drawviewborder(Scene *scene, Depsgraph *depsgraph, ARegion *region,
imm_draw_box_wire_2d(shdr_pos, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
}
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
immUnbindProgram();
@@ -1027,9 +1025,7 @@ static void draw_view_axis(RegionView3D *rv3d, const rcti *rect)
/* draw axis lines */
GPU_line_width(2.0f);
GPU_line_smooth(true);
- GPU_blend(true);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
@@ -1072,9 +1068,7 @@ static void draw_rotation_guide(const RegionView3D *rv3d)
negate_v3_v3(o, rv3d->ofs);
- GPU_blend(true);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
GPU_depth_mask(false); /* don't overwrite zbuf */
GPUVertFormat *format = immVertexFormat();
@@ -1164,7 +1158,7 @@ static void draw_rotation_guide(const RegionView3D *rv3d)
immEnd();
immUnbindProgram();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
GPU_depth_mask(true);
}
#endif /* WITH_INPUT_NDOF */
@@ -1623,12 +1617,8 @@ void view3d_main_region_draw(const bContext *C, ARegion *region)
BKE_image_free_old_gputextures(bmain);
GPU_pass_cache_garbage_collect();
- /* XXX This is in order to draw UI batches with the DRW
- * old context since we now use it for drawing the entire area. */
- gpu_batch_presets_reset();
-
/* No depth test for drawing action zones afterwards. */
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
v3d->flag |= V3D_INVALID_BACKBUF;
}
@@ -2163,7 +2153,7 @@ static void view3d_opengl_read_Z_pixels(GPUViewport *viewport, rcti *rect, void
{
DefaultTextureList *dtxl = (DefaultTextureList *)GPU_viewport_texture_list_get(viewport);
- GPUFrameBuffer *tmp_fb = GPU_framebuffer_create();
+ GPUFrameBuffer *tmp_fb = GPU_framebuffer_create(__func__);
GPU_framebuffer_texture_attach(tmp_fb, dtxl->depth, 0, 0);
GPU_framebuffer_bind(tmp_fb);
@@ -2329,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_edit.c b/source/blender/editors/space_view3d/view3d_edit.c
index ac9d12cdd58..5912bea4919 100644
--- a/source/blender/editors/space_view3d/view3d_edit.c
+++ b/source/blender/editors/space_view3d/view3d_edit.c
@@ -822,7 +822,7 @@ static void viewrotate_apply(ViewOpsData *vod, const int event_xy[2])
float xaxis[3];
/* Radians per-pixel. */
- const float sensitivity = U.view_rotate_sensitivity_turntable / U.pixelsize;
+ const float sensitivity = U.view_rotate_sensitivity_turntable / U.dpi_fac;
/* Get the 3x3 matrix and its inverse from the quaternion */
quat_to_mat3(m, vod->curr.viewquat);
@@ -2015,7 +2015,7 @@ static void view_zoom_to_window_xy_3d(ARegion *region, float dfac, const int zoo
}
static float viewzoom_scale_value(const rcti *winrct,
- const short viewzoom,
+ const eViewZoom_Style viewzoom,
const bool zoom_invert,
const bool zoom_invert_force,
const int xy_curr[2],
@@ -2038,7 +2038,7 @@ static float viewzoom_scale_value(const rcti *winrct,
fac = (float)(xy_init[1] - xy_curr[1]);
}
- fac /= U.pixelsize;
+ fac /= U.dpi_fac;
if (zoom_invert != zoom_invert_force) {
fac = -fac;
@@ -2055,8 +2055,8 @@ static float viewzoom_scale_value(const rcti *winrct,
BLI_rcti_cent_x(winrct),
BLI_rcti_cent_y(winrct),
};
- float len_new = (5 * U.pixelsize) + ((float)len_v2v2_int(ctr, xy_curr) / U.pixelsize);
- float len_old = (5 * U.pixelsize) + ((float)len_v2v2_int(ctr, xy_init) / U.pixelsize);
+ float len_new = (5 * U.dpi_fac) + ((float)len_v2v2_int(ctr, xy_curr) / U.dpi_fac);
+ float len_old = (5 * U.dpi_fac) + ((float)len_v2v2_int(ctr, xy_init) / U.dpi_fac);
/* intentionally ignore 'zoom_invert' for scale */
if (zoom_invert_force) {
@@ -2066,16 +2066,16 @@ static float viewzoom_scale_value(const rcti *winrct,
zfac = val_orig * (len_old / max_ff(len_new, 1.0f)) / val;
}
else { /* USER_ZOOM_DOLLY */
- float len_new = 5 * U.pixelsize;
- float len_old = 5 * U.pixelsize;
+ float len_new = 5 * U.dpi_fac;
+ float len_old = 5 * U.dpi_fac;
if (U.uiflag & USER_ZOOM_HORIZ) {
- len_new += (winrct->xmax - (xy_curr[0])) / U.pixelsize;
- len_old += (winrct->xmax - (xy_init[0])) / U.pixelsize;
+ len_new += (winrct->xmax - (xy_curr[0])) / U.dpi_fac;
+ len_old += (winrct->xmax - (xy_init[0])) / U.dpi_fac;
}
else {
- len_new += (winrct->ymax - (xy_curr[1])) / U.pixelsize;
- len_old += (winrct->ymax - (xy_init[1])) / U.pixelsize;
+ len_new += (winrct->ymax - (xy_curr[1])) / U.dpi_fac;
+ len_old += (winrct->ymax - (xy_init[1])) / U.dpi_fac;
}
if (zoom_invert != zoom_invert_force) {
@@ -2089,7 +2089,7 @@ static float viewzoom_scale_value(const rcti *winrct,
}
static float viewzoom_scale_value_offset(const rcti *winrct,
- const short viewzoom,
+ const eViewZoom_Style viewzoom,
const bool zoom_invert,
const bool zoom_invert_force,
const int xy_curr[2],
@@ -2120,7 +2120,7 @@ static float viewzoom_scale_value_offset(const rcti *winrct,
static void viewzoom_apply_camera(ViewOpsData *vod,
const int xy[2],
- const short viewzoom,
+ const eViewZoom_Style viewzoom,
const bool zoom_invert,
const bool zoom_to_pos)
{
@@ -2155,7 +2155,7 @@ static void viewzoom_apply_camera(ViewOpsData *vod,
static void viewzoom_apply_3d(ViewOpsData *vod,
const int xy[2],
- const short viewzoom,
+ const eViewZoom_Style viewzoom,
const bool zoom_invert,
const bool zoom_to_pos)
{
@@ -2197,7 +2197,7 @@ static void viewzoom_apply_3d(ViewOpsData *vod,
static void viewzoom_apply(ViewOpsData *vod,
const int xy[2],
- const short viewzoom,
+ const eViewZoom_Style viewzoom,
const bool zoom_invert,
const bool zoom_to_pos)
{
@@ -2248,7 +2248,7 @@ static int viewzoom_modal(bContext *C, wmOperator *op, const wmEvent *event)
const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init");
viewzoom_apply(vod,
&event->x,
- U.viewzoom,
+ (eViewZoom_Style)U.viewzoom,
(U.uiflag & USER_ZOOM_INVERT) != 0,
(use_cursor_init && (U.uiflag & USER_ZOOM_TO_MOUSEPOS)));
if (ED_screen_animation_playing(CTX_wm_manager(C))) {
diff --git a/source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c b/source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c
index 3ce4c8dc9a8..d3294e4e5a9 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c
@@ -241,6 +241,8 @@ static void axis_geom_draw(const wmGizmo *gz,
const float axis_depth_bias = 0.01f;
const float sphere_scale = 1.15f;
+ /* TODO(fclem) Is there a way to get the widget radius? */
+ const float widget_pix_size = 40.0f * U.dpi_fac;
#ifdef USE_AXIS_FONT
struct {
@@ -277,10 +279,22 @@ static void axis_geom_draw(const wmGizmo *gz,
GPU_matrix_ortho_set_z(-clip_range, clip_range);
}
+ UI_draw_roundbox_corner_set(UI_CNR_ALL);
+ GPU_polygon_smooth(false);
+
/* Circle defining active area. */
if (is_active) {
- immUniformColor4fv(color);
- imm_draw_circle_fill_3d(pos_id, 0, 0, 1.0f, DIAL_RESOLUTION);
+ immUnbindProgram();
+
+ float rad = widget_pix_size;
+ GPU_matrix_push();
+ GPU_matrix_scale_1f(1.0f / rad);
+
+ UI_draw_roundbox_4fv(true, -rad, -rad, rad, rad, rad, color);
+
+ GPU_matrix_pop();
+
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
}
GPU_matrix_push();
@@ -343,6 +357,8 @@ static void axis_geom_draw(const wmGizmo *gz,
float v_start[3];
immUnbindProgram();
+ GPU_blend(GPU_BLEND_ALPHA);
+
immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR);
immUniform2fv("viewportSize", &viewport[2]);
immUniform1f("lineWidth", 2.0f * U.pixelsize);
@@ -370,6 +386,34 @@ static void axis_geom_draw(const wmGizmo *gz,
}
/* Axis Ball. */
+#ifdef USE_AXIS_FONT
+ if (select == false) {
+ immUnbindProgram();
+
+ GPU_matrix_push();
+ GPU_matrix_translate_3fv(v_final);
+ GPU_matrix_mul(font.matrix);
+
+ float rad = widget_pix_size * (is_pos ? AXIS_HANDLE_SIZE_FG : AXIS_HANDLE_SIZE_BG);
+
+ /* Black outlines for negative axis balls, otherwise they can be hard to see since
+ * they use a faded color which can be similar to the circle backdrop in tone. */
+ if (is_active && !is_highlight && !is_pos && !select && !(axis_align == axis)) {
+ static const float axis_black_faded[4] = {0.0f, 0.0f, 0.0f, 0.2f};
+ float outline = rad * sphere_scale;
+ UI_draw_roundbox_4fv(
+ true, -outline, -outline, outline, outline, outline, axis_black_faded);
+ }
+
+ const float *col = is_pos_color ? color_current : color_current_fade;
+ UI_draw_roundbox_4fv(true, -rad, -rad, rad, rad, rad, col);
+
+ GPU_matrix_pop();
+
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
+ }
+ else
+#endif
{
GPU_matrix_push();
GPU_matrix_translate_3fv(v_final);
@@ -388,6 +432,7 @@ static void axis_geom_draw(const wmGizmo *gz,
GPU_matrix_scale_1f(1.0 / sphere_scale);
}
+ GPU_batch_program_set_builtin(sphere, GPU_SHADER_3D_UNIFORM_COLOR);
GPU_batch_uniform_4fv(sphere, "color", is_pos_color ? color_current : color_current_fade);
GPU_batch_draw(sphere);
GPU_matrix_pop();
@@ -407,7 +452,7 @@ static void axis_geom_draw(const wmGizmo *gz,
BLF_width_and_height(font.id, axis_str, 2, &offset[0], &offset[1]);
BLF_position(font.id, roundf(offset[0] * -0.5f), roundf(offset[1] * -0.5f), 0);
BLF_draw_ascii(font.id, axis_str, 2);
- GPU_blend(true); /* XXX, blf disables */
+ GPU_blend(GPU_BLEND_ALPHA); /* XXX, blf disables */
GPU_matrix_pop();
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
@@ -465,9 +510,9 @@ static void axis3d_draw_intern(const bContext *C,
UNUSED_VARS(C);
#endif
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
axis_geom_draw(gz, color, select, &draw_info);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
GPU_matrix_pop();
}
@@ -478,9 +523,9 @@ static void gizmo_axis_draw(const bContext *C, wmGizmo *gz)
(void)is_modal;
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
axis3d_draw_intern(C, gz, false, is_highlight);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
static int gizmo_axis_test_select(bContext *UNUSED(C), wmGizmo *gz, const int mval[2])
diff --git a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
index 7799aba5c19..5aba1fecc53 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
@@ -607,7 +607,7 @@ static void gizmo_ruler_draw(const bContext *C, wmGizmo *gz)
GPU_matrix_projection_set(rv3d->winmat);
GPU_matrix_set(rv3d->viewmat);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
const uint shdr_pos_3d = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
@@ -735,7 +735,7 @@ static void gizmo_ruler_draw(const bContext *C, wmGizmo *gz)
rot_90_vec_b[1] = dir_ruler[0];
normalize_v2(rot_90_vec_b);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
if (proj_ok[1] && is_act && (ruler_item->flag & RULERITEM_USE_ANGLE_ACTIVE)) {
GPU_line_width(3.0f);
@@ -781,7 +781,7 @@ static void gizmo_ruler_draw(const bContext *C, wmGizmo *gz)
immEnd();
}
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
/* text */
@@ -800,13 +800,13 @@ static void gizmo_ruler_draw(const bContext *C, wmGizmo *gz)
/* draw text (bg) */
if (proj_ok[1]) {
immUniformColor4fv(color_back);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
immRectf(shdr_pos_2d,
posit[0] - bg_margin,
posit[1] - bg_margin,
posit[0] + bg_margin + numstr_size[0],
posit[1] + bg_margin + numstr_size[1]);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
immUnbindProgram();
@@ -831,7 +831,7 @@ static void gizmo_ruler_draw(const bContext *C, wmGizmo *gz)
normalize_v2(rot_90_vec);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
immUniformColor3ubv(color_wire);
@@ -855,7 +855,7 @@ static void gizmo_ruler_draw(const bContext *C, wmGizmo *gz)
immEnd();
}
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
/* text */
@@ -877,13 +877,13 @@ static void gizmo_ruler_draw(const bContext *C, wmGizmo *gz)
/* draw text (bg) */
if (proj_ok[0] && proj_ok[2]) {
immUniformColor4fv(color_back);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
immRectf(shdr_pos_2d,
posit[0] - bg_margin,
posit[1] - bg_margin,
posit[0] + bg_margin + numstr_size[0],
posit[1] + bg_margin + numstr_size[1]);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
immUnbindProgram();
diff --git a/source/blender/editors/space_view3d/view3d_placement.c b/source/blender/editors/space_view3d/view3d_placement.c
index a828dbc2ee0..6c2f4df7004 100644
--- a/source/blender/editors/space_view3d/view3d_placement.c
+++ b/source/blender/editors/space_view3d/view3d_placement.c
@@ -258,12 +258,10 @@ static void draw_line_loop(const float coords[][3], int coords_len, const float
GPU_vertbuf_attr_set(vert, pos, i, coords[i]);
}
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
GPUBatch *batch = GPU_batch_create_ex(GPU_PRIM_LINE_LOOP, vert, NULL, GPU_BATCH_OWNS_VBO);
GPU_batch_program_set_builtin(batch, GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR);
- GPU_batch_bind(batch);
-
GPU_batch_uniform_4fv(batch, "color", color);
float viewport[4];
@@ -273,10 +271,8 @@ static void draw_line_loop(const float coords[][3], int coords_len, const float
GPU_batch_draw(batch);
- GPU_batch_program_use_end(batch);
-
GPU_batch_discard(batch);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
static void draw_line_pairs(const float coords_a[][3],
@@ -295,12 +291,10 @@ static void draw_line_pairs(const float coords_a[][3],
GPU_vertbuf_attr_set(vert, pos, (i * 2) + 1, coords_b[i]);
}
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
GPUBatch *batch = GPU_batch_create_ex(GPU_PRIM_LINES, vert, NULL, GPU_BATCH_OWNS_VBO);
GPU_batch_program_set_builtin(batch, GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR);
- GPU_batch_bind(batch);
-
GPU_batch_uniform_4fv(batch, "color", color);
float viewport[4];
@@ -310,10 +304,8 @@ static void draw_line_pairs(const float coords_a[][3],
GPU_batch_draw(batch);
- GPU_batch_program_use_end(batch);
-
GPU_batch_discard(batch);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
static void draw_line_bounds(const BoundBox *bounds, const float color[4])
@@ -347,12 +339,10 @@ static void draw_line_bounds(const BoundBox *bounds, const float color[4])
GPU_vertbuf_attr_set(vert, pos, j++, bounds->vec[edges[i][1]]);
}
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
GPUBatch *batch = GPU_batch_create_ex(GPU_PRIM_LINES, vert, NULL, GPU_BATCH_OWNS_VBO);
GPU_batch_program_set_builtin(batch, GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR);
- GPU_batch_bind(batch);
-
GPU_batch_uniform_4fv(batch, "color", color);
float viewport[4];
@@ -362,10 +352,8 @@ static void draw_line_bounds(const BoundBox *bounds, const float color[4])
GPU_batch_draw(batch);
- GPU_batch_program_use_end(batch);
-
GPU_batch_discard(batch);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
static bool calc_bbox(struct InteractivePlaceData *ipd, BoundBox *bounds)
@@ -598,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 ff9673a4262..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();
@@ -1732,6 +1732,8 @@ void ED_view3d_xr_shading_update(wmWindowManager *wm, const View3D *v3d, const S
{
if (v3d->runtime.flag & V3D_RUNTIME_XR_SESSION_ROOT) {
View3DShading *xr_shading = &wm->xr.session_settings.shading;
+ /* Flags that shouldn't be overridden by the 3D View shading. */
+ const int flag_copy = V3D_SHADING_WORLD_ORIENTATION;
BLI_assert(WM_xr_session_exists(&wm->xr));
@@ -1749,7 +1751,9 @@ void ED_view3d_xr_shading_update(wmWindowManager *wm, const View3D *v3d, const S
}
/* Copy shading from View3D to VR view. */
+ const int old_xr_shading_flag = xr_shading->flag;
*xr_shading = v3d->shading;
+ xr_shading->flag = (xr_shading->flag & ~flag_copy) | (old_xr_shading_flag & flag_copy);
if (v3d->shading.prop) {
xr_shading->prop = IDP_CopyProperty(xr_shading->prop);
}
diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c
index 76cce5e725f..11f0e791d14 100644
--- a/source/blender/editors/transform/transform.c
+++ b/source/blender/editors/transform/transform.c
@@ -560,94 +560,6 @@ static void viewRedrawPost(bContext *C, TransInfo *t)
#endif
}
-/* ************************** TRANSFORMATIONS **************************** */
-
-static void view_editmove(ushort UNUSED(event))
-{
-#if 0 // TRANSFORM_FIX_ME
- int refresh = 0;
- /* Regular: Zoom in */
- /* Shift: Scroll up */
- /* Ctrl: Scroll right */
- /* Alt-Shift: Rotate up */
- /* Alt-Ctrl: Rotate right */
-
- /* only work in 3D window for now
- * In the end, will have to send to event to a 2D window handler instead
- */
- if (Trans.flag & T_2D_EDIT) {
- return;
- }
-
- switch (event) {
- case WHEELUPMOUSE:
- if (G.qual & LR_SHIFTKEY) {
- if (G.qual & LR_ALTKEY) {
- G.qual &= ~LR_SHIFTKEY;
- persptoetsen(PAD2);
- G.qual |= LR_SHIFTKEY;
- }
- else {
- persptoetsen(PAD2);
- }
- }
- else if (G.qual & LR_CTRLKEY) {
- if (G.qual & LR_ALTKEY) {
- G.qual &= ~LR_CTRLKEY;
- persptoetsen(PAD4);
- G.qual |= LR_CTRLKEY;
- }
- else {
- persptoetsen(PAD4);
- }
- }
- else if (U.uiflag & USER_WHEELZOOMDIR) {
- persptoetsen(PADMINUS);
- }
- else {
- persptoetsen(PADPLUSKEY);
- }
-
- refresh = 1;
- break;
- case WHEELDOWNMOUSE:
- if (G.qual & LR_SHIFTKEY) {
- if (G.qual & LR_ALTKEY) {
- G.qual &= ~LR_SHIFTKEY;
- persptoetsen(PAD8);
- G.qual |= LR_SHIFTKEY;
- }
- else {
- persptoetsen(PAD8);
- }
- }
- else if (G.qual & LR_CTRLKEY) {
- if (G.qual & LR_ALTKEY) {
- G.qual &= ~LR_CTRLKEY;
- persptoetsen(PAD6);
- G.qual |= LR_CTRLKEY;
- }
- else {
- persptoetsen(PAD6);
- }
- }
- else if (U.uiflag & USER_WHEELZOOMDIR) {
- persptoetsen(PADPLUSKEY);
- }
- else {
- persptoetsen(PADMINUS);
- }
-
- refresh = 1;
- break;
- }
-
- if (refresh) {
- setTransformViewMatrices(&Trans);
- }
-#endif
-}
-
/* ************************************************* */
static bool transform_modal_item_poll(const wmOperator *op, int value)
@@ -770,6 +682,12 @@ wmKeyMap *transform_modal_keymap(wmKeyConfig *keyconf)
{TFM_MODAL_TRANSLATE, "TRANSLATE", 0, "Move", ""},
{TFM_MODAL_ROTATE, "ROTATE", 0, "Rotate", ""},
{TFM_MODAL_RESIZE, "RESIZE", 0, "Resize", ""},
+
+ {TFM_MODAL_AUTOCONSTRAINT,
+ "AUTOCONSTRAIN",
+ 0,
+ "Automatically detects one direction for constraint",
+ ""},
{0, NULL, 0, NULL, NULL},
};
@@ -778,6 +696,21 @@ wmKeyMap *transform_modal_keymap(wmKeyConfig *keyconf)
keymap = WM_modalkeymap_ensure(keyconf, "Transform Modal Map", modal_items);
keymap->poll_modal_item = transform_modal_item_poll;
+ /* Default modal map values:
+ *
+ * \code{.c}
+ * WM_modalkeymap_add_item(keymap, EVT_RETKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_CONFIRM);
+ * WM_modalkeymap_add_item(keymap, EVT_ESCKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_CANCEL);
+ * WM_modalkeymap_add_item(keymap, EVT_PAGEUPKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_AUTOIK_LEN_INC);
+ * WM_modalkeymap_add_item(
+ * keymap, EVT_PAGEDOWNKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_AUTOIK_LEN_DEC);
+ * WM_modalkeymap_add_item(keymap, EVT_GKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_TRANSLATE);
+ * WM_modalkeymap_add_item(keymap, EVT_RKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_ROTATE);
+ * WM_modalkeymap_add_item(keymap, EVT_SKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_RESIZE);
+ * WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_PRESS, KM_ANY, 0, TFM_MODAL_AUTOCONSTRAINT);
+ * \endcode
+ */
+
return keymap;
}
@@ -887,6 +820,7 @@ int transformEvent(TransInfo *t, const wmEvent *event)
handled = true;
}
/* handle modal keymap first */
+ /* enforce redraw of transform when modifiers are used */
else if (event->type == EVT_MODAL_MAP) {
switch (event->val) {
case TFM_MODAL_CANCEL:
@@ -1147,40 +1081,7 @@ int transformEvent(TransInfo *t, const wmEvent *event)
t->redraw |= TREDRAW_SOFT;
}
break;
- /* Those two are only handled in transform's own handler, see T44634! */
- case TFM_MODAL_EDGESLIDE_UP:
- case TFM_MODAL_EDGESLIDE_DOWN:
- default:
- break;
- }
- }
- /* else do non-mapped events */
- else if (event->val == KM_PRESS) {
- switch (event->type) {
- case EVT_ESCKEY:
- case RIGHTMOUSE:
- t->state = TRANS_CANCEL;
- handled = true;
- break;
-
- case EVT_SPACEKEY:
- case EVT_PADENTER:
- case EVT_RETKEY:
- if (event->is_repeat) {
- break;
- }
- t->state = TRANS_CONFIRM;
- handled = true;
- break;
-
- /* enforce redraw of transform when modifiers are used */
- case EVT_LEFTSHIFTKEY:
- case EVT_RIGHTSHIFTKEY:
- t->modifiers |= MOD_CONSTRAINT_PLANE;
- t->redraw |= TREDRAW_HARD;
- handled = true;
- break;
- case MIDDLEMOUSE:
+ case TFM_MODAL_AUTOCONSTRAINT:
if ((t->flag & T_NO_CONSTRAINT) == 0) {
/* exception for switching to dolly, or trackball, in camera view */
if (t->flag & T_CAMERA) {
@@ -1206,59 +1107,16 @@ int transformEvent(TransInfo *t, const wmEvent *event)
handled = true;
}
break;
- case EVT_GKEY:
- if (event->is_repeat) {
- break;
- }
- /* only switch when... */
- if (t->mode != TFM_TRANSLATION && transform_mode_is_changeable(t->mode)) {
- restoreTransObjects(t);
- resetTransModal(t);
- resetTransRestrictions(t);
- transform_mode_init(t, NULL, TFM_TRANSLATION);
- initSnapping(t, NULL); // need to reinit after mode change
- t->redraw |= TREDRAW_HARD;
- handled = true;
- }
- break;
- case EVT_SKEY:
- if (event->is_repeat) {
- break;
- }
- /* only switch when... */
- if (t->mode != TFM_RESIZE && transform_mode_is_changeable(t->mode)) {
- restoreTransObjects(t);
- resetTransModal(t);
- resetTransRestrictions(t);
- transform_mode_init(t, NULL, TFM_RESIZE);
- initSnapping(t, NULL); // need to reinit after mode change
- t->redraw |= TREDRAW_HARD;
- handled = true;
- }
- break;
- case EVT_RKEY:
- if (event->is_repeat) {
- break;
- }
- /* only switch when... */
- if (!(t->options & CTX_TEXTURE)) {
- if (transform_mode_is_changeable(t->mode)) {
- restoreTransObjects(t);
- resetTransModal(t);
- resetTransRestrictions(t);
-
- if (t->mode == TFM_ROTATION) {
- transform_mode_init(t, NULL, TFM_TRACKBALL);
- }
- else {
- transform_mode_init(t, NULL, TFM_ROTATION);
- }
- initSnapping(t, NULL); // need to reinit after mode change
- t->redraw |= TREDRAW_HARD;
- handled = true;
- }
- }
+ /* Those two are only handled in transform's own handler, see T44634! */
+ case TFM_MODAL_EDGESLIDE_UP:
+ case TFM_MODAL_EDGESLIDE_DOWN:
+ default:
break;
+ }
+ }
+ /* Else do non-mapped events. */
+ else if (event->val == KM_PRESS) {
+ switch (event->type) {
case EVT_CKEY:
if (event->is_repeat) {
break;
@@ -1295,17 +1153,6 @@ int transformEvent(TransInfo *t, const wmEvent *event)
handled = true;
}
break;
- case EVT_PAGEUPKEY:
- case WHEELDOWNMOUSE:
- if (t->flag & T_AUTOIK) {
- transform_autoik_update(t, 1);
- }
- else {
- view_editmove(event->type);
- }
- t->redraw = TREDRAW_HARD;
- handled = true;
- break;
case EVT_PADMINUS:
if (event->alt && t->flag & T_PROP_EDIT) {
t->prop_size /= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f;
@@ -1314,17 +1161,6 @@ int transformEvent(TransInfo *t, const wmEvent *event)
handled = true;
}
break;
- case EVT_PAGEDOWNKEY:
- case WHEELUPMOUSE:
- if (t->flag & T_AUTOIK) {
- transform_autoik_update(t, -1);
- }
- else {
- view_editmove(event->type);
- }
- t->redraw = TREDRAW_HARD;
- handled = true;
- break;
case EVT_LEFTALTKEY:
case EVT_RIGHTALTKEY:
if (ELEM(t->spacetype, SPACE_SEQ, SPACE_VIEW3D)) {
@@ -1357,31 +1193,29 @@ int transformEvent(TransInfo *t, const wmEvent *event)
}
else if (event->val == KM_RELEASE) {
switch (event->type) {
- case EVT_LEFTSHIFTKEY:
- case EVT_RIGHTSHIFTKEY:
- t->modifiers &= ~MOD_CONSTRAINT_PLANE;
- t->redraw |= TREDRAW_HARD;
- handled = true;
- break;
-
- case MIDDLEMOUSE:
- if ((t->flag & T_NO_CONSTRAINT) == 0) {
- t->modifiers &= ~MOD_CONSTRAINT_SELECT;
- postSelectConstraint(t);
- t->redraw |= TREDRAW_HARD;
- handled = true;
- }
- break;
case EVT_LEFTALTKEY:
case EVT_RIGHTALTKEY:
+ /* TODO: Modal Map */
if (ELEM(t->spacetype, SPACE_SEQ, SPACE_VIEW3D)) {
t->flag &= ~T_ALT_TRANSFORM;
t->redraw |= TREDRAW_HARD;
handled = true;
}
break;
- default:
+ default: {
+ /* Disable modifiers. */
+ int modifiers = t->modifiers;
+ modifiers &= ~MOD_CONSTRAINT_SELECT;
+ if (modifiers != t->modifiers) {
+ if (t->modifiers & MOD_CONSTRAINT_SELECT) {
+ postSelectConstraint(t);
+ }
+ t->modifiers = modifiers;
+ t->redraw |= TREDRAW_HARD;
+ handled = true;
+ }
break;
+ }
}
/* confirm transform if launch key is released after mouse move */
@@ -1544,16 +1378,14 @@ static void drawAutoKeyWarning(TransInfo *UNUSED(t), ARegion *region)
#endif
/* autokey recording icon... */
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
xco -= U.widget_unit;
yco -= (int)printable_size[1] / 2;
UI_icon_draw(xco, yco, ICON_REC);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
static void drawTransformPixel(const struct bContext *C, ARegion *region, void *arg)
diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h
index 1917d9463f4..1e4992e5f1a 100644
--- a/source/blender/editors/transform/transform.h
+++ b/source/blender/editors/transform/transform.h
@@ -79,7 +79,6 @@ typedef struct TransSnap {
bool project;
bool snap_self;
bool peel;
- bool snap_spatial_grid;
bool use_backface_culling;
char status;
/* Snapped Element Type (currently for objects only). */
@@ -498,7 +497,6 @@ enum {
MOD_PRECISION = 1 << 1,
MOD_SNAP = 1 << 2,
MOD_SNAP_INVERT = 1 << 3,
- MOD_CONSTRAINT_PLANE = 1 << 4,
};
/* use node center for transform instead of upper-left corner.
@@ -576,6 +574,8 @@ enum {
TFM_MODAL_PROPSIZE = 26,
/* node editor insert offset (aka auto-offset) direction toggle */
TFM_MODAL_INSERTOFS_TOGGLE_DIR = 27,
+
+ TFM_MODAL_AUTOCONSTRAINT = 28,
};
bool initTransform(struct bContext *C,
diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c
index d0e37f22236..8fdee0e8eec 100644
--- a/source/blender/editors/transform/transform_constraints.c
+++ b/source/blender/editors/transform/transform_constraints.c
@@ -167,7 +167,7 @@ static void postConstraintChecks(TransInfo *t, float vec[3])
{
mul_m3_v3(t->spacemtx_inv, vec);
- snapGridIncrement(t, vec);
+ transform_snap_increment(t, vec);
if (t->flag & T_NULL_ONE) {
if (!(t->con.mode & CON_AXIS0)) {
@@ -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);
@@ -884,7 +882,7 @@ void drawPropCircle(const struct bContext *C, TransInfo *t)
float viewport[4];
GPU_viewport_size_get_f(viewport);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
immUniform2fv("viewportSize", &viewport[2]);
immUniform1f("lineWidth", 3.0f * U.pixelsize);
@@ -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();
@@ -1081,34 +1079,16 @@ static void setNearestAxis3d(TransInfo *t)
}
if (len[0] <= len[1] && len[0] <= len[2]) {
- if (t->modifiers & MOD_CONSTRAINT_PLANE) {
- t->con.mode |= (CON_AXIS1 | CON_AXIS2);
- BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" locking %s X axis"), t->spacename);
- }
- else {
- t->con.mode |= CON_AXIS0;
- BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" along %s X axis"), t->spacename);
- }
+ t->con.mode |= CON_AXIS0;
+ BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" along %s X axis"), t->spacename);
}
else if (len[1] <= len[0] && len[1] <= len[2]) {
- if (t->modifiers & MOD_CONSTRAINT_PLANE) {
- t->con.mode |= (CON_AXIS0 | CON_AXIS2);
- BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" locking %s Y axis"), t->spacename);
- }
- else {
- t->con.mode |= CON_AXIS1;
- BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" along %s Y axis"), t->spacename);
- }
+ t->con.mode |= CON_AXIS1;
+ BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" along %s Y axis"), t->spacename);
}
else if (len[2] <= len[1] && len[2] <= len[0]) {
- if (t->modifiers & MOD_CONSTRAINT_PLANE) {
- t->con.mode |= (CON_AXIS0 | CON_AXIS1);
- BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" locking %s Z axis"), t->spacename);
- }
- else {
- t->con.mode |= CON_AXIS2;
- BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" along %s Z axis"), t->spacename);
- }
+ t->con.mode |= CON_AXIS2;
+ BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" along %s Z axis"), t->spacename);
}
}
diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c
index 573f4550fec..7ad54a56545 100644
--- a/source/blender/editors/transform/transform_convert_mesh.c
+++ b/source/blender/editors/transform/transform_convert_mesh.c
@@ -507,7 +507,7 @@ static void editmesh_mirror_data_calc(BMEditMesh *em,
index[a] = MEM_mallocN(totvert * sizeof(*index[a]), __func__);
EDBM_verts_mirror_cache_begin_ex(
- em, a, false, test_selected_only, use_topology, TRANSFORM_MAXDIST_MIRROR, index[a]);
+ em, a, false, test_selected_only, true, use_topology, TRANSFORM_MAXDIST_MIRROR, index[a]);
flag = TD_MIRROR_X << a;
BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
diff --git a/source/blender/editors/transform/transform_convert_object.c b/source/blender/editors/transform/transform_convert_object.c
index 2e92b4e5c09..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
*/
@@ -357,7 +357,7 @@ static void set_trans_object_base_flags(TransInfo *t)
/* Makes sure base flags and object flags are identical. */
BKE_scene_base_flag_to_objects(t->view_layer);
/* Make sure depsgraph is here. */
- DEG_graph_relations_update(depsgraph, bmain, scene, view_layer);
+ DEG_graph_relations_update(depsgraph);
/* Clear all flags we need. It will be used to detect dependencies. */
trans_object_base_deps_flag_prepare(view_layer);
/* Traverse all bases and set all possible flags. */
@@ -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_draw_cursors.c b/source/blender/editors/transform/transform_draw_cursors.c
index 95ca5ae0c30..84fc45e2b45 100644
--- a/source/blender/editors/transform/transform_draw_cursors.c
+++ b/source/blender/editors/transform/transform_draw_cursors.c
@@ -191,7 +191,7 @@ void transform_draw_cursor_draw(bContext *UNUSED(C), int x, int y, void *customd
}
GPU_line_smooth(true);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
GPU_matrix_push();
@@ -339,6 +339,6 @@ void transform_draw_cursor_draw(bContext *UNUSED(C), int x, int y, void *customd
GPU_matrix_pop();
GPU_line_smooth(false);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
}
diff --git a/source/blender/editors/transform/transform_gizmo_3d.c b/source/blender/editors/transform/transform_gizmo_3d.c
index 6155042f555..14ef5e87534 100644
--- a/source/blender/editors/transform/transform_gizmo_3d.c
+++ b/source/blender/editors/transform/transform_gizmo_3d.c
@@ -1343,8 +1343,8 @@ 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_blend(true);
+ GPU_depth_test(GPU_DEPTH_NONE);
+ GPU_blend(GPU_BLEND_ALPHA);
GPU_line_smooth(true);
ED_gizmotypes_dial_3d_draw_util(mat_basis,
@@ -1359,8 +1359,8 @@ void drawDial3d(const TransInfo *t)
});
GPU_line_smooth(false);
- GPU_depth_test(true);
- GPU_blend(false);
+ GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
+ GPU_blend(GPU_BLEND_NONE);
}
}
diff --git a/source/blender/editors/transform/transform_mode.c b/source/blender/editors/transform/transform_mode.c
index 495c21bc755..edc0781e18e 100644
--- a/source/blender/editors/transform/transform_mode.c
+++ b/source/blender/editors/transform/transform_mode.c
@@ -1014,7 +1014,7 @@ void ElementResize(TransInfo *t, TransDataContainer *tc, TransData *td, float ma
/* scale stroke thickness */
if (td->val) {
- snapGridIncrement(t, t->values_final);
+ transform_snap_increment(t, t->values_final);
applyNumInput(&t->num, t->values_final);
float ratio = t->values_final[0];
diff --git a/source/blender/editors/transform/transform_mode_baketime.c b/source/blender/editors/transform/transform_mode_baketime.c
index 4e7fc3578ce..235b04b1858 100644
--- a/source/blender/editors/transform/transform_mode_baketime.c
+++ b/source/blender/editors/transform/transform_mode_baketime.c
@@ -67,7 +67,7 @@ static void applyBakeTime(TransInfo *t, const int mval[2])
time = (float)(t->center2d[0] - mval[0]) * fac;
}
- snapGridIncrement(t, &time);
+ transform_snap_increment(t, &time);
applyNumInput(&t->num, &time);
diff --git a/source/blender/editors/transform/transform_mode_bbone_resize.c b/source/blender/editors/transform/transform_mode_bbone_resize.c
index 80a5b307a91..d067c9df418 100644
--- a/source/blender/editors/transform/transform_mode_bbone_resize.c
+++ b/source/blender/editors/transform/transform_mode_bbone_resize.c
@@ -124,7 +124,7 @@ static void applyBoneSize(TransInfo *t, const int UNUSED(mval[2]))
copy_v3_fl(t->values_final, ratio);
- snapGridIncrement(t, t->values_final);
+ transform_snap_increment(t, t->values_final);
if (applyNumInput(&t->num, t->values_final)) {
constraintNumInput(t, t->values_final);
diff --git a/source/blender/editors/transform/transform_mode_bend.c b/source/blender/editors/transform/transform_mode_bend.c
index 3b51626b170..86de40448b7 100644
--- a/source/blender/editors/transform/transform_mode_bend.c
+++ b/source/blender/editors/transform/transform_mode_bend.c
@@ -104,7 +104,7 @@ static void Bend(TransInfo *t, const int UNUSED(mval[2]))
const float radius_snap = 0.1f;
const float snap_hack = (t->snap[1] * data->warp_init_dist) / radius_snap;
values.scale *= snap_hack;
- snapGridIncrement(t, values.vector);
+ transform_snap_increment(t, values.vector);
values.scale /= snap_hack;
}
#endif
diff --git a/source/blender/editors/transform/transform_mode_boneenvelope.c b/source/blender/editors/transform/transform_mode_boneenvelope.c
index b7a34769f5a..7f5a8fedeef 100644
--- a/source/blender/editors/transform/transform_mode_boneenvelope.c
+++ b/source/blender/editors/transform/transform_mode_boneenvelope.c
@@ -53,7 +53,7 @@ static void applyBoneEnvelope(TransInfo *t, const int UNUSED(mval[2]))
ratio = t->values[0];
- snapGridIncrement(t, &ratio);
+ transform_snap_increment(t, &ratio);
applyNumInput(&t->num, &ratio);
diff --git a/source/blender/editors/transform/transform_mode_boneroll.c b/source/blender/editors/transform/transform_mode_boneroll.c
index 1503519c519..8805d54e1f8 100644
--- a/source/blender/editors/transform/transform_mode_boneroll.c
+++ b/source/blender/editors/transform/transform_mode_boneroll.c
@@ -54,7 +54,7 @@ static void applyBoneRoll(TransInfo *t, const int UNUSED(mval[2]))
final = t->values[0];
- snapGridIncrement(t, &final);
+ transform_snap_increment(t, &final);
applyNumInput(&t->num, &final);
diff --git a/source/blender/editors/transform/transform_mode_curveshrinkfatten.c b/source/blender/editors/transform/transform_mode_curveshrinkfatten.c
index 84e4e950804..fd65b019fe0 100644
--- a/source/blender/editors/transform/transform_mode_curveshrinkfatten.c
+++ b/source/blender/editors/transform/transform_mode_curveshrinkfatten.c
@@ -53,7 +53,7 @@ static void applyCurveShrinkFatten(TransInfo *t, const int UNUSED(mval[2]))
ratio = t->values[0];
- snapGridIncrement(t, &ratio);
+ transform_snap_increment(t, &ratio);
applyNumInput(&t->num, &ratio);
diff --git a/source/blender/editors/transform/transform_mode_edge_bevelweight.c b/source/blender/editors/transform/transform_mode_edge_bevelweight.c
index 399cec2d62c..4d6e25dbe34 100644
--- a/source/blender/editors/transform/transform_mode_edge_bevelweight.c
+++ b/source/blender/editors/transform/transform_mode_edge_bevelweight.c
@@ -55,7 +55,7 @@ static void applyBevelWeight(TransInfo *t, const int UNUSED(mval[2]))
CLAMP_MAX(weight, 1.0f);
- snapGridIncrement(t, &weight);
+ transform_snap_increment(t, &weight);
applyNumInput(&t->num, &weight);
diff --git a/source/blender/editors/transform/transform_mode_edge_crease.c b/source/blender/editors/transform/transform_mode_edge_crease.c
index 53c948c742b..a1822d99ff9 100644
--- a/source/blender/editors/transform/transform_mode_edge_crease.c
+++ b/source/blender/editors/transform/transform_mode_edge_crease.c
@@ -55,7 +55,7 @@ static void applyCrease(TransInfo *t, const int UNUSED(mval[2]))
CLAMP_MAX(crease, 1.0f);
- snapGridIncrement(t, &crease);
+ transform_snap_increment(t, &crease);
applyNumInput(&t->num, &crease);
diff --git a/source/blender/editors/transform/transform_mode_edge_rotate_normal.c b/source/blender/editors/transform/transform_mode_edge_rotate_normal.c
index fde0d5b187e..5b929c39915 100644
--- a/source/blender/editors/transform/transform_mode_edge_rotate_normal.c
+++ b/source/blender/editors/transform/transform_mode_edge_rotate_normal.c
@@ -99,7 +99,7 @@ static void applyNormalRotation(TransInfo *t, const int UNUSED(mval[2]))
float angle = t->values[0];
copy_v3_v3(axis, axis_final);
- snapGridIncrement(t, &angle);
+ transform_snap_increment(t, &angle);
applySnapping(t, &angle);
diff --git a/source/blender/editors/transform/transform_mode_edge_seq_slide.c b/source/blender/editors/transform/transform_mode_edge_seq_slide.c
index c1cb4325c09..141f9acdeb4 100644
--- a/source/blender/editors/transform/transform_mode_edge_seq_slide.c
+++ b/source/blender/editors/transform/transform_mode_edge_seq_slide.c
@@ -102,7 +102,7 @@ static void applySeqSlide(TransInfo *t, const int mval[2])
copy_v3_v3(t->values_final, tvec);
}
else {
- // snapGridIncrement(t, t->values);
+ // transform_snap_increment(t, t->values);
applyNumInput(&t->num, t->values);
copy_v3_v3(t->values_final, t->values);
}
diff --git a/source/blender/editors/transform/transform_mode_edge_slide.c b/source/blender/editors/transform/transform_mode_edge_slide.c
index 1886f95beae..7d0e555e362 100644
--- a/source/blender/editors/transform/transform_mode_edge_slide.c
+++ b/source/blender/editors/transform/transform_mode_edge_slide.c
@@ -1147,11 +1147,9 @@ 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(true);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
GPU_matrix_push();
GPU_matrix_mul(TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->obmat);
@@ -1266,9 +1264,9 @@ void drawEdgeSlide(TransInfo *t)
GPU_matrix_pop();
- GPU_blend(false);
+ 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)
@@ -1465,7 +1463,9 @@ static void applyEdgeSlide(TransInfo *t, const int UNUSED(mval[2]))
final = t->values[0];
applySnapping(t, &final);
- snapGridIncrement(t, &final);
+ if (!validSnap(t)) {
+ transform_snap_increment(t, &final);
+ }
/* only do this so out of range values are not displayed */
if (is_constrained) {
diff --git a/source/blender/editors/transform/transform_mode_gpopacity.c b/source/blender/editors/transform/transform_mode_gpopacity.c
index 4712fb7ba01..11c63be156c 100644
--- a/source/blender/editors/transform/transform_mode_gpopacity.c
+++ b/source/blender/editors/transform/transform_mode_gpopacity.c
@@ -53,7 +53,7 @@ static void applyGPOpacity(TransInfo *t, const int UNUSED(mval[2]))
ratio = t->values[0];
- snapGridIncrement(t, &ratio);
+ transform_snap_increment(t, &ratio);
applyNumInput(&t->num, &ratio);
diff --git a/source/blender/editors/transform/transform_mode_gpshrinkfatten.c b/source/blender/editors/transform/transform_mode_gpshrinkfatten.c
index ab9a0aa79ed..c025dbcaccb 100644
--- a/source/blender/editors/transform/transform_mode_gpshrinkfatten.c
+++ b/source/blender/editors/transform/transform_mode_gpshrinkfatten.c
@@ -53,7 +53,7 @@ static void applyGPShrinkFatten(TransInfo *t, const int UNUSED(mval[2]))
ratio = t->values[0];
- snapGridIncrement(t, &ratio);
+ transform_snap_increment(t, &ratio);
applyNumInput(&t->num, &ratio);
diff --git a/source/blender/editors/transform/transform_mode_maskshrinkfatten.c b/source/blender/editors/transform/transform_mode_maskshrinkfatten.c
index 68f3abda85b..3ac35ae7780 100644
--- a/source/blender/editors/transform/transform_mode_maskshrinkfatten.c
+++ b/source/blender/editors/transform/transform_mode_maskshrinkfatten.c
@@ -54,7 +54,7 @@ static void applyMaskShrinkFatten(TransInfo *t, const int UNUSED(mval[2]))
ratio = t->values[0];
- snapGridIncrement(t, &ratio);
+ transform_snap_increment(t, &ratio);
applyNumInput(&t->num, &ratio);
diff --git a/source/blender/editors/transform/transform_mode_push_pull.c b/source/blender/editors/transform/transform_mode_push_pull.c
index 4a2f979ec38..2b17f208e79 100644
--- a/source/blender/editors/transform/transform_mode_push_pull.c
+++ b/source/blender/editors/transform/transform_mode_push_pull.c
@@ -55,7 +55,7 @@ static void applyPushPull(TransInfo *t, const int UNUSED(mval[2]))
distance = t->values[0];
- snapGridIncrement(t, &distance);
+ transform_snap_increment(t, &distance);
applyNumInput(&t->num, &distance);
diff --git a/source/blender/editors/transform/transform_mode_resize.c b/source/blender/editors/transform/transform_mode_resize.c
index 5fb46b30e0d..b4245abcc12 100644
--- a/source/blender/editors/transform/transform_mode_resize.c
+++ b/source/blender/editors/transform/transform_mode_resize.c
@@ -91,7 +91,7 @@ static void applyResize(TransInfo *t, const int UNUSED(mval[2]))
copy_v3_fl(t->values_final, ratio);
- snapGridIncrement(t, t->values_final);
+ transform_snap_increment(t, t->values_final);
if (applyNumInput(&t->num, t->values_final)) {
constraintNumInput(t, t->values_final);
diff --git a/source/blender/editors/transform/transform_mode_rotate.c b/source/blender/editors/transform/transform_mode_rotate.c
index 4fa5dffc473..c0ddd4eb2a6 100644
--- a/source/blender/editors/transform/transform_mode_rotate.c
+++ b/source/blender/editors/transform/transform_mode_rotate.c
@@ -200,7 +200,7 @@ static void applyRotation(TransInfo *t, const int UNUSED(mval[2]))
final = t->values[0];
- snapGridIncrement(t, &final);
+ transform_snap_increment(t, &final);
float axis_final[3];
/* Use the negative axis to match the default Z axis of the view matrix. */
diff --git a/source/blender/editors/transform/transform_mode_shear.c b/source/blender/editors/transform/transform_mode_shear.c
index e508a1fa4c2..18968494395 100644
--- a/source/blender/editors/transform/transform_mode_shear.c
+++ b/source/blender/editors/transform/transform_mode_shear.c
@@ -128,7 +128,7 @@ static void applyShear(TransInfo *t, const int UNUSED(mval[2]))
value = t->values[0];
- snapGridIncrement(t, &value);
+ transform_snap_increment(t, &value);
applyNumInput(&t->num, &value);
diff --git a/source/blender/editors/transform/transform_mode_shrink_fatten.c b/source/blender/editors/transform/transform_mode_shrink_fatten.c
index 6302bc96330..2f221181d12 100644
--- a/source/blender/editors/transform/transform_mode_shrink_fatten.c
+++ b/source/blender/editors/transform/transform_mode_shrink_fatten.c
@@ -56,7 +56,7 @@ static void applyShrinkFatten(TransInfo *t, const int UNUSED(mval[2]))
distance = t->values[0];
- snapGridIncrement(t, &distance);
+ transform_snap_increment(t, &distance);
applyNumInput(&t->num, &distance);
diff --git a/source/blender/editors/transform/transform_mode_skin_resize.c b/source/blender/editors/transform/transform_mode_skin_resize.c
index 23d83050613..665c616bc2b 100644
--- a/source/blender/editors/transform/transform_mode_skin_resize.c
+++ b/source/blender/editors/transform/transform_mode_skin_resize.c
@@ -55,7 +55,7 @@ static void applySkinResize(TransInfo *t, const int UNUSED(mval[2]))
else {
copy_v3_fl(t->values_final, t->values[0]);
- snapGridIncrement(t, t->values_final);
+ transform_snap_increment(t, t->values_final);
if (applyNumInput(&t->num, t->values_final)) {
constraintNumInput(t, t->values_final);
diff --git a/source/blender/editors/transform/transform_mode_tilt.c b/source/blender/editors/transform/transform_mode_tilt.c
index ca0a8818477..5ab23000039 100644
--- a/source/blender/editors/transform/transform_mode_tilt.c
+++ b/source/blender/editors/transform/transform_mode_tilt.c
@@ -54,7 +54,7 @@ static void applyTilt(TransInfo *t, const int UNUSED(mval[2]))
final = t->values[0];
- snapGridIncrement(t, &final);
+ transform_snap_increment(t, &final);
applyNumInput(&t->num, &final);
diff --git a/source/blender/editors/transform/transform_mode_tosphere.c b/source/blender/editors/transform/transform_mode_tosphere.c
index f6c5448a906..e747f0e75d0 100644
--- a/source/blender/editors/transform/transform_mode_tosphere.c
+++ b/source/blender/editors/transform/transform_mode_tosphere.c
@@ -54,7 +54,7 @@ static void applyToSphere(TransInfo *t, const int UNUSED(mval[2]))
ratio = t->values[0];
- snapGridIncrement(t, &ratio);
+ transform_snap_increment(t, &ratio);
applyNumInput(&t->num, &ratio);
diff --git a/source/blender/editors/transform/transform_mode_trackball.c b/source/blender/editors/transform/transform_mode_trackball.c
index ca5a749b275..2656411e8ab 100644
--- a/source/blender/editors/transform/transform_mode_trackball.c
+++ b/source/blender/editors/transform/transform_mode_trackball.c
@@ -93,7 +93,7 @@ static void applyTrackball(TransInfo *t, const int UNUSED(mval[2]))
copy_v2_v2(phi, t->values);
- snapGridIncrement(t, phi);
+ transform_snap_increment(t, phi);
applyNumInput(&t->num, phi);
diff --git a/source/blender/editors/transform/transform_mode_translate.c b/source/blender/editors/transform/transform_mode_translate.c
index c083e1654dc..36be26049d3 100644
--- a/source/blender/editors/transform/transform_mode_translate.c
+++ b/source/blender/editors/transform/transform_mode_translate.c
@@ -362,15 +362,28 @@ static void applyTranslation(TransInfo *t, const int UNUSED(mval[2]))
}
else {
copy_v3_v3(global_dir, t->values);
- if ((t->con.mode & CON_APPLY) == 0) {
- snapGridIncrement(t, global_dir);
- }
-
if (applyNumInput(&t->num, global_dir)) {
removeAspectRatio(t, global_dir);
}
+ else {
+ applySnapping(t, global_dir);
+
+ if (!validSnap(t) && !(t->con.mode & CON_APPLY)) {
+ float dist_sq = FLT_MAX;
+ if (transform_snap_grid(t, global_dir)) {
+ dist_sq = len_squared_v3v3(t->values, global_dir);
+ }
- applySnapping(t, global_dir);
+ /* Check the snap distance to the initial value to work with mixed snap. */
+ float increment_loc[3];
+ copy_v3_v3(increment_loc, t->values);
+ if (transform_snap_increment(t, increment_loc)) {
+ if ((dist_sq == FLT_MAX) || (len_squared_v3v3(t->values, increment_loc) < dist_sq)) {
+ copy_v3_v3(global_dir, increment_loc);
+ }
+ }
+ }
+ }
}
if (t->con.mode & CON_APPLY) {
diff --git a/source/blender/editors/transform/transform_mode_vert_slide.c b/source/blender/editors/transform/transform_mode_vert_slide.c
index 38537194af3..75b973b6b14 100644
--- a/source/blender/editors/transform/transform_mode_vert_slide.c
+++ b/source/blender/editors/transform/transform_mode_vert_slide.c
@@ -390,11 +390,9 @@ void drawVertSlide(TransInfo *t)
const int alpha_shade = -160;
int i;
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
- GPU_blend(true);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
GPU_matrix_push();
GPU_matrix_mul(TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->obmat);
@@ -487,7 +485,7 @@ void drawVertSlide(TransInfo *t)
GPU_matrix_pop();
- GPU_depth_test(true);
+ GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
}
}
}
@@ -589,7 +587,9 @@ static void applyVertSlide(TransInfo *t, const int UNUSED(mval[2]))
final = t->values[0];
applySnapping(t, &final);
- snapGridIncrement(t, &final);
+ if (!validSnap(t)) {
+ transform_snap_increment(t, &final);
+ }
/* only do this so out of range values are not displayed */
if (is_constrained) {
diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c
index ab9548ad52e..d35c2f07482 100644
--- a/source/blender/editors/transform/transform_ops.c
+++ b/source/blender/editors/transform/transform_ops.c
@@ -993,8 +993,7 @@ static void TRANSFORM_OT_tosphere(struct wmOperatorType *ot)
{
/* identifiers */
ot->name = "To Sphere";
- // added "around mesh center" to differentiate between "MESH_OT_vertices_to_sphere()"
- ot->description = "Move selected vertices outward in a spherical shape around mesh center";
+ ot->description = "Move selected items outward in a spherical shape around geometric center";
ot->idname = OP_TOSPHERE;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c
index a700dd320b7..1813acadb9e 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) {
@@ -245,7 +245,7 @@ void drawSnapping(const struct bContext *C, TransInfo *t)
size = 2.5f * UI_GetThemeValuef(TH_VERTEX_SIZE);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
@@ -271,7 +271,7 @@ void drawSnapping(const struct bContext *C, TransInfo *t)
immUnbindProgram();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
}
}
@@ -378,30 +378,14 @@ void applyProject(TransInfo *t)
void applyGridAbsolute(TransInfo *t)
{
- float grid_size = 0.0f;
- GearsType grid_action;
int i;
if (!(activeSnap(t) && (t->tsnap.mode & (SCE_SNAP_MODE_INCREMENT | SCE_SNAP_MODE_GRID)))) {
return;
}
- grid_action = BIG_GEARS;
- if (t->modifiers & MOD_PRECISION) {
- grid_action = SMALL_GEARS;
- }
+ float grid_size = (t->modifiers & MOD_PRECISION) ? t->snap_spatial[2] : t->snap_spatial[1];
- switch (grid_action) {
- case NO_GEARS:
- grid_size = t->snap_spatial[0];
- break;
- case BIG_GEARS:
- grid_size = t->snap_spatial[1];
- break;
- case SMALL_GEARS:
- grid_size = t->snap_spatial[2];
- break;
- }
/* early exit on unusable grid size */
if (grid_size == 0.0f) {
return;
@@ -445,7 +429,12 @@ void applyGridAbsolute(TransInfo *t)
void applySnapping(TransInfo *t, float *vec)
{
/* Each Trans Data already makes the snap to face */
- if (doForceIncrementSnap(t) || (t->tsnap.project && t->tsnap.mode == SCE_SNAP_MODE_FACE)) {
+ if (doForceIncrementSnap(t)) {
+ return;
+ }
+
+ if (t->tsnap.project && t->tsnap.mode == SCE_SNAP_MODE_FACE) {
+ /* The snap has already been resolved for each transdata. */
return;
}
@@ -559,6 +548,12 @@ static void initSnappingMode(TransInfo *t)
}
t->tsnap.mode = ts->snap_mode;
+ if ((t->tsnap.mode & SCE_SNAP_MODE_INCREMENT) && (ts->snap_flag & SCE_SNAP_ABS_GRID) &&
+ (t->mode == TFM_TRANSLATION)) {
+ /* Special case in which snap to increments is transformed to snap to grid. */
+ t->tsnap.mode &= ~SCE_SNAP_MODE_INCREMENT;
+ t->tsnap.mode |= SCE_SNAP_MODE_GRID;
+ }
}
if ((t->spacetype == SPACE_VIEW3D || t->spacetype == SPACE_IMAGE) && (t->flag & T_CAMERA) == 0) {
@@ -600,7 +595,7 @@ static void initSnappingMode(TransInfo *t)
}
}
else {
- /* Grid if snap is not possible */
+ /* Increment if snap is not possible */
t->tsnap.mode = SCE_SNAP_MODE_INCREMENT;
}
}
@@ -613,7 +608,7 @@ static void initSnappingMode(TransInfo *t)
t->tsnap.mode = SCE_SNAP_MODE_GRID; /* Dummy, should we rather add a NOP mode? */
}
else {
- /* Always grid outside of 3D view */
+ /* Always increment outside of 3D view */
t->tsnap.mode = SCE_SNAP_MODE_INCREMENT;
}
@@ -686,11 +681,6 @@ void initSnapping(TransInfo *t, wmOperator *op)
t->tsnap.snap_self = !((t->settings->snap_flag & SCE_SNAP_NO_SELF) != 0);
t->tsnap.peel = ((t->settings->snap_flag & SCE_SNAP_PROJECT) != 0);
}
-
- /* for now only 3d view (others can be added if we want) */
- if (t->spacetype == SPACE_VIEW3D) {
- t->tsnap.snap_spatial_grid = ((t->settings->snap_flag & SCE_SNAP_ABS_GRID) != 0);
- }
}
t->tsnap.target = snap_target;
@@ -832,11 +822,6 @@ void getSnapPoint(const TransInfo *t, float vec[3])
/** \name Calc Snap (Generic)
* \{ */
-static void UNUSED_FUNCTION(CalcSnapGrid)(TransInfo *t, float *UNUSED(vec))
-{
- snapGridIncrementAction(t, t->tsnap.snapPoint, BIG_GEARS);
-}
-
static void CalcSnapGeometry(TransInfo *t, float *UNUSED(vec))
{
if (t->spacetype == SPACE_VIEW3D) {
@@ -1407,77 +1392,160 @@ void snapFrameTransform(TransInfo *t,
/*================================================================*/
-static void applyGridIncrement(
- TransInfo *t, float *val, int max_index, const float fac[3], GearsType action);
-
-void snapGridIncrementAction(TransInfo *t, float *val, GearsType action)
+void snapSequenceBounds(TransInfo *t, const int mval[2])
{
- float fac[3];
+ /* Reuse increment, strictly speaking could be another snap mode, but leave as is. */
+ if (!(t->modifiers & MOD_SNAP_INVERT)) {
+ return;
+ }
+
+ /* Convert to frame range. */
+ float xmouse, ymouse;
+ UI_view2d_region_to_view(&t->region->v2d, mval[0], mval[1], &xmouse, &ymouse);
+ const int frame_curr = round_fl_to_int(xmouse);
- fac[NO_GEARS] = t->snap[0];
- fac[BIG_GEARS] = t->snap[1];
- fac[SMALL_GEARS] = t->snap[2];
+ /* Now find the closest sequence. */
+ const int frame_near = BKE_sequencer_find_next_prev_edit(
+ t->scene, frame_curr, SEQ_SIDE_BOTH, true, false, true);
- applyGridIncrement(t, val, t->idx_max, fac, action);
+ const int frame_snap = transform_convert_sequencer_get_snap_bound(t);
+ t->values[0] = frame_near - frame_snap;
}
-void snapGridIncrement(TransInfo *t, float *val)
+static void snap_grid_apply_ex(
+ TransInfo *t, const int max_index, const float grid_dist, const float loc[3], float r_out[3])
{
- GearsType action;
+ const float *center_global = t->center_global;
+ const float *asp = t->aspect;
+ bool use_local_axis = false;
- /* only do something if using absolute or incremental grid snapping
- * and there is no valid snap point */
- if ((!(t->tsnap.mode & (SCE_SNAP_MODE_INCREMENT | SCE_SNAP_MODE_GRID)) || validSnap(t)) &&
- !doForceIncrementSnap(t)) {
- return;
+ /* use a fallback for cursor selection,
+ * this isn't useful as a global center for absolute grid snapping
+ * since its not based on the position of the selection. */
+ if (t->around == V3D_AROUND_CURSOR) {
+ const TransCenterData *cd = transformCenter_from_type(t, V3D_AROUND_CENTER_MEDIAN);
+ center_global = cd->global;
}
- action = activeSnap(t) ? BIG_GEARS : NO_GEARS;
-
- if (action == BIG_GEARS && (t->modifiers & MOD_PRECISION)) {
- action = SMALL_GEARS;
+ if (t->con.mode & (CON_AXIS0 | CON_AXIS1 | CON_AXIS2)) {
+ use_local_axis = true;
}
- snapGridIncrementAction(t, val, action);
+ for (int i = 0; i <= max_index; i++) {
+ /* do not let unconstrained axis jump to absolute grid increments */
+ if (!(t->con.mode & CON_APPLY) || t->con.mode & (CON_AXIS0 << i)) {
+ const float iter_fac = grid_dist * asp[i];
+
+ if (use_local_axis) {
+ float local_axis[3];
+ float pos_on_axis[3];
+
+ copy_v3_v3(local_axis, t->spacemtx[i]);
+ copy_v3_v3(pos_on_axis, t->spacemtx[i]);
+
+ /* amount of movement on axis from initial pos */
+ mul_v3_fl(pos_on_axis, loc[i]);
+
+ /* actual global position on axis */
+ add_v3_v3(pos_on_axis, center_global);
+
+ float min_dist = INFINITY;
+ for (int j = 0; j < 3; j++) {
+ if (fabs(local_axis[j]) < 0.01f) {
+ /* Ignore very small (normalized) axis changes */
+ continue;
+ }
+
+ /* closest point on grid */
+ float grid_p = iter_fac * roundf(pos_on_axis[j] / iter_fac);
+ float dist_p = fabs((grid_p - pos_on_axis[j]) / local_axis[j]);
+
+ /* The amount of distance needed to travel along the
+ * local axis to snap to the closest grid point */
+ /* in the global j axis direction */
+ float move_dist = (grid_p - center_global[j]) / local_axis[j];
+
+ if (dist_p < min_dist) {
+ min_dist = dist_p;
+ r_out[i] = move_dist;
+ }
+ }
+ }
+ else {
+ r_out[i] = iter_fac * roundf((loc[i] + center_global[i]) / iter_fac) - center_global[i];
+ }
+ }
+ }
}
-void snapSequenceBounds(TransInfo *t, const int mval[2])
+static void snap_grid_apply(TransInfo *t, int max_index, const float grid_dist, float *r_val)
{
- /* Reuse increment, strictly speaking could be another snap mode, but leave as is. */
- if (!(t->modifiers & MOD_SNAP_INVERT)) {
+ BLI_assert(t->tsnap.mode & SCE_SNAP_MODE_GRID);
+ BLI_assert(max_index <= 2);
+
+ /* Early bailing out if no need to snap */
+ if (grid_dist == 0.0f) {
return;
}
- /* Convert to frame range. */
- float xmouse, ymouse;
- UI_view2d_region_to_view(&t->region->v2d, mval[0], mval[1], &xmouse, &ymouse);
- const int frame_curr = round_fl_to_int(xmouse);
+ /* absolute snapping on grid based on global center.
+ * for now only 3d view (others can be added if we want) */
+ snap_grid_apply_ex(t, max_index, grid_dist, r_val, r_val);
+}
- /* Now find the closest sequence. */
- const int frame_near = BKE_sequencer_find_next_prev_edit(
- t->scene, frame_curr, SEQ_SIDE_BOTH, true, false, true);
+bool transform_snap_grid(TransInfo *t, float *val)
+{
+ if ((!(t->tsnap.mode & SCE_SNAP_MODE_GRID)) || validSnap(t)) {
+ /* Don't do grid snapping if there is a valid snap point. */
+ return false;
+ }
- const int frame_snap = transform_convert_sequencer_get_snap_bound(t);
- t->values[0] = frame_near - frame_snap;
+ if (t->spacetype != SPACE_VIEW3D) {
+ return false;
+ }
+
+ if (t->mode != TFM_TRANSLATION) {
+ return false;
+ }
+
+ float grid_dist = activeSnap(t) ? (t->modifiers & MOD_PRECISION) ? t->snap[2] : t->snap[1] :
+ t->snap[0];
+
+ snap_grid_apply(t, t->idx_max, grid_dist, val);
+ return true;
}
-static void applyGridIncrement(
- TransInfo *t, float *val, int max_index, const float fac[3], GearsType action)
+static void snap_increment_apply_ex(TransInfo *UNUSED(t),
+ const int max_index,
+ const float increment_val,
+ const float aspect[3],
+ const float loc[3],
+ float r_out[3])
{
- float asp_local[3] = {1, 1, 1};
- const bool use_aspect = ELEM(t->mode, TFM_TRANSLATION);
- const float *asp = use_aspect ? t->aspect : asp_local;
- int i;
+ /* relative snapping in fixed increments */
+ for (int i = 0; i <= max_index; i++) {
+ const float iter_fac = increment_val * aspect[i];
+ r_out[i] = iter_fac * roundf(loc[i] / iter_fac);
+ }
+}
- BLI_assert((t->tsnap.mode & (SCE_SNAP_MODE_INCREMENT | SCE_SNAP_MODE_GRID)) ||
- doForceIncrementSnap(t));
+static void snap_increment_apply(TransInfo *t,
+ int max_index,
+ const float increment_dist,
+ float *r_val)
+{
+ BLI_assert((t->tsnap.mode & SCE_SNAP_MODE_INCREMENT) || doForceIncrementSnap(t));
BLI_assert(max_index <= 2);
/* Early bailing out if no need to snap */
- if (fac[action] == 0.0f) {
+ if (increment_dist == 0.0f) {
return;
}
+ float asp_local[3] = {1, 1, 1};
+ const bool use_aspect = ELEM(t->mode, TFM_TRANSLATION);
+ const float *asp = use_aspect ? t->aspect : asp_local;
+
if (use_aspect) {
/* custom aspect for fcurve */
if (t->spacetype == SPACE_GRAPH) {
@@ -1491,76 +1559,26 @@ static void applyGridIncrement(
}
}
- /* absolute snapping on grid based on global center */
- if ((t->tsnap.snap_spatial_grid) && (t->mode == TFM_TRANSLATION)) {
- const float *center_global = t->center_global;
- bool use_local_axis = false;
-
- /* use a fallback for cursor selection,
- * this isn't useful as a global center for absolute grid snapping
- * since its not based on the position of the selection. */
- if (t->around == V3D_AROUND_CURSOR) {
- const TransCenterData *cd = transformCenter_from_type(t, V3D_AROUND_CENTER_MEDIAN);
- center_global = cd->global;
- }
-
- if (t->con.mode & (CON_AXIS0 | CON_AXIS1 | CON_AXIS2)) {
- use_local_axis = true;
- }
-
- for (i = 0; i <= max_index; i++) {
- /* do not let unconstrained axis jump to absolute grid increments */
- if (!(t->con.mode & CON_APPLY) || t->con.mode & (CON_AXIS0 << i)) {
- const float iter_fac = fac[action] * asp[i];
-
- if (use_local_axis) {
- float local_axis[3];
- float pos_on_axis[3];
-
- copy_v3_v3(local_axis, t->spacemtx[i]);
- copy_v3_v3(pos_on_axis, t->spacemtx[i]);
-
- /* amount of movement on axis from initial pos */
- mul_v3_fl(pos_on_axis, val[i]);
-
- /* actual global position on axis */
- add_v3_v3(pos_on_axis, center_global);
+ snap_increment_apply_ex(t, max_index, increment_dist, asp, r_val, r_val);
+}
- float min_dist = INFINITY;
- for (int j = 0; j < 3; j++) {
- if (fabs(local_axis[j]) < 0.01f) {
- /* Ignore very small (normalized) axis changes */
- continue;
- }
+bool transform_snap_increment(TransInfo *t, float *val)
+{
+ if (!(t->tsnap.mode & SCE_SNAP_MODE_INCREMENT) && !doForceIncrementSnap(t)) {
+ return false;
+ }
- /* closest point on grid */
- float grid_p = iter_fac * roundf(pos_on_axis[j] / iter_fac);
- float dist_p = fabs((grid_p - pos_on_axis[j]) / local_axis[j]);
+ if (t->spacetype != SPACE_VIEW3D && validSnap(t)) {
+ /* Only do something if using absolute or incremental grid snapping
+ * and there is no valid snap point. */
+ return false;
+ }
- /* The amount of distance needed to travel along the
- * local axis to snap to the closest grid point */
- /* in the global j axis direction */
- float move_dist = (grid_p - center_global[j]) / local_axis[j];
+ float increment_dist = activeSnap(t) ? (t->modifiers & MOD_PRECISION) ? t->snap[2] : t->snap[1] :
+ t->snap[0];
- if (dist_p < min_dist) {
- min_dist = dist_p;
- val[i] = move_dist;
- }
- }
- }
- else {
- val[i] = iter_fac * roundf((val[i] + center_global[i]) / iter_fac) - center_global[i];
- }
- }
- }
- }
- else {
- /* relative snapping in fixed increments */
- for (i = 0; i <= max_index; i++) {
- const float iter_fac = fac[action] * asp[i];
- val[i] = iter_fac * roundf(val[i] / iter_fac);
- }
- }
+ snap_increment_apply(t, t->idx_max, increment_dist, val);
+ return true;
}
/** \} */
diff --git a/source/blender/editors/transform/transform_snap.h b/source/blender/editors/transform/transform_snap.h
index b97a9dc882c..5bee572c603 100644
--- a/source/blender/editors/transform/transform_snap.h
+++ b/source/blender/editors/transform/transform_snap.h
@@ -54,16 +54,10 @@ void snapFrameTransform(struct TransInfo *t,
/* return args */
float *r_val);
-typedef enum {
- NO_GEARS = 0,
- BIG_GEARS = 1,
- SMALL_GEARS = 2,
-} GearsType;
-
bool transformModeUseSnap(const TransInfo *t);
-void snapGridIncrement(TransInfo *t, float *val);
-void snapGridIncrementAction(TransInfo *t, float *val, GearsType action);
+bool transform_snap_increment(TransInfo *t, float *val);
+bool transform_snap_grid(TransInfo *t, float *val);
void snapSequenceBounds(TransInfo *t, const int mval[2]);
diff --git a/source/blender/editors/util/ed_util_imbuf.c b/source/blender/editors/util/ed_util_imbuf.c
index 3e85342e0d7..a6eed5d54d1 100644
--- a/source/blender/editors/util/ed_util_imbuf.c
+++ b/source/blender/editors/util/ed_util_imbuf.c
@@ -305,7 +305,7 @@ static void sequencer_sample_apply(bContext *C, wmOperator *op, const wmEvent *e
Scene *scene = CTX_data_scene(C);
SpaceSeq *sseq = (SpaceSeq *)CTX_wm_space_data(C);
ARegion *region = CTX_wm_region(C);
- ImBuf *ibuf = sequencer_ibuf_get(bmain, depsgraph, scene, sseq, CFRA, 0, NULL);
+ ImBuf *ibuf = sequencer_ibuf_get(bmain, region, depsgraph, scene, sseq, CFRA, 0, NULL);
ImageSampleInfo *info = op->customdata;
float fx, fy;
diff --git a/source/blender/editors/util/numinput.c b/source/blender/editors/util/numinput.c
index 384da6fb931..041b2fec00b 100644
--- a/source/blender/editors/util/numinput.c
+++ b/source/blender/editors/util/numinput.c
@@ -38,7 +38,7 @@
#include "WM_types.h"
#ifdef WITH_PYTHON
-# include "BPY_extern.h"
+# include "BPY_extern_run.h"
#endif
#include "ED_numinput.h"
@@ -294,10 +294,10 @@ bool user_string_to_number(bContext *C,
bUnit_ReplaceString(
str_unit_convert, sizeof(str_unit_convert), str, unit_scale, unit->system, type);
- return BPY_execute_string_as_number(C, NULL, str_unit_convert, error_prefix, r_value);
+ return BPY_run_string_as_number(C, NULL, str_unit_convert, error_prefix, r_value);
}
- int success = BPY_execute_string_as_number(C, NULL, str, error_prefix, r_value);
+ int success = BPY_run_string_as_number(C, NULL, str, error_prefix, r_value);
*r_value *= bUnit_PreferredInputUnitScalar(unit, type);
*r_value /= unit_scale;
return success;
diff --git a/source/blender/editors/uvedit/uvedit_draw.c b/source/blender/editors/uvedit/uvedit_draw.c
index df8d3cfb8db..044fca2310c 100644
--- a/source/blender/editors/uvedit/uvedit_draw.c
+++ b/source/blender/editors/uvedit/uvedit_draw.c
@@ -237,10 +237,10 @@ static void draw_uvs_shadow(SpaceImage *sima,
if (edges) {
if (sima->flag & SI_SMOOTH_UV) {
GPU_line_smooth(true);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
}
else if (overlay_alpha < 1.0f) {
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
}
col[3] = overlay_alpha;
@@ -250,10 +250,10 @@ static void draw_uvs_shadow(SpaceImage *sima,
if (sima->flag & SI_SMOOTH_UV) {
GPU_line_smooth(false);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
else if (overlay_alpha < 1.0f) {
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
}
}
@@ -288,10 +288,6 @@ static void draw_uvs_texpaint(const Scene *scene, Object *ob, Depsgraph *depsgra
uint idx = 0;
bool prev_ma_match = (mpoly->mat_nr == (ob_eval->actcol - 1));
- GPU_matrix_bind(geom->interface);
- GPU_shader_set_srgb_uniform(geom->interface);
- GPU_batch_bind(geom);
-
/* TODO(fclem): If drawcall count becomes a problem in the future
* we can use multi draw indirect drawcalls for this.
* (not implemented in GPU module at the time of writing). */
@@ -299,7 +295,7 @@ static void draw_uvs_texpaint(const Scene *scene, Object *ob, Depsgraph *depsgra
bool ma_match = (mpoly->mat_nr == (ob_eval->actcol - 1));
if (ma_match != prev_ma_match) {
if (ma_match == false) {
- GPU_batch_draw_advanced(geom, draw_start, idx - draw_start, 0, 0);
+ GPU_batch_draw_range(geom, draw_start, idx - draw_start);
}
else {
draw_start = idx;
@@ -309,10 +305,8 @@ static void draw_uvs_texpaint(const Scene *scene, Object *ob, Depsgraph *depsgra
prev_ma_match = ma_match;
}
if (prev_ma_match == true) {
- GPU_batch_draw_advanced(geom, draw_start, idx - draw_start, 0, 0);
+ GPU_batch_draw_range(geom, draw_start, idx - draw_start);
}
-
- GPU_batch_program_use_end(geom);
}
else {
GPU_batch_draw(geom);
@@ -355,8 +349,7 @@ static void draw_uvs(SpaceImage *sima,
interpedges = (ts->uv_selectmode == UV_SELECT_VERTEX);
}
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
if (batch->faces) {
GPU_batch_program_set_builtin(batch->faces,
@@ -366,7 +359,7 @@ static void draw_uvs(SpaceImage *sima,
GPU_SHADER_2D_UV_FACES);
if (!draw_stretch) {
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
UI_GetThemeColor4fv(TH_FACE, col1);
UI_GetThemeColor4fv(TH_FACE_SELECT, col2);
@@ -393,16 +386,16 @@ static void draw_uvs(SpaceImage *sima,
GPU_batch_draw(batch->faces);
if (!draw_stretch) {
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
}
if (batch->edges) {
if (sima->flag & SI_SMOOTH_UV) {
GPU_line_smooth(true);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
}
else if (overlay_alpha < 1.0f) {
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
}
{
@@ -455,24 +448,26 @@ static void draw_uvs(SpaceImage *sima,
}
col1[3] = overlay_alpha;
+ 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);
}
if (sima->flag & SI_SMOOTH_UV) {
GPU_line_smooth(false);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
else if (overlay_alpha < 1.0f) {
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
}
@@ -482,7 +477,7 @@ static void draw_uvs(SpaceImage *sima,
const float point_size = UI_GetThemeValuef(TH_VERTEX_SIZE);
const float pinned_col[4] = {1.0f, 0.0f, 0.0f, 1.0f}; /* TODO Theme? */
UI_GetThemeColor4fv(TH_VERTEX, col1);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
GPU_program_point_size(true);
GPU_batch_program_set_builtin(batch->verts, GPU_SHADER_2D_UV_VERTS);
@@ -491,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
@@ -500,9 +500,13 @@ 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_blend(false);
+ 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);
}
if (batch->facedots) {
@@ -557,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/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c
index 6332a6ab48f..6dac31794b4 100644
--- a/source/blender/editors/uvedit/uvedit_smart_stitch.c
+++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c
@@ -1765,7 +1765,7 @@ static void stitch_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), void
pos_id = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
}
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
/* Static Tris */
if (stitch_preview->static_tris) {
@@ -1829,7 +1829,7 @@ static void stitch_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), void
stitch_draw_vbo(vbo_line, GPU_PRIM_LINES, col);
}
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
/* draw stitch vert/lines preview */
if (ssc->mode == STITCH_VERT) {
diff --git a/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp b/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp
index 68b5b4baeca..4b2fd7b6735 100644
--- a/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp
+++ b/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp
@@ -869,8 +869,7 @@ Render *BlenderStrokeRenderer::RenderScene(Render * /*re*/, bool render)
#endif
Render *freestyle_render = RE_NewSceneRender(freestyle_scene);
- ViewLayer *view_layer = (ViewLayer *)freestyle_scene->view_layers.first;
- DEG_graph_relations_update(freestyle_depsgraph, freestyle_bmain, freestyle_scene, view_layer);
+ DEG_graph_relations_update(freestyle_depsgraph);
RE_RenderFreestyleStrokes(
freestyle_render, freestyle_bmain, freestyle_scene, render && get_stroke_count() > 0);
diff --git a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp
index 388c2f35774..2446bfc13dc 100644
--- a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp
+++ b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp
@@ -655,7 +655,7 @@ void FRS_do_stroke_rendering(Render *re, ViewLayer *view_layer)
ViewLayer *scene_view_layer = (ViewLayer *)BLI_findstring(
&re->scene->view_layers, view_layer->name, offsetof(ViewLayer, name));
Depsgraph *depsgraph = DEG_graph_new(re->main, re->scene, scene_view_layer, DAG_EVAL_RENDER);
- BKE_scene_graph_update_for_newframe(depsgraph, re->main);
+ BKE_scene_graph_update_for_newframe(depsgraph);
// prepare Freestyle:
// - load mesh
diff --git a/source/blender/freestyle/intern/system/PythonInterpreter.h b/source/blender/freestyle/intern/system/PythonInterpreter.h
index bae69aa0a42..50ebacbd6e8 100644
--- a/source/blender/freestyle/intern/system/PythonInterpreter.h
+++ b/source/blender/freestyle/intern/system/PythonInterpreter.h
@@ -42,7 +42,7 @@ extern "C" {
#include "BKE_report.h"
#include "BKE_text.h"
-#include "BPY_extern.h"
+#include "BPY_extern_run.h"
#include "bpy_capi_utils.h"
@@ -68,12 +68,12 @@ class PythonInterpreter : public Interpreter {
BKE_reports_clear(reports);
char *fn = const_cast<char *>(filename.c_str());
#if 0
- bool ok = BPY_execute_filepath(_context, fn, reports);
+ bool ok = BPY_run_filepath(_context, fn, reports);
#else
bool ok;
Text *text = BKE_text_load(&_freestyle_bmain, fn, G_MAIN->name);
if (text) {
- ok = BPY_execute_text(_context, text, reports, false);
+ ok = BPY_run_text(_context, text, reports, false);
BKE_id_delete(&_freestyle_bmain, text);
}
else {
@@ -102,7 +102,7 @@ class PythonInterpreter : public Interpreter {
BKE_reports_clear(reports);
- if (!BPY_execute_string(_context, NULL, str.c_str())) {
+ if (!BPY_run_string_eval(_context, NULL, str.c_str())) {
BPy_errors_to_report(reports);
cerr << "\nError executing Python script from PythonInterpreter::interpretString" << endl;
cerr << "Name: " << name << endl;
@@ -122,7 +122,7 @@ class PythonInterpreter : public Interpreter {
BKE_reports_clear(reports);
- if (!BPY_execute_text(_context, text, reports, false)) {
+ if (!BPY_run_text(_context, text, reports, false)) {
cerr << "\nError executing Python script from PythonInterpreter::interpretText" << endl;
cerr << "Name: " << name << endl;
cerr << "Errors: " << endl;
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/gpencil_modifiers/intern/MOD_gpencilarmature.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c
index 60c3877b89a..41365da1153 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c
@@ -128,7 +128,10 @@ static void deformStroke(GpencilModifierData *md,
BKE_gpencil_stroke_geometry_update(gps);
}
-static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData *md, Object *ob)
+static void bakeModifier(Main *UNUSED(bmain),
+ Depsgraph *depsgraph,
+ GpencilModifierData *md,
+ Object *ob)
{
Scene *scene = DEG_get_evaluated_scene(depsgraph);
Object *object_eval = DEG_get_evaluated_object(depsgraph, ob);
@@ -147,7 +150,7 @@ static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData
* NOTE: this assumes that we don't want armature animation on non-keyframed frames
*/
CFRA = gpf->framenum;
- BKE_scene_graph_update_for_newframe(depsgraph, bmain);
+ BKE_scene_graph_update_for_newframe(depsgraph);
/* compute armature effects on this frame */
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
@@ -158,7 +161,7 @@ static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData
/* return frame state and DB to original state */
CFRA = oldframe;
- BKE_scene_graph_update_for_newframe(depsgraph, bmain);
+ BKE_scene_graph_update_for_newframe(depsgraph);
}
static bool isDisabled(GpencilModifierData *md, int UNUSED(userRenderParams))
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c
index 30ac18c64ae..1608d7a2d4c 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c
@@ -280,7 +280,10 @@ static void deformStroke(GpencilModifierData *md,
/* FIXME: Ideally we be doing this on a copy of the main depsgraph
* (i.e. one where we don't have to worry about restoring state)
*/
-static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData *md, Object *ob)
+static void bakeModifier(Main *UNUSED(bmain),
+ Depsgraph *depsgraph,
+ GpencilModifierData *md,
+ Object *ob)
{
HookGpencilModifierData *mmd = (HookGpencilModifierData *)md;
Scene *scene = DEG_get_evaluated_scene(depsgraph);
@@ -297,7 +300,7 @@ static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData
* NOTE: this assumes that we don't want hook animation on non-keyframed frames
*/
CFRA = gpf->framenum;
- BKE_scene_graph_update_for_newframe(depsgraph, bmain);
+ BKE_scene_graph_update_for_newframe(depsgraph);
/* compute hook effects on this frame */
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
@@ -308,7 +311,7 @@ static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData
/* return frame state and DB to original state */
CFRA = oldframe;
- BKE_scene_graph_update_for_newframe(depsgraph, bmain);
+ BKE_scene_graph_update_for_newframe(depsgraph);
}
static void freeData(GpencilModifierData *md)
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c
index 0f5fc4d5cf6..2d2f39f0189 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c
@@ -126,7 +126,10 @@ static void deformStroke(GpencilModifierData *md,
/* FIXME: Ideally we be doing this on a copy of the main depsgraph
* (i.e. one where we don't have to worry about restoring state)
*/
-static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData *md, Object *ob)
+static void bakeModifier(Main *UNUSED(bmain),
+ Depsgraph *depsgraph,
+ GpencilModifierData *md,
+ Object *ob)
{
LatticeGpencilModifierData *mmd = (LatticeGpencilModifierData *)md;
Scene *scene = DEG_get_evaluated_scene(depsgraph);
@@ -144,7 +147,7 @@ static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData
* NOTE: this assumes that we don't want lattice animation on non-keyframed frames
*/
CFRA = gpf->framenum;
- BKE_scene_graph_update_for_newframe(depsgraph, bmain);
+ BKE_scene_graph_update_for_newframe(depsgraph);
/* recalculate lattice data */
BKE_gpencil_lattice_init(ob);
@@ -165,7 +168,7 @@ static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData
/* return frame state and DB to original state */
CFRA = oldframe;
- BKE_scene_graph_update_for_newframe(depsgraph, bmain);
+ BKE_scene_graph_update_for_newframe(depsgraph);
}
static void freeData(GpencilModifierData *md)
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c
index c99ca64325c..3554cf2e4eb 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c
@@ -174,7 +174,10 @@ static void generateStrokes(GpencilModifierData *md, Depsgraph *depsgraph, Objec
}
}
-static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData *md, Object *ob)
+static void bakeModifier(Main *UNUSED(bmain),
+ Depsgraph *depsgraph,
+ GpencilModifierData *md,
+ Object *ob)
{
Scene *scene = DEG_get_evaluated_scene(depsgraph);
bGPdata *gpd = ob->data;
@@ -184,7 +187,7 @@ static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
/* apply mirror effects on this frame */
CFRA = gpf->framenum;
- BKE_scene_graph_update_for_newframe(depsgraph, bmain);
+ BKE_scene_graph_update_for_newframe(depsgraph);
/* compute mirror effects on this frame */
generate_geometry(md, ob, gpl, gpf);
@@ -193,7 +196,7 @@ static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData
/* return frame state and DB to original state */
CFRA = oldframe;
- BKE_scene_graph_update_for_newframe(depsgraph, bmain);
+ BKE_scene_graph_update_for_newframe(depsgraph);
}
static bool isDisabled(GpencilModifierData *UNUSED(md), int UNUSED(userRenderParams))
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c
index 9d10fcbe49b..9e86ea6ecdc 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c
@@ -259,7 +259,10 @@ static void deformStroke(GpencilModifierData *md,
/* FIXME: Ideally we be doing this on a copy of the main depsgraph
* (i.e. one where we don't have to worry about restoring state)
*/
-static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData *md, Object *ob)
+static void bakeModifier(Main *UNUSED(bmain),
+ Depsgraph *depsgraph,
+ GpencilModifierData *md,
+ Object *ob)
{
TintGpencilModifierData *mmd = (TintGpencilModifierData *)md;
Scene *scene = DEG_get_evaluated_scene(depsgraph);
@@ -276,7 +279,7 @@ static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData
* NOTE: this assumes that we don't want animation on non-keyframed frames
*/
CFRA = gpf->framenum;
- BKE_scene_graph_update_for_newframe(depsgraph, bmain);
+ BKE_scene_graph_update_for_newframe(depsgraph);
/* compute effects on this frame */
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
@@ -287,7 +290,7 @@ static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData
/* return frame state and DB to original state */
CFRA = oldframe;
- BKE_scene_graph_update_for_newframe(depsgraph, bmain);
+ BKE_scene_graph_update_for_newframe(depsgraph);
}
static void freeData(GpencilModifierData *md)
diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt
index 80ea28aca3c..cf0399b776d 100644
--- a/source/blender/gpu/CMakeLists.txt
+++ b/source/blender/gpu/CMakeLists.txt
@@ -55,7 +55,6 @@ set(INC_SYS
)
set(SRC
- intern/gpu_attr_binding.cc
intern/gpu_batch.cc
intern/gpu_batch_presets.c
intern/gpu_batch_utils.c
@@ -63,6 +62,7 @@ set(SRC
intern/gpu_codegen.c
intern/gpu_context.cc
intern/gpu_debug.cc
+ intern/gpu_drawlist.cc
intern/gpu_element.cc
intern/gpu_extensions.cc
intern/gpu_framebuffer.cc
@@ -74,7 +74,6 @@ set(SRC
intern/gpu_matrix.cc
intern/gpu_node_graph.c
intern/gpu_platform.cc
- intern/gpu_primitive.c
intern/gpu_select.c
intern/gpu_select_pick.c
intern/gpu_select_sample_query.c
@@ -83,14 +82,23 @@ 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
+ opengl/gl_batch.cc
opengl/gl_context.cc
+ opengl/gl_drawlist.cc
+ opengl/gl_debug.cc
+ opengl/gl_framebuffer.cc
+ opengl/gl_immediate.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
GPU_batch.h
GPU_batch_presets.h
GPU_batch_utils.h
@@ -98,6 +106,7 @@ set(SRC
GPU_common.h
GPU_context.h
GPU_debug.h
+ GPU_drawlist.h
GPU_element.h
GPU_extensions.h
GPU_framebuffer.h
@@ -112,30 +121,45 @@ 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
- intern/gpu_attr_binding_private.h
intern/gpu_backend.hh
- intern/gpu_batch_private.h
+ intern/gpu_batch_private.hh
intern/gpu_codegen.h
intern/gpu_context_private.hh
+ intern/gpu_drawlist_private.hh
+ intern/gpu_framebuffer_private.hh
+ intern/gpu_immediate_private.hh
intern/gpu_material_library.h
intern/gpu_matrix_private.h
intern/gpu_node_graph.h
- intern/gpu_primitive_private.h
intern/gpu_private.h
intern/gpu_select_private.h
- intern/gpu_shader_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
+ opengl/gl_batch.hh
opengl/gl_context.hh
+ opengl/gl_debug.hh
+ opengl/gl_drawlist.hh
+ opengl/gl_framebuffer.hh
+ opengl/gl_immediate.hh
+ opengl/gl_primitive.hh
+ opengl/gl_shader.hh
+ opengl/gl_shader_interface.hh
+ opengl/gl_state.hh
+ opengl/gl_texture.hh
+ opengl/gl_uniform_buffer.hh
+ opengl/gl_vertex_array.hh
)
set(LIB
diff --git a/source/blender/gpu/GPU_batch.h b/source/blender/gpu/GPU_batch.h
index 855214c279c..b45898f9c6a 100644
--- a/source/blender/gpu/GPU_batch.h
+++ b/source/blender/gpu/GPU_batch.h
@@ -26,85 +26,82 @@
#pragma once
+#include "BLI_utildefines.h"
+
#include "GPU_element.h"
#include "GPU_shader.h"
-#include "GPU_shader_interface.h"
#include "GPU_vertex_buffer.h"
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef enum {
- GPU_BATCH_UNUSED,
- GPU_BATCH_READY_TO_FORMAT,
- GPU_BATCH_READY_TO_BUILD,
- GPU_BATCH_BUILDING,
- GPU_BATCH_READY_TO_DRAW,
-} GPUBatchPhase;
-
#define GPU_BATCH_VBO_MAX_LEN 6
#define GPU_BATCH_INST_VBO_MAX_LEN 2
#define GPU_BATCH_VAO_STATIC_LEN 3
#define GPU_BATCH_VAO_DYN_ALLOC_COUNT 16
-typedef struct GPUBatch {
- /* geometry */
+typedef enum eGPUBatchFlag {
+ /** Invalid default state. */
+ GPU_BATCH_INVALID = 0,
+
+ /** GPUVertBuf ownership. (One bit per vbo) */
+ GPU_BATCH_OWNS_VBO = (1 << 0),
+ GPU_BATCH_OWNS_VBO_MAX = (GPU_BATCH_OWNS_VBO << (GPU_BATCH_VBO_MAX_LEN - 1)),
+ GPU_BATCH_OWNS_VBO_ANY = ((GPU_BATCH_OWNS_VBO << GPU_BATCH_VBO_MAX_LEN) - 1),
+ /** Instance GPUVertBuf ownership. (One bit per vbo) */
+ GPU_BATCH_OWNS_INST_VBO = (GPU_BATCH_OWNS_VBO_MAX << 1),
+ GPU_BATCH_OWNS_INST_VBO_MAX = (GPU_BATCH_OWNS_INST_VBO << (GPU_BATCH_INST_VBO_MAX_LEN - 1)),
+ GPU_BATCH_OWNS_INST_VBO_ANY = ((GPU_BATCH_OWNS_INST_VBO << GPU_BATCH_INST_VBO_MAX_LEN) - 1) &
+ ~GPU_BATCH_OWNS_VBO_ANY,
+ /** GPUIndexBuf ownership. */
+ GPU_BATCH_OWNS_INDEX = (GPU_BATCH_OWNS_INST_VBO_MAX << 1),
+
+ /** Has been initialized. At least one VBO is set. */
+ GPU_BATCH_INIT = (1 << 16),
+ /** Batch is initialized but it's VBOs are still being populated. (optional) */
+ GPU_BATCH_BUILDING = (1 << 16),
+ /** Cached data need to be rebuild. (VAO, PSO, ...) */
+ GPU_BATCH_DIRTY = (1 << 17),
+} eGPUBatchFlag;
+
+#define GPU_BATCH_OWNS_NONE GPU_BATCH_INVALID
+
+BLI_STATIC_ASSERT(GPU_BATCH_OWNS_INDEX < GPU_BATCH_INIT,
+ "eGPUBatchFlag: Error: status flags are shadowed by the ownership bits!")
+
+ENUM_OPERATORS(eGPUBatchFlag)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/**
+ * IMPORTANT: Do not allocate manually as the real struct is bigger (i.e: GLBatch). This is only
+ * the common and "public" part of the struct. Use the provided allocator.
+ * TODO(fclem) Make the content of this struct hidden and expose getters/setters.
+ **/
+typedef struct GPUBatch {
/** verts[0] is required, others can be NULL */
GPUVertBuf *verts[GPU_BATCH_VBO_MAX_LEN];
/** Instance attributes. */
GPUVertBuf *inst[GPU_BATCH_INST_VBO_MAX_LEN];
/** NULL if element list not needed */
GPUIndexBuf *elem;
- uint32_t gl_prim_type;
-
- /* cached values (avoid dereferencing later) */
- uint32_t vao_id;
- uint32_t program;
- const struct GPUShaderInterface *interface;
-
- /* book-keeping */
- uint owns_flag;
- /** used to free all vaos. this implies all vaos were created under the same context. */
- struct GPUContext *context;
- GPUBatchPhase phase;
- bool program_in_use;
-
- /* Vao management: remembers all geometry state (vertex attribute bindings & element buffer)
- * for each shader interface. Start with a static number of vaos and fallback to dynamic count
- * if necessary. Once a batch goes dynamic it does not go back. */
- bool is_dynamic_vao_count;
- union {
- /** Static handle count */
- struct {
- const struct GPUShaderInterface *interfaces[GPU_BATCH_VAO_STATIC_LEN];
- uint32_t vao_ids[GPU_BATCH_VAO_STATIC_LEN];
- } static_vaos;
- /** Dynamic handle count */
- struct {
- uint count;
- const struct GPUShaderInterface **interfaces;
- uint32_t *vao_ids;
- } dynamic_vaos;
- };
-
- /* XXX This is the only solution if we want to have some data structure using
- * batches as key to identify nodes. We must destroy these nodes with this callback. */
- void (*free_callback)(struct GPUBatch *, void *);
- void *callback_data;
+ /** Bookeeping. */
+ eGPUBatchFlag flag;
+ /** Type of geometry to draw. */
+ GPUPrimType prim_type;
+ /** Current assigned shader. DEPRECATED. Here only for uniform binding. */
+ struct GPUShader *shader;
} GPUBatch;
-enum {
- GPU_BATCH_OWNS_VBO = (1 << 0),
- /* each vbo index gets bit-shifted */
- GPU_BATCH_OWNS_INSTANCES = (1 << 30),
- GPU_BATCH_OWNS_INDEX = (1u << 31u),
-};
-
-GPUBatch *GPU_batch_calloc(uint count);
-GPUBatch *GPU_batch_create_ex(GPUPrimType, GPUVertBuf *, GPUIndexBuf *, uint owns_flag);
-void GPU_batch_init_ex(GPUBatch *, GPUPrimType, GPUVertBuf *, GPUIndexBuf *, uint owns_flag);
+GPUBatch *GPU_batch_calloc(void);
+GPUBatch *GPU_batch_create_ex(GPUPrimType prim,
+ GPUVertBuf *vert,
+ GPUIndexBuf *elem,
+ eGPUBatchFlag own_flag);
+void GPU_batch_init_ex(GPUBatch *batch,
+ GPUPrimType prim,
+ GPUVertBuf *vert,
+ GPUIndexBuf *elem,
+ eGPUBatchFlag own_flag);
void GPU_batch_copy(GPUBatch *batch_dst, GPUBatch *batch_src);
#define GPU_batch_create(prim, verts, elem) GPU_batch_create_ex(prim, verts, elem, 0)
@@ -115,10 +112,6 @@ void GPU_batch_clear(GPUBatch *);
void GPU_batch_discard(GPUBatch *); /* verts & elem are not discarded */
-void GPU_batch_vao_cache_clear(GPUBatch *);
-
-void GPU_batch_callback_free_set(GPUBatch *, void (*callback)(GPUBatch *, void *), void *);
-
void GPU_batch_instbuf_set(GPUBatch *, GPUVertBuf *, bool own_vbo); /* Instancing */
void GPU_batch_elembuf_set(GPUBatch *batch, GPUIndexBuf *elem, bool own_ibo);
@@ -128,42 +121,39 @@ int GPU_batch_vertbuf_add_ex(GPUBatch *, GPUVertBuf *, bool own_vbo);
#define GPU_batch_vertbuf_add(batch, verts) GPU_batch_vertbuf_add_ex(batch, verts, false)
void GPU_batch_set_shader(GPUBatch *batch, GPUShader *shader);
-void GPU_batch_set_shader_no_bind(GPUBatch *batch, GPUShader *shader);
void GPU_batch_program_set_imm_shader(GPUBatch *batch);
void GPU_batch_program_set_builtin(GPUBatch *batch, eGPUBuiltinShader shader_id);
void GPU_batch_program_set_builtin_with_config(GPUBatch *batch,
eGPUBuiltinShader shader_id,
eGPUShaderConfig sh_cfg);
-/* Entire batch draws with one shader program, but can be redrawn later with another program. */
-/* Vertex shader's inputs must be compatible with the batch's vertex format. */
-
-void GPU_batch_program_use_begin(GPUBatch *); /* call before Batch_Uniform (temp hack?) */
-void GPU_batch_program_use_end(GPUBatch *);
-
-void GPU_batch_uniform_1ui(GPUBatch *, const char *name, uint value);
-void GPU_batch_uniform_1i(GPUBatch *, const char *name, int value);
-void GPU_batch_uniform_1b(GPUBatch *, const char *name, bool value);
-void GPU_batch_uniform_1f(GPUBatch *, const char *name, float value);
-void GPU_batch_uniform_2f(GPUBatch *, const char *name, float x, float y);
-void GPU_batch_uniform_3f(GPUBatch *, const char *name, float x, float y, float z);
-void GPU_batch_uniform_4f(GPUBatch *, const char *name, float x, float y, float z, float w);
-void GPU_batch_uniform_2fv(GPUBatch *, const char *name, const float data[2]);
-void GPU_batch_uniform_3fv(GPUBatch *, const char *name, const float data[3]);
-void GPU_batch_uniform_4fv(GPUBatch *, const char *name, const float data[4]);
-void GPU_batch_uniform_2fv_array(GPUBatch *, const char *name, const int len, const float *data);
-void GPU_batch_uniform_4fv_array(GPUBatch *, const char *name, const int len, const float *data);
-void GPU_batch_uniform_mat4(GPUBatch *, const char *name, const float data[4][4]);
-
-void GPU_batch_draw(GPUBatch *);
-
-/* Needs to be called before GPU_batch_draw_advanced. */
-void GPU_batch_bind(GPUBatch *);
+
+/* Will only work after setting the batch program. */
+/* TODO(fclem) Theses needs to be replaced by GPU_shader_uniform_* with explicit shader. */
+#define GPU_batch_uniform_1i(batch, name, x) GPU_shader_uniform_1i((batch)->shader, name, x);
+#define GPU_batch_uniform_1b(batch, name, x) GPU_shader_uniform_1b((batch)->shader, name, x);
+#define GPU_batch_uniform_1f(batch, name, x) GPU_shader_uniform_1f((batch)->shader, name, x);
+#define GPU_batch_uniform_2f(batch, name, x, y) GPU_shader_uniform_2f((batch)->shader, name, x, y);
+#define GPU_batch_uniform_3f(batch, name, x, y, z) \
+ GPU_shader_uniform_3f((batch)->shader, name, x, y, z);
+#define GPU_batch_uniform_4f(batch, name, x, y, z, w) \
+ GPU_shader_uniform_4f((batch)->shader, name, x, y, z, w);
+#define GPU_batch_uniform_2fv(batch, name, val) GPU_shader_uniform_2fv((batch)->shader, name, val);
+#define GPU_batch_uniform_3fv(batch, name, val) GPU_shader_uniform_3fv((batch)->shader, name, val);
+#define GPU_batch_uniform_4fv(batch, name, val) GPU_shader_uniform_4fv((batch)->shader, name, val);
+#define GPU_batch_uniform_2fv_array(batch, name, len, val) \
+ GPU_shader_uniform_2fv_array((batch)->shader, name, len, val);
+#define GPU_batch_uniform_4fv_array(batch, name, len, val) \
+ GPU_shader_uniform_4fv_array((batch)->shader, name, len, val);
+#define GPU_batch_uniform_mat4(batch, name, val) \
+ GPU_shader_uniform_mat4((batch)->shader, name, val);
+
+void GPU_batch_draw(GPUBatch *batch);
+void GPU_batch_draw_range(GPUBatch *batch, int v_first, int v_count);
+void GPU_batch_draw_instanced(GPUBatch *batch, int i_count);
+
/* This does not bind/unbind shader and does not call GPU_matrix_bind() */
void GPU_batch_draw_advanced(GPUBatch *, int v_first, int v_count, int i_first, int i_count);
-/* Does not even need batch */
-void GPU_draw_primitive(GPUPrimType, int v_count);
-
#if 0 /* future plans */
/* Can multiple batches share a GPUVertBuf? Use ref count? */
@@ -199,19 +189,6 @@ GPUBatch *create_BatchInGeneral(GPUPrimType, VertexBufferStuff, ElementListStuff
#endif /* future plans */
-/**
- * #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.
- */
-typedef struct GPUDrawList GPUDrawList;
-
-GPUDrawList *GPU_draw_list_create(int length);
-void GPU_draw_list_discard(GPUDrawList *list);
-void GPU_draw_list_init(GPUDrawList *list, GPUBatch *batch);
-void GPU_draw_list_command_add(
- GPUDrawList *list, int v_first, int v_count, int i_first, int i_count);
-void GPU_draw_list_submit(GPUDrawList *list);
-
void gpu_batch_init(void);
void gpu_batch_exit(void);
diff --git a/source/blender/gpu/GPU_batch_presets.h b/source/blender/gpu/GPU_batch_presets.h
index 1674cf776db..19f200fecbf 100644
--- a/source/blender/gpu/GPU_batch_presets.h
+++ b/source/blender/gpu/GPU_batch_presets.h
@@ -43,14 +43,13 @@ struct GPUBatch *GPU_batch_preset_panel_drag_widget(const float pixelsize,
const float col_dark[4],
const float width) ATTR_WARN_UNUSED_RESULT;
+struct GPUBatch *GPU_batch_preset_quad(void);
+
void gpu_batch_presets_init(void);
void gpu_batch_presets_register(struct GPUBatch *preset_batch);
bool gpu_batch_presets_unregister(struct GPUBatch *preset_batch);
-void gpu_batch_presets_reset(void);
void gpu_batch_presets_exit(void);
-void GPU_batch_presets_reset(void);
-
#ifdef __cplusplus
}
#endif
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_debug.h b/source/blender/gpu/GPU_debug.h
index be822056678..09dc02c0fc6 100644
--- a/source/blender/gpu/GPU_debug.h
+++ b/source/blender/gpu/GPU_debug.h
@@ -30,9 +30,6 @@ extern "C" {
/* prints something if debug mode is active only */
void GPU_print_error_debug(const char *str);
-/* inserts a debug marker message for the debug context messaging system */
-void GPU_string_marker(const char *str);
-
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/gpu/intern/gpu_attr_binding_private.h b/source/blender/gpu/GPU_drawlist.h
index 4d359343c38..27f70da8cf8 100644
--- a/source/blender/gpu/intern/gpu_attr_binding_private.h
+++ b/source/blender/gpu/GPU_drawlist.h
@@ -13,32 +13,33 @@
* 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.
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup gpu
*
- * GPU vertex attribute binding
+ * 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
-#include "GPU_shader_interface.h"
-#include "GPU_vertex_format.h"
-
#ifdef __cplusplus
extern "C" {
#endif
-/* TODO(fclem) remove, use shaderface directly. */
-void AttrBinding_clear(GPUAttrBinding *binding);
+struct GPUBatch;
+
+typedef void *GPUDrawList; /* Opaque pointer. */
+
+/* Create a list with at least length drawcalls. Length can affect performance. */
+GPUDrawList GPU_draw_list_create(int length);
+void GPU_draw_list_discard(GPUDrawList list);
-void get_attr_locations(const GPUVertFormat *format,
- GPUAttrBinding *binding,
- const GPUShaderInterface *shaderface);
-uint read_attr_location(const GPUAttrBinding *binding, uint a_idx);
+void GPU_draw_list_append(GPUDrawList list, GPUBatch *batch, int i_first, int i_count);
+void GPU_draw_list_submit(GPUDrawList list);
#ifdef __cplusplus
}
diff --git a/source/blender/gpu/GPU_element.h b/source/blender/gpu/GPU_element.h
index 3d5195b12fc..5cf85b4ea0e 100644
--- a/source/blender/gpu/GPU_element.h
+++ b/source/blender/gpu/GPU_element.h
@@ -54,6 +54,8 @@ typedef struct GPUIndexBuf {
};
} GPUIndexBuf;
+GPUIndexBuf *GPU_indexbuf_calloc(void);
+
void GPU_indexbuf_use(GPUIndexBuf *);
uint GPU_indexbuf_size_get(const GPUIndexBuf *);
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 9dc07fefd4e..e6648c69de7 100644
--- a/source/blender/gpu/GPU_framebuffer.h
+++ b/source/blender/gpu/GPU_framebuffer.h
@@ -19,6 +19,13 @@
/** \file
* \ingroup gpu
+ *
+ * GPU Framebuffer
+ * - this is a wrapper for an OpenGL framebuffer object (FBO). in practice
+ * multiple FBO's may be created.
+ * - actual FBO creation & config is deferred until GPU_framebuffer_bind or
+ * GPU_framebuffer_check_valid to allow creation & config while another
+ * opengl context is bound (since FBOs are not shared between ogl contexts).
*/
#pragma once
@@ -41,32 +48,28 @@ typedef enum eGPUFrameBufferBits {
} eGPUFrameBufferBits;
typedef enum eGPUBackBuffer {
- GPU_BACKBUFFER = 0,
+ GPU_BACKBUFFER_LEFT = 0,
GPU_BACKBUFFER_RIGHT,
- GPU_BACKBUFFER_LEFT,
} eGPUBackBuffer;
-typedef struct GPUFrameBuffer GPUFrameBuffer;
-typedef struct GPUOffScreen GPUOffScreen;
+/** Opaque pointer hiding blender::gpu::FrameBuffer. */
+typedef struct GPUFrameBuffer {
+ void *dummy;
+} GPUFrameBuffer;
-/* GPU Framebuffer
- * - this is a wrapper for an OpenGL framebuffer object (FBO). in practice
- * multiple FBO's may be created, to get around limitations on the number
- * of attached textures and the dimension requirements.
- * - actual FBO creation & config is deferred until GPU_framebuffer_bind or
- * GPU_framebuffer_check_valid to allow creation & config while another
- * opengl context is bound (since FBOs are not shared between ogl contexts).
- */
+typedef struct GPUOffScreen GPUOffScreen;
-GPUFrameBuffer *GPU_framebuffer_create(void);
+GPUFrameBuffer *GPU_framebuffer_create(const char *name);
void GPU_framebuffer_free(GPUFrameBuffer *fb);
void GPU_framebuffer_bind(GPUFrameBuffer *fb);
+void GPU_framebuffer_bind_no_srgb(GPUFrameBuffer *fb);
void GPU_framebuffer_restore(void);
bool GPU_framebuffer_bound(GPUFrameBuffer *fb);
bool GPU_framebuffer_check_valid(GPUFrameBuffer *fb, char err_out[256]);
GPUFrameBuffer *GPU_framebuffer_active_get(void);
+GPUFrameBuffer *GPU_framebuffer_back_get(void);
#define GPU_FRAMEBUFFER_FREE_SAFE(fb) \
do { \
@@ -79,13 +82,10 @@ GPUFrameBuffer *GPU_framebuffer_active_get(void);
/* Framebuffer setup : You need to call GPU_framebuffer_bind for these
* to be effective. */
-void GPU_framebuffer_texture_attach(GPUFrameBuffer *fb, struct GPUTexture *tex, int slot, int mip);
-void GPU_framebuffer_texture_layer_attach(
- GPUFrameBuffer *fb, struct GPUTexture *tex, int slot, int layer, int mip);
-void GPU_framebuffer_texture_cubeface_attach(
- GPUFrameBuffer *fb, struct GPUTexture *tex, int slot, int face, int mip);
+void GPU_framebuffer_texture_attach_ex(GPUFrameBuffer *gpu_fb,
+ GPUAttachment attachement,
+ int slot);
void GPU_framebuffer_texture_detach(GPUFrameBuffer *fb, struct GPUTexture *tex);
-void GPU_framebuffer_texture_detach_slot(GPUFrameBuffer *fb, struct GPUTexture *tex, int type);
/**
* How to use #GPU_framebuffer_ensure_config().
@@ -109,7 +109,7 @@ void GPU_framebuffer_texture_detach_slot(GPUFrameBuffer *fb, struct GPUTexture *
#define GPU_framebuffer_ensure_config(_fb, ...) \
do { \
if (*(_fb) == NULL) { \
- *(_fb) = GPU_framebuffer_create(); \
+ *(_fb) = GPU_framebuffer_create(#_fb); \
} \
GPUAttachment config[] = __VA_ARGS__; \
GPU_framebuffer_config_array(*(_fb), config, (sizeof(config) / sizeof(GPUAttachment))); \
@@ -150,9 +150,17 @@ void GPU_framebuffer_config_array(GPUFrameBuffer *fb, const GPUAttachment *confi
_tex, _face, _mip, \
}
+void GPU_framebuffer_texture_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, int mip);
+void GPU_framebuffer_texture_layer_attach(
+ GPUFrameBuffer *fb, GPUTexture *tex, int slot, int layer, int mip);
+void GPU_framebuffer_texture_cubeface_attach(
+ GPUFrameBuffer *fb, GPUTexture *tex, int slot, int face, int mip);
+
/* Framebuffer operations */
void GPU_framebuffer_viewport_set(GPUFrameBuffer *fb, int x, int y, int w, int h);
+void GPU_framebuffer_viewport_get(GPUFrameBuffer *fb, int r_viewport[4]);
+void GPU_framebuffer_viewport_reset(GPUFrameBuffer *fb);
void GPU_framebuffer_clear(GPUFrameBuffer *fb,
eGPUFrameBufferBits buffers,
@@ -224,7 +232,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 b8957ff1819..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;
@@ -112,15 +112,6 @@ typedef enum eGPUMatFlag {
GPU_MATFLAG_BARYCENTRIC = (1 << 4),
} eGPUMatFlag;
-typedef enum eGPUBlendMode {
- GPU_BLEND_SOLID = 0,
- GPU_BLEND_ADD = 1,
- GPU_BLEND_ALPHA = 2,
- GPU_BLEND_CLIP = 4,
- GPU_BLEND_ALPHA_SORT = 8,
- GPU_BLEND_ALPHA_TO_COVERAGE = 16,
-} eGPUBlendMode;
-
typedef struct GPUNodeStack {
eGPUType type;
float vec[4];
@@ -167,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);
@@ -178,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,
@@ -210,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_matrix.h b/source/blender/gpu/GPU_matrix.h
index 7b94a535a30..aad6ae9e2ba 100644
--- a/source/blender/gpu/GPU_matrix.h
+++ b/source/blender/gpu/GPU_matrix.h
@@ -29,7 +29,7 @@
extern "C" {
#endif
-struct GPUShaderInterface;
+struct GPUShader;
void GPU_matrix_reset(void); /* to Identity transform & empty stack */
@@ -147,7 +147,7 @@ const float (*GPU_matrix_normal_get(float m[3][3]))[3];
const float (*GPU_matrix_normal_inverse_get(float m[3][3]))[3];
/* set uniform values for currently bound shader */
-void GPU_matrix_bind(const struct GPUShaderInterface *);
+void GPU_matrix_bind(struct GPUShader *shader);
bool GPU_matrix_dirty_get(void); /* since last bind */
/* own working polygon offset */
diff --git a/source/blender/gpu/GPU_primitive.h b/source/blender/gpu/GPU_primitive.h
index e910e81fac1..781a10f3636 100644
--- a/source/blender/gpu/GPU_primitive.h
+++ b/source/blender/gpu/GPU_primitive.h
@@ -56,8 +56,11 @@ typedef enum {
GPU_PRIM_CLASS_ANY = GPU_PRIM_CLASS_POINT | GPU_PRIM_CLASS_LINE | GPU_PRIM_CLASS_SURFACE,
} GPUPrimClass;
-GPUPrimClass GPU_primtype_class(GPUPrimType);
-bool GPU_primtype_belongs_to_class(GPUPrimType, GPUPrimClass);
+/**
+ * TODO Improve error checking by validating that the shader is suited for this primitive type.
+ * GPUPrimClass GPU_primtype_class(GPUPrimType);
+ * bool GPU_primtype_belongs_to_class(GPUPrimType, GPUPrimClass);
+ **/
#ifdef __cplusplus
}
diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h
index f782742ae53..33fef266c42 100644
--- a/source/blender/gpu/GPU_shader.h
+++ b/source/blender/gpu/GPU_shader.h
@@ -27,14 +27,12 @@
extern "C" {
#endif
-typedef struct GPUShader GPUShader;
-struct GPUShaderInterface;
struct GPUTexture;
-struct GPUUniformBuffer;
+struct GPUUniformBuf;
+struct GPUVertBuf;
-/* GPU Shader
- * - only for fragment shaders now
- * - must call texture bind before setting a texture as uniform! */
+/** Opaque type hidding blender::gpu::Shader */
+typedef struct GPUShader GPUShader;
typedef enum eGPUShaderTFBType {
GPU_SHADER_TFB_NONE = 0, /* Transform feedback unsupported. */
@@ -63,17 +61,16 @@ GPUShader *GPU_shader_create_ex(const char *vertexcode,
const char **tf_names,
const int tf_count,
const char *shader_name);
-GPUShader *GPU_shader_load_from_binary(const char *binary,
- const int binary_format,
- const int binary_len,
- const char *shname);
+
struct GPU_ShaderCreateFromArray_Params {
const char **vert, **geom, **frag, **defs;
};
struct GPUShader *GPU_shader_create_from_arrays_impl(
- const struct GPU_ShaderCreateFromArray_Params *params);
+ const struct GPU_ShaderCreateFromArray_Params *params, const char *func, int line);
+
#define GPU_shader_create_from_arrays(...) \
- GPU_shader_create_from_arrays_impl(&(const struct GPU_ShaderCreateFromArray_Params)__VA_ARGS__)
+ GPU_shader_create_from_arrays_impl( \
+ &(const struct GPU_ShaderCreateFromArray_Params)__VA_ARGS__, __func__, __LINE__)
void GPU_shader_free(GPUShader *shader);
@@ -81,12 +78,47 @@ void GPU_shader_bind(GPUShader *shader);
void GPU_shader_unbind(void);
/* Returns true if transform feedback was successfully enabled. */
-bool GPU_shader_transform_feedback_enable(GPUShader *shader, unsigned int vbo_id);
+bool GPU_shader_transform_feedback_enable(GPUShader *shader, struct GPUVertBuf *vertbuf);
void GPU_shader_transform_feedback_disable(GPUShader *shader);
int GPU_shader_get_program(GPUShader *shader);
-void GPU_shader_set_srgb_uniform(const struct GPUShaderInterface *interface);
+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);
int GPU_shader_get_builtin_uniform(GPUShader *shader, int builtin);
@@ -104,9 +136,20 @@ void GPU_shader_uniform_vector_int(
void GPU_shader_uniform_float(GPUShader *shader, int location, float value);
void GPU_shader_uniform_int(GPUShader *shader, int location, int value);
-int GPU_shader_get_attribute(GPUShader *shader, const char *name);
+void GPU_shader_uniform_1i(GPUShader *sh, const char *name, int value);
+void GPU_shader_uniform_1b(GPUShader *sh, const char *name, bool value);
+void GPU_shader_uniform_1f(GPUShader *sh, const char *name, float value);
+void GPU_shader_uniform_2f(GPUShader *sh, const char *name, float x, float y);
+void GPU_shader_uniform_3f(GPUShader *sh, const char *name, float x, float y, float z);
+void GPU_shader_uniform_4f(GPUShader *sh, const char *name, float x, float y, float z, float w);
+void GPU_shader_uniform_2fv(GPUShader *sh, const char *name, const float data[2]);
+void GPU_shader_uniform_3fv(GPUShader *sh, const char *name, const float data[3]);
+void GPU_shader_uniform_4fv(GPUShader *sh, const char *name, const float data[4]);
+void GPU_shader_uniform_mat4(GPUShader *sh, const char *name, const float data[4][4]);
+void GPU_shader_uniform_2fv_array(GPUShader *sh, const char *name, int len, const float (*val)[2]);
+void GPU_shader_uniform_4fv_array(GPUShader *sh, const char *name, int len, const float (*val)[4]);
-char *GPU_shader_get_binary(GPUShader *shader, uint *r_binary_format, int *r_binary_len);
+int GPU_shader_get_attribute(GPUShader *shader, const char *name);
void GPU_shader_set_framebuffer_srgb_target(int use_srgb_to_linear);
diff --git a/source/blender/gpu/GPU_shader_interface.h b/source/blender/gpu/GPU_shader_interface.h
deleted file mode 100644
index 8aba1236b65..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 */
- struct GPUBatch **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 *, struct GPUBatch *);
-void GPU_shaderinterface_remove_batch_ref(GPUShaderInterface *, struct GPUBatch *);
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/source/blender/gpu/GPU_state.h b/source/blender/gpu/GPU_state.h
index 4a2c90e241b..253877bcca0 100644
--- a/source/blender/gpu/GPU_state.h
+++ b/source/blender/gpu/GPU_state.h
@@ -20,90 +20,138 @@
#pragma once
-#ifdef __cplusplus
-extern "C" {
-#endif
+#include "BLI_utildefines.h"
-/* These map directly to the GL_ blend functions, to minimize API add as needed*/
-typedef enum eGPUBlendFunction {
- GPU_ONE,
- GPU_SRC_ALPHA,
- GPU_ONE_MINUS_SRC_ALPHA,
- GPU_DST_COLOR,
- GPU_ZERO,
-} eGPUBlendFunction;
-
-/* These map directly to the GL_ filter functions, to minimize API add as needed*/
-typedef enum eGPUFilterFunction {
- GPU_NEAREST,
- GPU_LINEAR,
-} eGPUFilterFunction;
-
-typedef enum eGPUFaceCull {
+typedef enum eGPUWriteMask {
+ GPU_WRITE_NONE = 0,
+ GPU_WRITE_RED = (1 << 0),
+ GPU_WRITE_GREEN = (1 << 1),
+ 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;
+
+ENUM_OPERATORS(eGPUWriteMask)
+
+/**
+ * Defines the fixed pipeline blending equation.
+ * SRC is the output color from the shader.
+ * DST is the color from the framebuffer.
+ * The blending equation is :
+ * (SRC * A) + (DST * B).
+ * The blend mode will modify the A and B parameters.
+ */
+typedef enum eGPUBlend {
+ GPU_BLEND_NONE = 0,
+ /** Premult variants will _NOT_ multiply rgb output by alpha. */
+ GPU_BLEND_ALPHA,
+ GPU_BLEND_ALPHA_PREMULT,
+ GPU_BLEND_ADDITIVE,
+ GPU_BLEND_ADDITIVE_PREMULT,
+ GPU_BLEND_MULTIPLY,
+ GPU_BLEND_SUBTRACT,
+ /** Replace logic op: SRC * (1 - DST)
+ * NOTE: Does not modify alpha. */
+ GPU_BLEND_INVERT,
+ /** Order independent transparency.
+ * NOTE: Cannot be used as is. Needs special setup (framebuffer, shader ...). */
+ GPU_BLEND_OIT,
+ /** Special blend to add color under and multiply dst color by src alpha. */
+ GPU_BLEND_BACKGROUND,
+ /** Custom blend parameters using dual source blending : SRC0 + SRC1 * DST
+ * NOTE: Can only be used with _ONE_ Draw Buffer and shader needs to be specialized. */
+ GPU_BLEND_CUSTOM,
+} eGPUBlend;
+
+typedef enum eGPUDepthTest {
+ GPU_DEPTH_NONE = 0,
+ GPU_DEPTH_ALWAYS, /* Used to draw to the depth buffer without really testing. */
+ GPU_DEPTH_LESS,
+ GPU_DEPTH_LESS_EQUAL, /* Default. */
+ GPU_DEPTH_EQUAL,
+ GPU_DEPTH_GREATER,
+ GPU_DEPTH_GREATER_EQUAL,
+} eGPUDepthTest;
+
+typedef enum eGPUStencilTest {
+ GPU_STENCIL_NONE = 0,
+ GPU_STENCIL_ALWAYS,
+ GPU_STENCIL_EQUAL,
+ GPU_STENCIL_NEQUAL,
+} eGPUStencilTest;
+
+typedef enum eGPUStencilOp {
+ GPU_STENCIL_OP_NONE = 0,
+ GPU_STENCIL_OP_REPLACE,
+ /** Special values for stencil shadows. */
+ GPU_STENCIL_OP_COUNT_DEPTH_PASS,
+ GPU_STENCIL_OP_COUNT_DEPTH_FAIL,
+} eGPUStencilOp;
+
+typedef enum eGPUFaceCullTest {
GPU_CULL_NONE = 0, /* Culling disabled. */
GPU_CULL_FRONT,
GPU_CULL_BACK,
-} eGPUFaceCull;
+} eGPUFaceCullTest;
typedef enum eGPUProvokingVertex {
- GPU_VERTEX_FIRST = 0,
- GPU_VERTEX_LAST, /* Default */
+ GPU_VERTEX_LAST = 0, /* Default. */
+ GPU_VERTEX_FIRST = 1, /* Follow Blender loop order. */
} eGPUProvokingVertex;
-/* Initialize
- * - sets the default Blender opengl state, if in doubt, check
- * the contents of this function
- * - this is called when starting Blender, for opengl rendering. */
-void GPU_state_init(void);
-
-void GPU_blend(bool enable);
-void GPU_blend_set_func(eGPUBlendFunction sfactor, eGPUBlendFunction dfactor);
-void GPU_blend_set_func_separate(eGPUBlendFunction src_rgb,
- eGPUBlendFunction dst_rgb,
- eGPUBlendFunction src_alpha,
- eGPUBlendFunction dst_alpha);
-void GPU_face_culling(eGPUFaceCull culling);
-void GPU_front_facing(bool invert);
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void GPU_blend(eGPUBlend blend);
+void GPU_face_culling(eGPUFaceCullTest culling);
+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);
+void GPU_logic_op_xor_set(bool enable);
void GPU_point_size(float size);
void GPU_polygon_smooth(bool enable);
void GPU_program_point_size(bool enable);
void GPU_scissor(int x, int y, int width, int height);
-void GPU_scissor_get_f(float coords[4]);
-void GPU_scissor_get_i(int coords[4]);
+void GPU_scissor_get(int coords[4]);
void GPU_viewport(int x, int y, int width, int height);
void GPU_viewport_size_get_f(float coords[4]);
void GPU_viewport_size_get_i(int coords[4]);
+void GPU_write_mask(eGPUWriteMask mask);
void GPU_color_mask(bool r, bool g, bool b, bool a);
void GPU_depth_mask(bool depth);
bool GPU_depth_mask_get(void);
-void GPU_stencil_mask(uint stencil);
void GPU_unpack_row_length_set(uint len);
+void GPU_shadow_offset(bool enable);
void GPU_clip_distances(int enabled_len);
bool GPU_mipmap_enabled(void);
+void GPU_state_set(eGPUWriteMask write_mask,
+ eGPUBlend blend,
+ eGPUFaceCullTest culling_test,
+ eGPUDepthTest depth_test,
+ eGPUStencilTest stencil_test,
+ eGPUStencilOp stencil_op,
+ eGPUProvokingVertex provoking_vert);
-void GPU_flush(void);
-void GPU_finish(void);
+void GPU_stencil_reference_set(uint reference);
+void GPU_stencil_write_mask_set(uint write_mask);
+void GPU_stencil_compare_mask_set(uint compare_mask);
-void GPU_logic_op_xor_set(bool enable);
+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);
-/* Attribute push & pop. */
-typedef enum eGPUAttrMask {
- GPU_DEPTH_BUFFER_BIT = (1 << 0),
- GPU_ENABLE_BIT = (1 << 1),
- GPU_SCISSOR_BIT = (1 << 2),
- GPU_VIEWPORT_BIT = (1 << 3),
- GPU_BLEND_BIT = (1 << 4),
-} eGPUAttrMask;
-
-void gpuPushAttr(eGPUAttrMask mask);
-void gpuPopAttr(void);
+void GPU_flush(void);
+void GPU_finish(void);
#ifdef __cplusplus
}
diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h
index 7ee7f8fcdec..93865c098b8 100644
--- a/source/blender/gpu/GPU_texture.h
+++ b/source/blender/gpu/GPU_texture.h
@@ -275,8 +275,10 @@ void GPU_texture_mipmap_mode(GPUTexture *tex, bool use_mipmap, bool use_filter);
void GPU_texture_wrap_mode(GPUTexture *tex, bool use_repeat, bool use_clamp);
void GPU_texture_swizzle_set(GPUTexture *tex, const char swizzle[4]);
+/* TODO should be private internal functions. */
void GPU_texture_attach_framebuffer(GPUTexture *tex, struct GPUFrameBuffer *fb, int attachment);
-int GPU_texture_detach_framebuffer(GPUTexture *tex, struct GPUFrameBuffer *fb);
+void GPU_texture_detach_framebuffer(GPUTexture *tex, struct GPUFrameBuffer *fb);
+int GPU_texture_framebuffer_attachement_get(GPUTexture *tex, struct GPUFrameBuffer *fb);
int GPU_texture_target(const GPUTexture *tex);
int GPU_texture_width(const GPUTexture *tex);
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_vertex_buffer.h b/source/blender/gpu/GPU_vertex_buffer.h
index 757255496e0..bd1019bb1f5 100644
--- a/source/blender/gpu/GPU_vertex_buffer.h
+++ b/source/blender/gpu/GPU_vertex_buffer.h
@@ -59,6 +59,8 @@ typedef struct GPUVertBuf {
uint32_t vbo_id;
/** Usage hint for GL optimisation. */
GPUUsageType usage;
+ /** This counter will only avoid freeing the GPUVertBuf, not the data. */
+ char handle_refcount;
/** Data has been touched and need to be reuploaded to GPU. */
bool dirty;
uchar *data; /* NULL indicates data in VRAM (unmapped) */
@@ -73,6 +75,10 @@ GPUVertBuf *GPU_vertbuf_create_with_format_ex(const GPUVertFormat *, GPUUsageTyp
void GPU_vertbuf_clear(GPUVertBuf *verts);
void GPU_vertbuf_discard(GPUVertBuf *);
+/* Avoid GPUVertBuf datablock being free but not its data. */
+void GPU_vertbuf_handle_ref_add(GPUVertBuf *verts);
+void GPU_vertbuf_handle_ref_remove(GPUVertBuf *verts);
+
void GPU_vertbuf_init(GPUVertBuf *, GPUUsageType);
void GPU_vertbuf_init_with_format_ex(GPUVertBuf *, const GPUVertFormat *, GPUUsageType);
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
deleted file mode 100644
index 6cb60884620..00000000000
--- a/source/blender/gpu/intern/gpu_attr_binding.cc
+++ /dev/null
@@ -1,83 +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 vertex attribute binding
- */
-
-#include "GPU_attr_binding.h"
-#include "gpu_attr_binding_private.h"
-#include <stddef.h>
-#include <stdlib.h>
-
-#if GPU_VERT_ATTR_MAX_LEN != 16
-# error "attribute binding code assumes GPU_VERT_ATTR_MAX_LEN = 16"
-#endif
-
-void AttrBinding_clear(GPUAttrBinding *binding)
-{
- binding->loc_bits = 0;
- binding->enabled_bits = 0;
-}
-
-uint read_attr_location(const GPUAttrBinding *binding, uint a_idx)
-{
-#if TRUST_NO_ONE
- assert(a_idx < GPU_VERT_ATTR_MAX_LEN);
- assert(binding->enabled_bits & (1 << a_idx));
-#endif
- return (binding->loc_bits >> (4 * a_idx)) & 0xF;
-}
-
-static void write_attr_location(GPUAttrBinding *binding, uint a_idx, uint location)
-{
-#if TRUST_NO_ONE
- assert(a_idx < GPU_VERT_ATTR_MAX_LEN);
- assert(location < GPU_VERT_ATTR_MAX_LEN);
-#endif
- const uint shift = 4 * a_idx;
- const uint64_t mask = ((uint64_t)0xF) << shift;
- /* overwrite this attr's previous location */
- binding->loc_bits = (binding->loc_bits & ~mask) | (location << shift);
- /* mark this attr as enabled */
- binding->enabled_bits |= 1 << a_idx;
-}
-
-void get_attr_locations(const GPUVertFormat *format,
- GPUAttrBinding *binding,
- const GPUShaderInterface *shaderface)
-{
- AttrBinding_clear(binding);
-
- for (uint a_idx = 0; a_idx < format->attr_len; a_idx++) {
- 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);
- /* TODO: make this a recoverable runtime error?
- * indicates mismatch between vertex format and program. */
-#endif
- write_attr_location(binding, a_idx, input->location);
- }
- }
-}
diff --git a/source/blender/gpu/intern/gpu_backend.hh b/source/blender/gpu/intern/gpu_backend.hh
index 24f592f214f..ec60e6b5704 100644
--- a/source/blender/gpu/intern/gpu_backend.hh
+++ b/source/blender/gpu/intern/gpu_backend.hh
@@ -27,11 +27,30 @@
struct GPUContext;
+namespace blender {
+namespace gpu {
+
+class Batch;
+class DrawList;
+class FrameBuffer;
+class Shader;
+class UniformBuf;
+
class GPUBackend {
public:
virtual ~GPUBackend(){};
+ static GPUBackend *get(void);
+
virtual GPUContext *context_alloc(void *ghost_window) = 0;
+
+ virtual Batch *batch_alloc(void) = 0;
+ virtual DrawList *drawlist_alloc(int list_length) = 0;
+ virtual FrameBuffer *framebuffer_alloc(const char *name) = 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;
};
-GPUBackend *gpu_backend_get(void);
+} // namespace gpu
+} // namespace blender
diff --git a/source/blender/gpu/intern/gpu_batch.cc b/source/blender/gpu/intern/gpu_batch.cc
index a6ba4d3d89a..0b0c88a42e2 100644
--- a/source/blender/gpu/intern/gpu_batch.cc
+++ b/source/blender/gpu/intern/gpu_batch.cc
@@ -26,6 +26,8 @@
#include "MEM_guardedalloc.h"
+#include "BLI_math_base.h"
+
#include "GPU_batch.h"
#include "GPU_batch_presets.h"
#include "GPU_extensions.h"
@@ -33,79 +35,50 @@
#include "GPU_platform.h"
#include "GPU_shader.h"
-#include "gpu_batch_private.h"
+#include "gpu_backend.hh"
+#include "gpu_batch_private.hh"
#include "gpu_context_private.hh"
-#include "gpu_primitive_private.h"
-#include "gpu_shader_private.h"
+#include "gpu_shader_private.hh"
#include "gpu_vertex_format_private.h"
+#include "gl_primitive.hh" /* TODO remove */
+
#include <limits.h>
#include <stdlib.h>
#include <string.h>
-static GLuint g_default_attr_vbo = 0;
-
-static void batch_update_program_bindings(GPUBatch *batch, uint i_first);
+using namespace blender::gpu;
-void GPU_batch_vao_cache_clear(GPUBatch *batch)
-{
- if (batch->context == NULL) {
- return;
- }
- if (batch->is_dynamic_vao_count) {
- for (int i = 0; i < batch->dynamic_vaos.count; i++) {
- if (batch->dynamic_vaos.vao_ids[i]) {
- GPU_vao_free(batch->dynamic_vaos.vao_ids[i], batch->context);
- }
- if (batch->dynamic_vaos.interfaces[i]) {
- GPU_shaderinterface_remove_batch_ref(
- (GPUShaderInterface *)batch->dynamic_vaos.interfaces[i], batch);
- }
- }
- MEM_freeN((void *)batch->dynamic_vaos.interfaces);
- MEM_freeN(batch->dynamic_vaos.vao_ids);
- }
- else {
- for (int i = 0; i < GPU_BATCH_VAO_STATIC_LEN; i++) {
- if (batch->static_vaos.vao_ids[i]) {
- GPU_vao_free(batch->static_vaos.vao_ids[i], batch->context);
- }
- if (batch->static_vaos.interfaces[i]) {
- GPU_shaderinterface_remove_batch_ref(
- (GPUShaderInterface *)batch->static_vaos.interfaces[i], batch);
- }
- }
- }
- batch->is_dynamic_vao_count = false;
- for (int i = 0; i < GPU_BATCH_VAO_STATIC_LEN; i++) {
- batch->static_vaos.vao_ids[i] = 0;
- batch->static_vaos.interfaces[i] = NULL;
- }
- gpu_context_remove_batch(batch->context, batch);
- batch->context = NULL;
-}
+/* -------------------------------------------------------------------- */
+/** \name Creation & Deletion
+ * \{ */
-GPUBatch *GPU_batch_calloc(uint count)
+GPUBatch *GPU_batch_calloc(void)
{
- return (GPUBatch *)MEM_callocN(sizeof(GPUBatch) * count, "GPUBatch");
+ GPUBatch *batch = GPUBackend::get()->batch_alloc();
+ memset(batch, 0, sizeof(*batch));
+ return batch;
}
GPUBatch *GPU_batch_create_ex(GPUPrimType prim_type,
GPUVertBuf *verts,
GPUIndexBuf *elem,
- uint owns_flag)
+ eGPUBatchFlag owns_flag)
{
- GPUBatch *batch = GPU_batch_calloc(1);
+ GPUBatch *batch = GPU_batch_calloc();
GPU_batch_init_ex(batch, prim_type, verts, elem, owns_flag);
return batch;
}
-void GPU_batch_init_ex(
- GPUBatch *batch, GPUPrimType prim_type, GPUVertBuf *verts, GPUIndexBuf *elem, uint owns_flag)
+void GPU_batch_init_ex(GPUBatch *batch,
+ GPUPrimType prim_type,
+ GPUVertBuf *verts,
+ GPUIndexBuf *elem,
+ eGPUBatchFlag owns_flag)
{
-#if TRUST_NO_ONE
- assert(verts != NULL);
-#endif
+ BLI_assert(verts != NULL);
+ /* Do not pass any other flag */
+ BLI_assert((owns_flag & ~(GPU_BATCH_OWNS_VBO | GPU_BATCH_OWNS_INDEX)) == 0);
batch->verts[0] = verts;
for (int v = 1; v < GPU_BATCH_VBO_MAX_LEN; v++) {
@@ -115,19 +88,18 @@ void GPU_batch_init_ex(
batch->inst[v] = NULL;
}
batch->elem = elem;
- batch->gl_prim_type = convert_prim_type_to_gl(prim_type);
- batch->phase = GPU_BATCH_READY_TO_DRAW;
- batch->is_dynamic_vao_count = false;
- batch->owns_flag = owns_flag;
- batch->free_callback = NULL;
+ batch->prim_type = prim_type;
+ batch->flag = owns_flag | GPU_BATCH_INIT | GPU_BATCH_DIRTY;
+ batch->shader = NULL;
}
/* This will share the VBOs with the new batch. */
void GPU_batch_copy(GPUBatch *batch_dst, GPUBatch *batch_src)
{
- GPU_batch_init_ex(batch_dst, GPU_PRIM_POINTS, batch_src->verts[0], batch_src->elem, 0);
+ GPU_batch_init_ex(
+ batch_dst, GPU_PRIM_POINTS, batch_src->verts[0], batch_src->elem, GPU_BATCH_INVALID);
- batch_dst->gl_prim_type = batch_src->gl_prim_type;
+ batch_dst->prim_type = batch_src->prim_type;
for (int v = 1; v < GPU_BATCH_VBO_MAX_LEN; v++) {
batch_dst->verts[v] = batch_src->verts[v];
}
@@ -135,563 +107,159 @@ void GPU_batch_copy(GPUBatch *batch_dst, GPUBatch *batch_src)
void GPU_batch_clear(GPUBatch *batch)
{
- if (batch->owns_flag & GPU_BATCH_OWNS_INDEX) {
+ if (batch->flag & GPU_BATCH_OWNS_INDEX) {
GPU_indexbuf_discard(batch->elem);
}
- if (batch->owns_flag & GPU_BATCH_OWNS_INSTANCES) {
- GPU_vertbuf_discard(batch->inst[0]);
- GPU_VERTBUF_DISCARD_SAFE(batch->inst[1]);
- }
- if ((batch->owns_flag & ~GPU_BATCH_OWNS_INDEX) != 0) {
- for (int v = 0; v < GPU_BATCH_VBO_MAX_LEN; v++) {
- if (batch->verts[v] == NULL) {
- break;
+ if (batch->flag & GPU_BATCH_OWNS_VBO_ANY) {
+ for (int v = 0; (v < GPU_BATCH_VBO_MAX_LEN) && batch->verts[v]; v++) {
+ if (batch->flag & (GPU_BATCH_OWNS_VBO << v)) {
+ GPU_VERTBUF_DISCARD_SAFE(batch->verts[v]);
}
- if (batch->owns_flag & (1 << v)) {
- GPU_vertbuf_discard(batch->verts[v]);
+ }
+ }
+ if (batch->flag & GPU_BATCH_OWNS_INST_VBO_ANY) {
+ for (int v = 0; (v < GPU_BATCH_INST_VBO_MAX_LEN) && batch->inst[v]; v++) {
+ if (batch->flag & (GPU_BATCH_OWNS_INST_VBO << v)) {
+ GPU_VERTBUF_DISCARD_SAFE(batch->inst[v]);
}
}
}
- GPU_batch_vao_cache_clear(batch);
- batch->phase = GPU_BATCH_UNUSED;
+ batch->flag = GPU_BATCH_INVALID;
}
void GPU_batch_discard(GPUBatch *batch)
{
- if (batch->free_callback) {
- batch->free_callback(batch, batch->callback_data);
- }
-
GPU_batch_clear(batch);
- MEM_freeN(batch);
-}
-void GPU_batch_callback_free_set(GPUBatch *batch,
- void (*callback)(GPUBatch *, void *),
- void *user_data)
-{
- batch->free_callback = callback;
- batch->callback_data = user_data;
+ delete static_cast<Batch *>(batch);
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Buffers Management
+ * \{ */
+
+/* NOTE: Override ONLY the first instance vbo (and free them if owned). */
void GPU_batch_instbuf_set(GPUBatch *batch, GPUVertBuf *inst, bool own_vbo)
{
-#if TRUST_NO_ONE
- assert(inst != NULL);
-#endif
- /* redo the bindings */
- GPU_batch_vao_cache_clear(batch);
+ BLI_assert(inst);
+ batch->flag |= GPU_BATCH_DIRTY;
- if (batch->inst[0] != NULL && (batch->owns_flag & GPU_BATCH_OWNS_INSTANCES)) {
+ if (batch->inst[0] && (batch->flag & GPU_BATCH_OWNS_INST_VBO)) {
GPU_vertbuf_discard(batch->inst[0]);
- GPU_VERTBUF_DISCARD_SAFE(batch->inst[1]);
}
batch->inst[0] = inst;
- if (own_vbo) {
- batch->owns_flag |= GPU_BATCH_OWNS_INSTANCES;
- }
- else {
- batch->owns_flag &= ~GPU_BATCH_OWNS_INSTANCES;
- }
+ SET_FLAG_FROM_TEST(batch->flag, own_vbo, GPU_BATCH_OWNS_INST_VBO);
}
+/* NOTE: Override any previously assigned elem (and free it if owned). */
void GPU_batch_elembuf_set(GPUBatch *batch, GPUIndexBuf *elem, bool own_ibo)
{
- BLI_assert(elem != NULL);
- /* redo the bindings */
- GPU_batch_vao_cache_clear(batch);
+ BLI_assert(elem);
+ batch->flag |= GPU_BATCH_DIRTY;
- if (batch->elem != NULL && (batch->owns_flag & GPU_BATCH_OWNS_INDEX)) {
+ if (batch->elem && (batch->flag & GPU_BATCH_OWNS_INDEX)) {
GPU_indexbuf_discard(batch->elem);
}
batch->elem = elem;
- if (own_ibo) {
- batch->owns_flag |= GPU_BATCH_OWNS_INDEX;
- }
- else {
- batch->owns_flag &= ~GPU_BATCH_OWNS_INDEX;
- }
+ SET_FLAG_FROM_TEST(batch->flag, own_ibo, GPU_BATCH_OWNS_INDEX);
}
-/* A bit of a quick hack. Should be streamlined as the vbos handling */
int GPU_batch_instbuf_add_ex(GPUBatch *batch, GPUVertBuf *insts, bool own_vbo)
{
- /* redo the bindings */
- GPU_batch_vao_cache_clear(batch);
+ BLI_assert(insts);
+ batch->flag |= GPU_BATCH_DIRTY;
for (uint v = 0; v < GPU_BATCH_INST_VBO_MAX_LEN; v++) {
if (batch->inst[v] == NULL) {
-#if TRUST_NO_ONE
/* for now all VertexBuffers must have same vertex_len */
- if (batch->inst[0] != NULL) {
- /* Allow for different size of vertex buf (will choose the smallest number of verts). */
- // assert(insts->vertex_len == batch->inst[0]->vertex_len);
- assert(own_vbo == ((batch->owns_flag & GPU_BATCH_OWNS_INSTANCES) != 0));
+ if (batch->inst[0]) {
+ /* Allow for different size of vertex buffer (will choose the smallest number of verts). */
+ // BLI_assert(insts->vertex_len == batch->inst[0]->vertex_len);
}
-#endif
+
batch->inst[v] = insts;
- if (own_vbo) {
- batch->owns_flag |= GPU_BATCH_OWNS_INSTANCES;
- }
+ SET_FLAG_FROM_TEST(batch->flag, own_vbo, (eGPUBatchFlag)(GPU_BATCH_OWNS_INST_VBO << v));
return v;
}
}
-
/* we only make it this far if there is no room for another GPUVertBuf */
-#if TRUST_NO_ONE
- assert(false);
-#endif
+ BLI_assert(0 && "Not enough Instance VBO slot in batch");
return -1;
}
/* Returns the index of verts in the batch. */
int GPU_batch_vertbuf_add_ex(GPUBatch *batch, GPUVertBuf *verts, bool own_vbo)
{
- /* redo the bindings */
- GPU_batch_vao_cache_clear(batch);
+ BLI_assert(verts);
+ batch->flag |= GPU_BATCH_DIRTY;
for (uint v = 0; v < GPU_BATCH_VBO_MAX_LEN; v++) {
if (batch->verts[v] == NULL) {
-#if TRUST_NO_ONE
/* for now all VertexBuffers must have same vertex_len */
if (batch->verts[0] != NULL) {
- assert(verts->vertex_len == batch->verts[0]->vertex_len);
+ BLI_assert(verts->vertex_len == batch->verts[0]->vertex_len);
}
-#endif
batch->verts[v] = verts;
- /* TODO: mark dirty so we can keep attribute bindings up-to-date */
- if (own_vbo) {
- batch->owns_flag |= (1 << v);
- }
+ SET_FLAG_FROM_TEST(batch->flag, own_vbo, (eGPUBatchFlag)(GPU_BATCH_OWNS_VBO << v));
return v;
}
}
-
/* we only make it this far if there is no room for another GPUVertBuf */
-#if TRUST_NO_ONE
- assert(false);
-#endif
+ BLI_assert(0 && "Not enough VBO slot in batch");
return -1;
}
-static GLuint batch_vao_get(GPUBatch *batch)
-{
- /* Search through cache */
- if (batch->is_dynamic_vao_count) {
- for (int i = 0; i < batch->dynamic_vaos.count; i++) {
- if (batch->dynamic_vaos.interfaces[i] == batch->interface) {
- return batch->dynamic_vaos.vao_ids[i];
- }
- }
- }
- else {
- for (int i = 0; i < GPU_BATCH_VAO_STATIC_LEN; i++) {
- if (batch->static_vaos.interfaces[i] == batch->interface) {
- return batch->static_vaos.vao_ids[i];
- }
- }
- }
-
- /* Set context of this batch.
- * It will be bound to it until GPU_batch_vao_cache_clear is called.
- * Until then it can only be drawn with this context. */
- if (batch->context == NULL) {
- batch->context = GPU_context_active_get();
- gpu_context_add_batch(batch->context, batch);
- }
-#if TRUST_NO_ONE
- else {
- /* Make sure you are not trying to draw this batch in another context. */
- assert(batch->context == GPU_context_active_get());
- }
-#endif
-
- /* Cache miss, time to add a new entry! */
- GLuint new_vao = 0;
- if (!batch->is_dynamic_vao_count) {
- int i; /* find first unused slot */
- for (i = 0; i < GPU_BATCH_VAO_STATIC_LEN; i++) {
- if (batch->static_vaos.vao_ids[i] == 0) {
- break;
- }
- }
-
- if (i < GPU_BATCH_VAO_STATIC_LEN) {
- batch->static_vaos.interfaces[i] = batch->interface;
- batch->static_vaos.vao_ids[i] = new_vao = GPU_vao_alloc();
- }
- else {
- /* Not enough place switch to dynamic. */
- batch->is_dynamic_vao_count = true;
- /* Erase previous entries, they will be added back if drawn again. */
- for (int j = 0; j < GPU_BATCH_VAO_STATIC_LEN; j++) {
- GPU_shaderinterface_remove_batch_ref(
- (GPUShaderInterface *)batch->static_vaos.interfaces[j], batch);
- GPU_vao_free(batch->static_vaos.vao_ids[j], batch->context);
- }
- /* Init dynamic arrays and let the branch below set the values. */
- batch->dynamic_vaos.count = GPU_BATCH_VAO_DYN_ALLOC_COUNT;
- batch->dynamic_vaos.interfaces = (const GPUShaderInterface **)MEM_callocN(
- batch->dynamic_vaos.count * sizeof(GPUShaderInterface *), "dyn vaos interfaces");
- batch->dynamic_vaos.vao_ids = (GLuint *)MEM_callocN(
- batch->dynamic_vaos.count * sizeof(GLuint), "dyn vaos ids");
- }
- }
-
- if (batch->is_dynamic_vao_count) {
- int i; /* find first unused slot */
- for (i = 0; i < batch->dynamic_vaos.count; i++) {
- if (batch->dynamic_vaos.vao_ids[i] == 0) {
- break;
- }
- }
-
- if (i == batch->dynamic_vaos.count) {
- /* Not enough place, realloc the array. */
- i = batch->dynamic_vaos.count;
- batch->dynamic_vaos.count += GPU_BATCH_VAO_DYN_ALLOC_COUNT;
- batch->dynamic_vaos.interfaces = (const GPUShaderInterface **)MEM_recallocN(
- (void *)batch->dynamic_vaos.interfaces,
- sizeof(GPUShaderInterface *) * batch->dynamic_vaos.count);
- batch->dynamic_vaos.vao_ids = (GLuint *)MEM_recallocN(
- batch->dynamic_vaos.vao_ids, sizeof(GLuint) * batch->dynamic_vaos.count);
- }
- batch->dynamic_vaos.interfaces[i] = batch->interface;
- batch->dynamic_vaos.vao_ids[i] = new_vao = GPU_vao_alloc();
- }
-
- GPU_shaderinterface_add_batch_ref((GPUShaderInterface *)batch->interface, batch);
-
-#if TRUST_NO_ONE
- assert(new_vao != 0);
-#endif
-
- /* We just got a fresh VAO we need to initialize it. */
- glBindVertexArray(new_vao);
- batch_update_program_bindings(batch, 0);
- glBindVertexArray(0);
-
- return new_vao;
-}
+/** \} */
-void GPU_batch_set_shader_no_bind(GPUBatch *batch, GPUShader *shader)
-{
-#if TRUST_NO_ONE
- assert(glIsProgram(shader->program));
- assert(batch->program_in_use == 0);
-#endif
- batch->interface = shader->interface;
- batch->program = shader->program;
- batch->vao_id = batch_vao_get(batch);
-}
+/* -------------------------------------------------------------------- */
+/** \name Uniform setters
+ *
+ * TODO(fclem) port this to GPUShader.
+ * \{ */
void GPU_batch_set_shader(GPUBatch *batch, GPUShader *shader)
{
- GPU_batch_set_shader_no_bind(batch, shader);
- GPU_batch_program_use_begin(batch); /* hack! to make Batch_Uniform* simpler */
-}
-
-void gpu_batch_remove_interface_ref(GPUBatch *batch, const GPUShaderInterface *interface)
-{
- if (batch->is_dynamic_vao_count) {
- for (int i = 0; i < batch->dynamic_vaos.count; i++) {
- if (batch->dynamic_vaos.interfaces[i] == interface) {
- GPU_vao_free(batch->dynamic_vaos.vao_ids[i], batch->context);
- batch->dynamic_vaos.vao_ids[i] = 0;
- batch->dynamic_vaos.interfaces[i] = NULL;
- break; /* cannot have duplicates */
- }
- }
- }
- else {
- int i;
- for (i = 0; i < GPU_BATCH_VAO_STATIC_LEN; i++) {
- if (batch->static_vaos.interfaces[i] == interface) {
- GPU_vao_free(batch->static_vaos.vao_ids[i], batch->context);
- batch->static_vaos.vao_ids[i] = 0;
- batch->static_vaos.interfaces[i] = NULL;
- break; /* cannot have duplicates */
- }
- }
- }
-}
-
-static void create_bindings(GPUVertBuf *verts,
- const GPUShaderInterface *interface,
- uint16_t *attr_mask,
- uint v_first,
- const bool use_instancing)
-{
- const GPUVertFormat *format = &verts->format;
-
- const uint attr_len = format->attr_len;
- uint stride = format->stride;
- uint offset = 0;
-
- GPU_vertbuf_use(verts);
-
- for (uint a_idx = 0; a_idx < attr_len; a_idx++) {
- const GPUVertAttr *a = &format->attrs[a_idx];
-
- if (format->deinterleaved) {
- offset += ((a_idx == 0) ? 0 : format->attrs[a_idx - 1].sz) * verts->vertex_len;
- stride = a->sz;
- }
- else {
- offset = a->offset;
- }
-
- const GLvoid *pointer = (const GLubyte *)0 + offset + v_first * stride;
- const GLenum type = convert_comp_type_to_gl(static_cast<GPUVertCompType>(a->comp_type));
-
- 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);
-
- if (input == NULL) {
- continue;
- }
-
- *attr_mask &= ~(1 << input->location);
-
- if (a->comp_len == 16 || a->comp_len == 12 || a->comp_len == 8) {
- BLI_assert(a->fetch_mode == GPU_FETCH_FLOAT);
- BLI_assert(a->comp_type == GPU_COMP_F32);
- for (int i = 0; i < a->comp_len / 4; i++) {
- glEnableVertexAttribArray(input->location + i);
- glVertexAttribDivisor(input->location + i, (use_instancing) ? 1 : 0);
- glVertexAttribPointer(
- input->location + i, 4, type, GL_FALSE, stride, (const GLubyte *)pointer + i * 16);
- }
- }
- else {
- glEnableVertexAttribArray(input->location);
- glVertexAttribDivisor(input->location, (use_instancing) ? 1 : 0);
-
- switch (a->fetch_mode) {
- case GPU_FETCH_FLOAT:
- case GPU_FETCH_INT_TO_FLOAT:
- glVertexAttribPointer(input->location, a->comp_len, type, GL_FALSE, stride, pointer);
- break;
- case GPU_FETCH_INT_TO_FLOAT_UNIT:
- glVertexAttribPointer(input->location, a->comp_len, type, GL_TRUE, stride, pointer);
- break;
- case GPU_FETCH_INT:
- glVertexAttribIPointer(input->location, a->comp_len, type, stride, pointer);
- break;
- }
- }
- }
- }
-}
-
-static void batch_update_program_bindings(GPUBatch *batch, uint i_first)
-{
- uint16_t attr_mask = batch->interface->enabled_attr_mask;
-
- /* Reverse order so first VBO'S have more prevalence (in term of attribute override). */
- for (int v = GPU_BATCH_VBO_MAX_LEN - 1; v > -1; v--) {
- if (batch->verts[v] != NULL) {
- create_bindings(batch->verts[v], batch->interface, &attr_mask, 0, false);
- }
- }
-
- for (int v = GPU_BATCH_INST_VBO_MAX_LEN - 1; v > -1; v--) {
- if (batch->inst[v]) {
- create_bindings(batch->inst[v], batch->interface, &attr_mask, i_first, true);
- }
- }
-
- if (attr_mask != 0 && GLEW_ARB_vertex_attrib_binding) {
- for (uint16_t mask = 1, a = 0; a < 16; a++, mask <<= 1) {
- if (attr_mask & mask) {
- /* This replaces glVertexAttrib4f(a, 0.0f, 0.0f, 0.0f, 1.0f); with a more modern style.
- * Fix issues for some drivers (see T75069). */
- glBindVertexBuffer(a, g_default_attr_vbo, (intptr_t)0, (intptr_t)0);
-
- glEnableVertexAttribArray(a);
- glVertexAttribFormat(a, 4, GL_FLOAT, GL_FALSE, 0);
- glVertexAttribBinding(a, a);
- }
- }
- }
-
- if (batch->elem) {
- GPU_indexbuf_use(batch->elem);
- }
-}
-
-void GPU_batch_program_use_begin(GPUBatch *batch)
-{
- /* NOTE: use_program & done_using_program are fragile, depend on staying in sync with
- * the GL context's active program.
- * use_program doesn't mark other programs as "not used". */
- /* TODO: make not fragile (somehow) */
-
- if (!batch->program_in_use) {
- glUseProgram(batch->program);
- batch->program_in_use = true;
- }
-}
-
-void GPU_batch_program_use_end(GPUBatch *batch)
-{
- if (batch->program_in_use) {
-#if PROGRAM_NO_OPTI
- glUseProgram(0);
-#endif
- batch->program_in_use = false;
- }
-}
-
-#if TRUST_NO_ONE
-# define GET_UNIFORM \
- const GPUShaderInput *uniform = GPU_shaderinterface_uniform(batch->interface, name); \
- assert(uniform);
-#else
-# define GET_UNIFORM \
- const GPUShaderInput *uniform = GPU_shaderinterface_uniform(batch->interface, name);
-#endif
-
-void GPU_batch_uniform_1ui(GPUBatch *batch, const char *name, uint value)
-{
- GET_UNIFORM
- glUniform1ui(uniform->location, value);
-}
-
-void GPU_batch_uniform_1i(GPUBatch *batch, const char *name, int value)
-{
- GET_UNIFORM
- glUniform1i(uniform->location, value);
-}
-
-void GPU_batch_uniform_1b(GPUBatch *batch, const char *name, bool value)
-{
- GET_UNIFORM
- glUniform1i(uniform->location, value ? GL_TRUE : GL_FALSE);
-}
-
-void GPU_batch_uniform_2f(GPUBatch *batch, const char *name, float x, float y)
-{
- GET_UNIFORM
- glUniform2f(uniform->location, x, y);
-}
-
-void GPU_batch_uniform_3f(GPUBatch *batch, const char *name, float x, float y, float z)
-{
- GET_UNIFORM
- glUniform3f(uniform->location, x, y, z);
-}
-
-void GPU_batch_uniform_4f(GPUBatch *batch, const char *name, float x, float y, float z, float w)
-{
- GET_UNIFORM
- glUniform4f(uniform->location, x, y, z, w);
-}
-
-void GPU_batch_uniform_1f(GPUBatch *batch, const char *name, float x)
-{
- GET_UNIFORM
- glUniform1f(uniform->location, x);
-}
-
-void GPU_batch_uniform_2fv(GPUBatch *batch, const char *name, const float data[2])
-{
- GET_UNIFORM
- glUniform2fv(uniform->location, 1, data);
-}
-
-void GPU_batch_uniform_3fv(GPUBatch *batch, const char *name, const float data[3])
-{
- GET_UNIFORM
- glUniform3fv(uniform->location, 1, data);
-}
-
-void GPU_batch_uniform_4fv(GPUBatch *batch, const char *name, const float data[4])
-{
- GET_UNIFORM
- glUniform4fv(uniform->location, 1, data);
-}
-
-void GPU_batch_uniform_2fv_array(GPUBatch *batch,
- const char *name,
- const int len,
- const float *data)
-{
- GET_UNIFORM
- glUniform2fv(uniform->location, len, data);
+ batch->shader = shader;
+ GPU_shader_bind(batch->shader);
}
-void GPU_batch_uniform_4fv_array(GPUBatch *batch,
- const char *name,
- const int len,
- const float *data)
-{
- GET_UNIFORM
- glUniform4fv(uniform->location, len, data);
-}
+/** \} */
-void GPU_batch_uniform_mat4(GPUBatch *batch, const char *name, const float data[4][4])
-{
- GET_UNIFORM
- glUniformMatrix4fv(uniform->location, 1, GL_FALSE, (const float *)data);
-}
+/* -------------------------------------------------------------------- */
+/** \name Drawing / Drawcall functions
+ * \{ */
-static void *elem_offset(const GPUIndexBuf *el, int v_first)
+void GPU_batch_draw(GPUBatch *batch)
{
-#if GPU_TRACK_INDEX_RANGE
- if (el->index_type == GPU_INDEX_U16) {
- return (GLushort *)0 + v_first + el->index_start;
- }
-#endif
- return (GLuint *)0 + v_first + el->index_start;
+ GPU_shader_bind(batch->shader);
+ GPU_batch_draw_advanced(batch, 0, 0, 0, 0);
+ GPU_shader_unbind();
}
-/* Use when drawing with GPU_batch_draw_advanced */
-void GPU_batch_bind(GPUBatch *batch)
+void GPU_batch_draw_range(GPUBatch *batch, int v_first, int v_count)
{
- glBindVertexArray(batch->vao_id);
-
-#if GPU_TRACK_INDEX_RANGE
- /* Can be removed if GL 4.3 is required. */
- if (!GLEW_ARB_ES3_compatibility && batch->elem != NULL) {
- GLuint restart_index = (batch->elem->index_type == GPU_INDEX_U16) ? (GLuint)0xFFFF :
- (GLuint)0xFFFFFFFF;
- glPrimitiveRestartIndex(restart_index);
- }
-#endif
+ GPU_shader_bind(batch->shader);
+ GPU_batch_draw_advanced(batch, v_first, v_count, 0, 0);
+ GPU_shader_unbind();
}
-void GPU_batch_draw(GPUBatch *batch)
+/* Draw multiple instance of a batch without having any instance attributes. */
+void GPU_batch_draw_instanced(GPUBatch *batch, int i_count)
{
-#if TRUST_NO_ONE
- assert(batch->phase == GPU_BATCH_READY_TO_DRAW);
- assert(batch->verts[0]->vbo_id != 0);
-#endif
- GPU_batch_program_use_begin(batch);
- GPU_matrix_bind(batch->interface); // external call.
- GPU_shader_set_srgb_uniform(batch->interface);
+ BLI_assert(batch->inst[0] == NULL);
- GPU_batch_bind(batch);
- GPU_batch_draw_advanced(batch, 0, 0, 0, 0);
-
- GPU_batch_program_use_end(batch);
+ GPU_shader_bind(batch->shader);
+ GPU_batch_draw_advanced(batch, 0, 0, 0, i_count);
+ GPU_shader_unbind();
}
-#if GPU_TRACK_INDEX_RANGE
-# define BASE_INDEX(el) ((el)->base_index)
-# define INDEX_TYPE(el) ((el)->gl_index_type)
-#else
-# define BASE_INDEX(el) 0
-# define INDEX_TYPE(el) GL_UNSIGNED_INT
-#endif
-
void GPU_batch_draw_advanced(GPUBatch *batch, int v_first, int v_count, int i_first, int i_count)
{
- BLI_assert(batch->program_in_use);
- /* TODO could assert that VAO is bound. */
+ BLI_assert(GPU_context_active_get()->shader != NULL);
if (v_count == 0) {
v_count = (batch->elem) ? batch->elem->index_len : batch->verts[0]->vertex_len;
@@ -699,8 +267,8 @@ void GPU_batch_draw_advanced(GPUBatch *batch, int v_first, int v_count, int i_fi
if (i_count == 0) {
i_count = (batch->inst[0]) ? batch->inst[0]->vertex_len : 1;
/* Meh. This is to be able to use different numbers of verts in instance vbos. */
- if (batch->inst[1] && i_count > batch->inst[1]->vertex_len) {
- i_count = batch->inst[1]->vertex_len;
+ if (batch->inst[1] != NULL) {
+ i_count = min_ii(i_count, batch->inst[1]->vertex_len);
}
}
@@ -709,275 +277,7 @@ void GPU_batch_draw_advanced(GPUBatch *batch, int v_first, int v_count, int i_fi
return;
}
- /* Verify there is enough data do draw. */
- /* TODO(fclem) Nice to have but this is invalid when using procedural draw-calls.
- * The right assert would be to check if there is an enabled attribute from each VBO
- * and check their length. */
- // BLI_assert(i_first + i_count <= (batch->inst ? batch->inst->vertex_len : INT_MAX));
- // BLI_assert(v_first + v_count <=
- // (batch->elem ? batch->elem->index_len : batch->verts[0]->vertex_len));
-
-#ifdef __APPLE__
- GLuint vao = 0;
-#endif
-
- if (!GPU_arb_base_instance_is_supported()) {
- if (i_first > 0) {
-#ifdef __APPLE__
- /**
- * There seems to be a nasty bug when drawing using the same VAO reconfiguring. (see T71147)
- * We just use a throwaway VAO for that. Note that this is likely to degrade performance.
- **/
- glGenVertexArrays(1, &vao);
- glBindVertexArray(vao);
-#else
- /* If using offset drawing with instancing, we must
- * use the default VAO and redo bindings. */
- glBindVertexArray(GPU_vao_default());
-#endif
- batch_update_program_bindings(batch, i_first);
- }
- else {
- /* Previous call could have bind the default vao
- * see above. */
- glBindVertexArray(batch->vao_id);
- }
- }
-
- if (batch->elem) {
- const GPUIndexBuf *el = batch->elem;
- GLenum index_type = INDEX_TYPE(el);
- GLint base_index = BASE_INDEX(el);
- void *v_first_ofs = elem_offset(el, v_first);
-
- if (GPU_arb_base_instance_is_supported()) {
- glDrawElementsInstancedBaseVertexBaseInstance(
- batch->gl_prim_type, v_count, index_type, v_first_ofs, i_count, base_index, i_first);
- }
- else {
- glDrawElementsInstancedBaseVertex(
- batch->gl_prim_type, v_count, index_type, v_first_ofs, i_count, base_index);
- }
- }
- else {
-#ifdef __APPLE__
- glDisable(GL_PRIMITIVE_RESTART);
-#endif
- if (GPU_arb_base_instance_is_supported()) {
- glDrawArraysInstancedBaseInstance(batch->gl_prim_type, v_first, v_count, i_count, i_first);
- }
- else {
- glDrawArraysInstanced(batch->gl_prim_type, v_first, v_count, i_count);
- }
-#ifdef __APPLE__
- glEnable(GL_PRIMITIVE_RESTART);
-#endif
- }
-
-#ifdef __APPLE__
- if (vao != 0) {
- glDeleteVertexArrays(1, &vao);
- }
-#endif
-}
-
-/* just draw some vertices and let shader place them where we want. */
-void GPU_draw_primitive(GPUPrimType prim_type, int v_count)
-{
- /* we cannot draw without vao ... annoying ... */
- glBindVertexArray(GPU_vao_default());
-
- GLenum type = convert_prim_type_to_gl(prim_type);
- glDrawArrays(type, 0, v_count);
-
- /* Performance hog if you are drawing with the same vao multiple time.
- * Only activate for debugging.*/
- // glBindVertexArray(0);
-}
-
-/* -------------------------------------------------------------------- */
-/** \name Indirect Draw Calls
- * \{ */
-
-#if 0
-# define USE_MULTI_DRAW_INDIRECT 0
-#else
-# define USE_MULTI_DRAW_INDIRECT \
- (GL_ARB_multi_draw_indirect && GPU_arb_base_instance_is_supported())
-#endif
-
-typedef struct GPUDrawCommand {
- uint v_count;
- uint i_count;
- uint v_first;
- uint i_first;
-} GPUDrawCommand;
-
-typedef struct GPUDrawCommandIndexed {
- uint v_count;
- uint i_count;
- uint v_first;
- uint base_index;
- uint i_first;
-} GPUDrawCommandIndexed;
-
-struct GPUDrawList {
- GPUBatch *batch;
- uint base_index; /* Avoid dereferencing batch. */
- uint cmd_offset; /* in bytes, offset inside indirect command buffer. */
- uint cmd_len; /* Number of used command for the next call. */
- uint buffer_size; /* in bytes, size of indirect command buffer. */
- GLuint buffer_id; /* Draw Indirect Buffer id */
- union {
- GPUDrawCommand *commands;
- GPUDrawCommandIndexed *commands_indexed;
- };
-};
-
-GPUDrawList *GPU_draw_list_create(int length)
-{
- GPUDrawList *list = (GPUDrawList *)MEM_callocN(sizeof(GPUDrawList), "GPUDrawList");
- /* Alloc the biggest possible command list which is indexed. */
- list->buffer_size = sizeof(GPUDrawCommandIndexed) * length;
- if (USE_MULTI_DRAW_INDIRECT) {
- list->buffer_id = GPU_buf_alloc();
- glBindBuffer(GL_DRAW_INDIRECT_BUFFER, list->buffer_id);
- glBufferData(GL_DRAW_INDIRECT_BUFFER, list->buffer_size, NULL, GL_DYNAMIC_DRAW);
- }
- else {
- list->commands = (GPUDrawCommand *)MEM_mallocN(list->buffer_size, "GPUDrawList data");
- }
- return list;
-}
-
-void GPU_draw_list_discard(GPUDrawList *list)
-{
- if (list->buffer_id) {
- GPU_buf_free(list->buffer_id);
- }
- else {
- MEM_SAFE_FREE(list->commands);
- }
- MEM_freeN(list);
-}
-
-void GPU_draw_list_init(GPUDrawList *list, GPUBatch *batch)
-{
- BLI_assert(batch->phase == GPU_BATCH_READY_TO_DRAW);
- list->batch = batch;
- list->base_index = batch->elem ? BASE_INDEX(batch->elem) : UINT_MAX;
- list->cmd_len = 0;
-
- if (USE_MULTI_DRAW_INDIRECT) {
- if (list->commands == NULL) {
- glBindBuffer(GL_DRAW_INDIRECT_BUFFER, list->buffer_id);
- if (list->cmd_offset >= list->buffer_size) {
- /* Orphan buffer data and start fresh. */
- glBufferData(GL_DRAW_INDIRECT_BUFFER, list->buffer_size, NULL, GL_DYNAMIC_DRAW);
- list->cmd_offset = 0;
- }
- GLenum flags = GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT;
- list->commands = (GPUDrawCommand *)glMapBufferRange(
- GL_DRAW_INDIRECT_BUFFER, list->cmd_offset, list->buffer_size - list->cmd_offset, flags);
- }
- }
- else {
- list->cmd_offset = 0;
- }
-}
-
-void GPU_draw_list_command_add(
- GPUDrawList *list, int v_first, int v_count, int i_first, int i_count)
-{
- BLI_assert(list->commands);
-
- if (v_count == 0 || i_count == 0) {
- return;
- }
-
- if (list->base_index != UINT_MAX) {
- GPUDrawCommandIndexed *cmd = list->commands_indexed + list->cmd_len;
- cmd->v_first = v_first;
- cmd->v_count = v_count;
- cmd->i_count = i_count;
- cmd->base_index = list->base_index;
- cmd->i_first = i_first;
- }
- else {
- GPUDrawCommand *cmd = list->commands + list->cmd_len;
- cmd->v_first = v_first;
- cmd->v_count = v_count;
- cmd->i_count = i_count;
- cmd->i_first = i_first;
- }
-
- list->cmd_len++;
- uint offset = list->cmd_offset + list->cmd_len * sizeof(GPUDrawCommandIndexed);
-
- if (offset == list->buffer_size) {
- GPU_draw_list_submit(list);
- GPU_draw_list_init(list, list->batch);
- }
-}
-
-void GPU_draw_list_submit(GPUDrawList *list)
-{
- GPUBatch *batch = list->batch;
-
- if (list->cmd_len == 0) {
- return;
- }
-
- BLI_assert(list->commands);
- BLI_assert(batch->program_in_use);
- /* TODO could assert that VAO is bound. */
-
- /* TODO We loose a bit of memory here if we only draw arrays. Fix that. */
- uintptr_t offset = list->cmd_offset;
- uint cmd_len = list->cmd_len;
- size_t bytes_used = cmd_len * sizeof(GPUDrawCommandIndexed);
- list->cmd_len = 0; /* Avoid reuse. */
-
- /* Only do multi-draw indirect if doing more than 2 drawcall.
- * This avoids the overhead of buffer mapping if scene is
- * not very instance friendly.
- * BUT we also need to take into account the case where only
- * a few instances are needed to finish filling a call buffer. */
- const bool do_mdi = (cmd_len > 2) || (list->cmd_offset + bytes_used == list->buffer_size);
-
- if (USE_MULTI_DRAW_INDIRECT && do_mdi) {
- GLenum prim = batch->gl_prim_type;
-
- glBindBuffer(GL_DRAW_INDIRECT_BUFFER, list->buffer_id);
- glFlushMappedBufferRange(GL_DRAW_INDIRECT_BUFFER, 0, bytes_used);
- glUnmapBuffer(GL_DRAW_INDIRECT_BUFFER);
- list->commands = NULL; /* Unmapped */
- list->cmd_offset += bytes_used;
-
- if (batch->elem) {
- glMultiDrawElementsIndirect(prim, INDEX_TYPE(batch->elem), (void *)offset, cmd_len, 0);
- }
- else {
- glMultiDrawArraysIndirect(prim, (void *)offset, cmd_len, 0);
- }
- }
- else {
- /* Fallback */
- if (batch->elem) {
- GPUDrawCommandIndexed *cmd = list->commands_indexed;
- for (int i = 0; i < cmd_len; i++, cmd++) {
- /* Index start was added by Draw manager. Avoid counting it twice. */
- cmd->v_first -= batch->elem->index_start;
- GPU_batch_draw_advanced(batch, cmd->v_first, cmd->v_count, cmd->i_first, cmd->i_count);
- }
- }
- else {
- GPUDrawCommand *cmd = list->commands;
- for (int i = 0; i < cmd_len; i++, cmd++) {
- GPU_batch_draw_advanced(batch, cmd->v_first, cmd->v_count, cmd->i_first, cmd->i_count);
- }
- }
- }
+ static_cast<Batch *>(batch)->draw(v_first, v_count, i_first, i_count);
}
/** \} */
@@ -1015,23 +315,11 @@ void GPU_batch_program_set_imm_shader(GPUBatch *batch)
void gpu_batch_init(void)
{
- if (g_default_attr_vbo == 0) {
- g_default_attr_vbo = GPU_buf_alloc();
-
- float default_attrib_data[4] = {0.0f, 0.0f, 0.0f, 1.0f};
- glBindBuffer(GL_ARRAY_BUFFER, g_default_attr_vbo);
- glBufferData(GL_ARRAY_BUFFER, sizeof(float[4]), default_attrib_data, GL_STATIC_DRAW);
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- }
-
gpu_batch_presets_init();
}
void gpu_batch_exit(void)
{
- GPU_buf_free(g_default_attr_vbo);
- g_default_attr_vbo = 0;
-
gpu_batch_presets_exit();
}
diff --git a/source/blender/gpu/intern/gpu_batch_presets.c b/source/blender/gpu/intern/gpu_batch_presets.c
index 3d9b4326c7e..6a1645a71d8 100644
--- a/source/blender/gpu/intern/gpu_batch_presets.c
+++ b/source/blender/gpu/intern/gpu_batch_presets.c
@@ -35,7 +35,6 @@
#include "GPU_batch.h"
#include "GPU_batch_presets.h" /* own include */
#include "GPU_batch_utils.h"
-#include "gpu_shader_private.h"
/* -------------------------------------------------------------------- */
/** \name Local Structures
@@ -63,6 +62,7 @@ static struct {
static struct {
struct {
GPUBatch *panel_drag_widget;
+ GPUBatch *quad;
} batch;
float panel_drag_widget_pixelsize;
@@ -331,6 +331,24 @@ GPUBatch *GPU_batch_preset_panel_drag_widget(const float pixelsize,
return g_presets_2d.batch.panel_drag_widget;
}
+/* To be used with procedural placement inside shader. */
+GPUBatch *GPU_batch_preset_quad(void)
+{
+ if (!g_presets_2d.batch.quad) {
+ GPUVertBuf *vbo = GPU_vertbuf_create_with_format(preset_2d_format());
+ GPU_vertbuf_data_alloc(vbo, 4);
+
+ float pos_data[4][2] = {{0.0f, 0.0f}, {0.0f, 1.0f}, {1.0f, 1.0f}, {1.0f, 0.0f}};
+ GPU_vertbuf_attr_fill(vbo, g_presets_2d.attr_id.pos, pos_data);
+ /* Don't fill the color. */
+
+ g_presets_2d.batch.quad = GPU_batch_create_ex(GPU_PRIM_TRI_FAN, vbo, NULL, GPU_BATCH_OWNS_VBO);
+
+ gpu_batch_presets_register(g_presets_2d.batch.quad);
+ }
+ return g_presets_2d.batch.quad;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -380,18 +398,6 @@ bool gpu_batch_presets_unregister(GPUBatch *preset_batch)
return false;
}
-void gpu_batch_presets_reset(void)
-{
- BLI_mutex_lock(&g_presets_3d.mutex);
- /* Reset vao caches for these every time we switch opengl context.
- * This way they will draw correctly for each window. */
- LISTBASE_FOREACH (LinkData *, link, &presets_list) {
- GPUBatch *preset = link->data;
- GPU_batch_vao_cache_clear(preset);
- }
- BLI_mutex_unlock(&g_presets_3d.mutex);
-}
-
void gpu_batch_presets_exit(void)
{
LinkData *link;
@@ -404,17 +410,4 @@ void gpu_batch_presets_exit(void)
BLI_mutex_end(&g_presets_3d.mutex);
}
-/**
- * This function only needs to be accessed externally because
- * we are drawing UI batches with the DRW old context.
- *
- * And now we use it for drawing the entire area.
- *
- * XXX (Clément) - to cleanup in the upcoming 2.91 refactor.
- **/
-void GPU_batch_presets_reset()
-{
- gpu_batch_presets_reset();
-}
-
/** \} */
diff --git a/source/blender/gpu/intern/gpu_batch_private.h b/source/blender/gpu/intern/gpu_batch_private.hh
index 93745b9ca9b..c0444647fe1 100644
--- a/source/blender/gpu/intern/gpu_batch_private.h
+++ b/source/blender/gpu/intern/gpu_batch_private.hh
@@ -28,14 +28,21 @@
#include "GPU_batch.h"
#include "GPU_context.h"
-#include "GPU_shader_interface.h"
-#ifdef __cplusplus
-extern "C" {
-#endif
+namespace blender {
+namespace gpu {
-void gpu_batch_remove_interface_ref(GPUBatch *batch, const GPUShaderInterface *interface);
+/**
+ * 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(){};
+ virtual ~Batch(){};
-#ifdef __cplusplus
-}
-#endif
+ virtual void draw(int v_first, int v_count, int i_first, int i_count) = 0;
+};
+
+} // namespace gpu
+} // namespace blender
diff --git a/source/blender/gpu/intern/gpu_batch_utils.c b/source/blender/gpu/intern/gpu_batch_utils.c
index 0660d4a1724..e2d03d27035 100644
--- a/source/blender/gpu/intern/gpu_batch_utils.c
+++ b/source/blender/gpu/intern/gpu_batch_utils.c
@@ -28,7 +28,6 @@
#include "GPU_batch.h"
#include "GPU_batch_utils.h" /* own include */
-#include "gpu_shader_private.h"
/* -------------------------------------------------------------------- */
/** \name Polygon Creation (2D)
diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c
index b051d4fe59a..d67ce0be310 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 */
@@ -686,7 +686,7 @@ static char *code_generate_vertex(GPUNodeGraph *graph,
BLI_dynstr_append(ds, "#define USE_ATTR\n\n");
BLI_dynstr_append(ds, vert_code);
- BLI_dynstr_append(ds, "\n");
+ BLI_dynstr_append(ds, "\n\n");
BLI_dynstr_append(ds, "void pass_attr(vec3 position, mat3 normalmat, mat4 modelmatinv) {\n");
@@ -755,15 +755,16 @@ static char *code_generate_geometry(GPUNodeGraph *graph,
/* Generate varying assignments. */
BLI_dynstr_append(ds, "#define USE_ATTR\n");
- BLI_dynstr_append(ds, "void pass_attr(const int vert) {\n");
+ /* This needs to be a define. Some drivers don't like variable vert index inside dataAttrIn. */
+ BLI_dynstr_append(ds, "#define pass_attr(vert) {\\\n");
if (builtins & GPU_BARYCENTRIC_TEXCO) {
- BLI_dynstr_append(ds, " dataAttrOut.barycentricTexCo = calc_barycentric_co(vert);\n");
+ BLI_dynstr_append(ds, "dataAttrOut.barycentricTexCo = calc_barycentric_co(vert);\\\n");
}
LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) {
/* TODO let shader choose what to do depending on what the attribute is. */
- BLI_dynstr_appendf(ds, " dataAttrOut.var%d = dataAttrIn[vert].var%d;\n", attr->id, attr->id);
+ BLI_dynstr_appendf(ds, "dataAttrOut.var%d = dataAttrIn[vert].var%d;\\\n", attr->id, attr->id);
}
BLI_dynstr_append(ds, "}\n\n");
diff --git a/source/blender/gpu/intern/gpu_context.cc b/source/blender/gpu/intern/gpu_context.cc
index 283784aec20..85e7dffe3e7 100644
--- a/source/blender/gpu/intern/gpu_context.cc
+++ b/source/blender/gpu/intern/gpu_context.cc
@@ -40,7 +40,7 @@
#include "GHOST_C-api.h"
#include "gpu_backend.hh"
-#include "gpu_batch_private.h"
+#include "gpu_batch_private.hh"
#include "gpu_context_private.hh"
#include "gpu_matrix_private.h"
@@ -70,6 +70,12 @@ GPUContext::GPUContext()
GPUContext::~GPUContext()
{
GPU_matrix_state_discard(matrix_state);
+ delete state_manager;
+ delete front_left;
+ delete back_left;
+ delete front_right;
+ delete back_right;
+ delete imm;
}
bool GPUContext::is_active_on_thread(void)
@@ -83,12 +89,12 @@ bool GPUContext::is_active_on_thread(void)
GPUContext *GPU_context_create(void *ghost_window)
{
- if (gpu_backend_get() == NULL) {
+ if (GPUBackend::get() == NULL) {
/* TODO move where it make sense. */
GPU_backend_init(GPU_BACKEND_OPENGL);
}
- GPUContext *ctx = gpu_backend_get()->context_alloc(ghost_window);
+ GPUContext *ctx = GPUBackend::get()->context_alloc(ghost_window);
GPU_context_active_set(ctx);
return ctx;
@@ -120,18 +126,6 @@ GPUContext *GPU_context_active_get(void)
return active_ctx;
}
-GLuint GPU_vao_default(void)
-{
- BLI_assert(active_ctx); /* need at least an active context */
- return static_cast<GLContext *>(active_ctx)->default_vao_;
-}
-
-GLuint GPU_framebuffer_default(void)
-{
- BLI_assert(active_ctx); /* need at least an active context */
- return static_cast<GLContext *>(active_ctx)->default_framebuffer_;
-}
-
GLuint GPU_vao_alloc(void)
{
GLuint new_vao_id = 0;
@@ -173,63 +167,17 @@ void GPU_fbo_free(GLuint fbo_id, GPUContext *ctx)
void GPU_buf_free(GLuint buf_id)
{
/* TODO avoid using backend */
- GPUBackend *backend = gpu_backend_get();
+ GPUBackend *backend = GPUBackend::get();
static_cast<GLBackend *>(backend)->buf_free(buf_id);
}
void GPU_tex_free(GLuint tex_id)
{
/* TODO avoid using backend */
- GPUBackend *backend = gpu_backend_get();
+ GPUBackend *backend = GPUBackend::get();
static_cast<GLBackend *>(backend)->tex_free(tex_id);
}
-/* GPUBatch & GPUFrameBuffer contains respectively VAO & FBO indices
- * which are not shared across contexts. So we need to keep track of
- * ownership. */
-
-void gpu_context_add_batch(GPUContext *ctx, GPUBatch *batch)
-{
- BLI_assert(ctx);
- static_cast<GLContext *>(ctx)->batch_register(batch);
-}
-
-void gpu_context_remove_batch(GPUContext *ctx, GPUBatch *batch)
-{
- BLI_assert(ctx);
- static_cast<GLContext *>(ctx)->batch_unregister(batch);
-}
-
-void gpu_context_add_framebuffer(GPUContext *ctx, GPUFrameBuffer *fb)
-{
-#ifdef DEBUG
- BLI_assert(ctx);
- static_cast<GLContext *>(ctx)->framebuffer_register(fb);
-#else
- UNUSED_VARS(ctx, fb);
-#endif
-}
-
-void gpu_context_remove_framebuffer(GPUContext *ctx, GPUFrameBuffer *fb)
-{
-#ifdef DEBUG
- BLI_assert(ctx);
- static_cast<GLContext *>(ctx)->framebuffer_unregister(fb);
-#else
- UNUSED_VARS(ctx, fb);
-#endif
-}
-
-void gpu_context_active_framebuffer_set(GPUContext *ctx, GPUFrameBuffer *fb)
-{
- ctx->current_fbo = fb;
-}
-
-GPUFrameBuffer *gpu_context_active_framebuffer_get(GPUContext *ctx)
-{
- return ctx->current_fbo;
-}
-
struct GPUMatrixState *gpu_context_active_matrix_state_get()
{
BLI_assert(active_ctx);
@@ -285,7 +233,7 @@ void GPU_backend_exit(void)
delete g_backend;
}
-GPUBackend *gpu_backend_get(void)
+GPUBackend *GPUBackend::get(void)
{
return g_backend;
}
diff --git a/source/blender/gpu/intern/gpu_context_private.hh b/source/blender/gpu/intern/gpu_context_private.hh
index d369dbe7402..b72eee13105 100644
--- a/source/blender/gpu/intern/gpu_context_private.hh
+++ b/source/blender/gpu/intern/gpu_context_private.hh
@@ -29,25 +29,46 @@
#include "GPU_context.h"
+#include "gpu_framebuffer_private.hh"
+#include "gpu_immediate_private.hh"
+#include "gpu_shader_private.hh"
+#include "gpu_state_private.hh"
+
#include <mutex>
#include <pthread.h>
#include <string.h>
#include <unordered_set>
#include <vector>
-struct GPUFrameBuffer;
struct GPUMatrixState;
struct GPUContext {
public:
/** State managment */
- GPUFrameBuffer *current_fbo = NULL;
+ blender::gpu::Shader *shader = NULL;
+ blender::gpu::FrameBuffer *active_fb = NULL;
GPUMatrixState *matrix_state = NULL;
+ blender::gpu::GPUStateManager *state_manager = NULL;
+ blender::gpu::Immediate *imm = NULL;
+
+ /**
+ * All 4 window framebuffers.
+ * None of them are valid in an offscreen context.
+ * Right framebuffers are only available if using stereo rendering.
+ * Front framebuffers contains (in principle, but not always) the last frame color.
+ * Default framebuffer is back_left.
+ */
+ blender::gpu::FrameBuffer *back_left = NULL;
+ blender::gpu::FrameBuffer *front_left = NULL;
+ blender::gpu::FrameBuffer *back_right = NULL;
+ blender::gpu::FrameBuffer *front_right = NULL;
protected:
/** Thread on which this context is active. */
pthread_t thread_;
bool is_active_;
+ /** Avoid including GHOST headers. Can be NULL for offscreen contexts. */
+ void *ghost_window_;
public:
GPUContext();
@@ -61,9 +82,6 @@ struct GPUContext {
MEM_CXX_CLASS_ALLOC_FUNCS("GPUContext")
};
-GLuint GPU_vao_default(void);
-GLuint GPU_framebuffer_default(void);
-
/* These require a gl ctx bound. */
GLuint GPU_buf_alloc(void);
GLuint GPU_tex_alloc(void);
@@ -77,12 +95,6 @@ void GPU_tex_free(GLuint tex_id);
void GPU_vao_free(GLuint vao_id, GPUContext *ctx);
void GPU_fbo_free(GLuint fbo_id, GPUContext *ctx);
-void gpu_context_add_batch(GPUContext *ctx, GPUBatch *batch);
-void gpu_context_remove_batch(GPUContext *ctx, GPUBatch *batch);
-
-void gpu_context_add_framebuffer(GPUContext *ctx, struct GPUFrameBuffer *fb);
-void gpu_context_remove_framebuffer(GPUContext *ctx, struct GPUFrameBuffer *fb);
-
void gpu_context_active_framebuffer_set(GPUContext *ctx, struct GPUFrameBuffer *fb);
struct GPUFrameBuffer *gpu_context_active_framebuffer_get(GPUContext *ctx);
diff --git a/source/blender/gpu/intern/gpu_debug.cc b/source/blender/gpu/intern/gpu_debug.cc
index f7d6236071d..f179a241926 100644
--- a/source/blender/gpu/intern/gpu_debug.cc
+++ b/source/blender/gpu/intern/gpu_debug.cc
@@ -36,226 +36,6 @@
#include <stdlib.h>
#include <string.h>
-#ifndef __APPLE__ /* only non-Apple systems implement OpenGL debug callbacks */
-
-/* control whether we use older AMD_debug_output extension
- * some supported GPU + OS combos do not have the newer extensions */
-# define LEGACY_DEBUG 1
-
-/* Debug callbacks need the same calling convention as OpenGL functions. */
-# if defined(_WIN32)
-# define APIENTRY __stdcall
-# else
-# define APIENTRY
-# endif
-
-static const char *source_name(GLenum source)
-{
- switch (source) {
- case GL_DEBUG_SOURCE_API:
- return "API";
- case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
- return "window system";
- case GL_DEBUG_SOURCE_SHADER_COMPILER:
- return "shader compiler";
- case GL_DEBUG_SOURCE_THIRD_PARTY:
- return "3rd party";
- case GL_DEBUG_SOURCE_APPLICATION:
- return "application";
- case GL_DEBUG_SOURCE_OTHER:
- return "other";
- default:
- return "???";
- }
-}
-
-static const char *message_type_name(GLenum message)
-{
- switch (message) {
- case GL_DEBUG_TYPE_ERROR:
- return "error";
- case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
- return "deprecated behavior";
- case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
- return "undefined behavior";
- case GL_DEBUG_TYPE_PORTABILITY:
- return "portability";
- case GL_DEBUG_TYPE_PERFORMANCE:
- return "performance";
- case GL_DEBUG_TYPE_OTHER:
- return "other";
- case GL_DEBUG_TYPE_MARKER:
- return "marker"; /* KHR has this, ARB does not */
- default:
- return "???";
- }
-}
-
-static void APIENTRY gpu_debug_proc(GLenum source,
- GLenum type,
- GLuint UNUSED(id),
- GLenum severity,
- GLsizei UNUSED(length),
- const GLchar *message,
- const GLvoid *UNUSED(userParm))
-{
- bool backtrace = false;
-
- switch (severity) {
- case GL_DEBUG_SEVERITY_HIGH:
- backtrace = true;
- ATTR_FALLTHROUGH;
- case GL_DEBUG_SEVERITY_MEDIUM:
- case GL_DEBUG_SEVERITY_LOW:
- case GL_DEBUG_SEVERITY_NOTIFICATION: /* KHR has this, ARB does not */
- fprintf(stderr, "GL %s %s: %s\n", source_name(source), message_type_name(type), message);
- }
-
- if (backtrace) {
- BLI_system_backtrace(stderr);
- fflush(stderr);
- }
-}
-
-# if LEGACY_DEBUG
-
-static const char *category_name_amd(GLenum category)
-{
- switch (category) {
- case GL_DEBUG_CATEGORY_API_ERROR_AMD:
- return "API error";
- case GL_DEBUG_CATEGORY_WINDOW_SYSTEM_AMD:
- return "window system";
- case GL_DEBUG_CATEGORY_DEPRECATION_AMD:
- return "deprecated behavior";
- case GL_DEBUG_CATEGORY_UNDEFINED_BEHAVIOR_AMD:
- return "undefined behavior";
- case GL_DEBUG_CATEGORY_PERFORMANCE_AMD:
- return "performance";
- case GL_DEBUG_CATEGORY_SHADER_COMPILER_AMD:
- return "shader compiler";
- case GL_DEBUG_CATEGORY_APPLICATION_AMD:
- return "application";
- case GL_DEBUG_CATEGORY_OTHER_AMD:
- return "other";
- default:
- return "???";
- }
-}
-
-static void APIENTRY gpu_debug_proc_amd(GLuint UNUSED(id),
- GLenum category,
- GLenum severity,
- GLsizei UNUSED(length),
- const GLchar *message,
- GLvoid *UNUSED(userParm))
-{
- bool backtrace = false;
-
- switch (severity) {
- case GL_DEBUG_SEVERITY_HIGH:
- backtrace = true;
- ATTR_FALLTHROUGH;
- case GL_DEBUG_SEVERITY_MEDIUM:
- case GL_DEBUG_SEVERITY_LOW:
- fprintf(stderr, "GL %s: %s\n", category_name_amd(category), message);
- }
-
- if (backtrace) {
- BLI_system_backtrace(stderr);
- fflush(stderr);
- }
-}
-# endif /* LEGACY_DEBUG */
-
-# undef APIENTRY
-#endif /* not Apple */
-
-void gpu_debug_init(void)
-{
-#ifdef __APPLE__
- fprintf(stderr, "OpenGL debug callback is not available on Apple.\n");
-#else /* not Apple */
- const char success[] = "Successfully hooked OpenGL debug callback.";
-
- if (GLEW_VERSION_4_3 || GLEW_KHR_debug) {
- fprintf(stderr,
- "Using %s\n",
- GLEW_VERSION_4_3 ? "OpenGL 4.3 debug facilities" : "KHR_debug extension");
- glEnable(GL_DEBUG_OUTPUT);
- glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
- glDebugMessageCallback((GLDEBUGPROC)gpu_debug_proc, NULL);
- glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE);
- GPU_string_marker(success);
- }
- else if (GLEW_ARB_debug_output) {
- fprintf(stderr, "Using ARB_debug_output extension\n");
- glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
- glDebugMessageCallbackARB((GLDEBUGPROCARB)gpu_debug_proc, NULL);
- glDebugMessageControlARB(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE);
- GPU_string_marker(success);
- }
-# if LEGACY_DEBUG
- else if (GLEW_AMD_debug_output) {
- fprintf(stderr, "Using AMD_debug_output extension\n");
- glDebugMessageCallbackAMD(gpu_debug_proc_amd, NULL);
- glDebugMessageEnableAMD(GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE);
- GPU_string_marker(success);
- }
-# endif
- else {
- fprintf(stderr, "Failed to hook OpenGL debug callback.\n");
- }
-#endif /* not Apple */
-}
-
-void gpu_debug_exit(void)
-{
-#ifndef __APPLE__
- if (GLEW_VERSION_4_3 || GLEW_KHR_debug) {
- glDebugMessageCallback(NULL, NULL);
- }
- else if (GLEW_ARB_debug_output) {
- glDebugMessageCallbackARB(NULL, NULL);
- }
-# if LEGACY_DEBUG
- else if (GLEW_AMD_debug_output) {
- glDebugMessageCallbackAMD(NULL, NULL);
- }
-# endif
-#endif
-}
-
-void GPU_string_marker(const char *buf)
-{
-#ifdef __APPLE__
- UNUSED_VARS(buf);
-#else /* not Apple */
- if (GLEW_VERSION_4_3 || GLEW_KHR_debug) {
- glDebugMessageInsert(GL_DEBUG_SOURCE_APPLICATION,
- GL_DEBUG_TYPE_MARKER,
- 0,
- GL_DEBUG_SEVERITY_NOTIFICATION,
- -1,
- buf);
- }
- else if (GLEW_ARB_debug_output) {
- glDebugMessageInsertARB(GL_DEBUG_SOURCE_APPLICATION_ARB,
- GL_DEBUG_TYPE_OTHER_ARB,
- 0,
- GL_DEBUG_SEVERITY_LOW_ARB,
- -1,
- buf);
- }
-# if LEGACY_DEBUG
- else if (GLEW_AMD_debug_output) {
- glDebugMessageInsertAMD(
- GL_DEBUG_CATEGORY_APPLICATION_AMD, GL_DEBUG_SEVERITY_LOW_AMD, 0, 0, buf);
- }
-# endif
-#endif /* not Apple */
-}
-
void GPU_print_error_debug(const char *str)
{
if (G.debug & G_DEBUG) {
diff --git a/source/blender/gpu/intern/gpu_primitive.c b/source/blender/gpu/intern/gpu_drawlist.cc
index 3b11b38db87..7b807a2fa80 100644
--- a/source/blender/gpu/intern/gpu_primitive.c
+++ b/source/blender/gpu/intern/gpu_drawlist.cc
@@ -20,30 +20,40 @@
/** \file
* \ingroup gpu
*
- * GPU geometric primitives
+ * Implementation of Multi Draw Indirect.
*/
-#include "GPU_primitive.h"
-#include "gpu_primitive_private.h"
+#include "MEM_guardedalloc.h"
-GLenum convert_prim_type_to_gl(GPUPrimType prim_type)
+#include "GPU_batch.h"
+#include "GPU_drawlist.h"
+
+#include "gpu_backend.hh"
+
+#include "gpu_drawlist_private.hh"
+
+using namespace blender::gpu;
+
+GPUDrawList GPU_draw_list_create(int list_length)
+{
+ DrawList *list_ptr = GPUBackend::get()->drawlist_alloc(list_length);
+ return reinterpret_cast<DrawList *>(list_ptr);
+}
+
+void GPU_draw_list_discard(GPUDrawList list)
+{
+ DrawList *list_ptr = reinterpret_cast<DrawList *>(list);
+ delete list_ptr;
+}
+
+void GPU_draw_list_append(GPUDrawList list, GPUBatch *batch, int i_first, int i_count)
+{
+ DrawList *list_ptr = reinterpret_cast<DrawList *>(list);
+ list_ptr->append(batch, i_first, i_count);
+}
+
+void GPU_draw_list_submit(GPUDrawList list)
{
-#if TRUST_NO_ONE
- assert(prim_type != GPU_PRIM_NONE);
-#endif
- static const GLenum table[] = {
- [GPU_PRIM_POINTS] = GL_POINTS,
- [GPU_PRIM_LINES] = GL_LINES,
- [GPU_PRIM_LINE_STRIP] = GL_LINE_STRIP,
- [GPU_PRIM_LINE_LOOP] = GL_LINE_LOOP,
- [GPU_PRIM_TRIS] = GL_TRIANGLES,
- [GPU_PRIM_TRI_STRIP] = GL_TRIANGLE_STRIP,
- [GPU_PRIM_TRI_FAN] = GL_TRIANGLE_FAN,
-
- [GPU_PRIM_LINES_ADJ] = GL_LINES_ADJACENCY,
- [GPU_PRIM_LINE_STRIP_ADJ] = GL_LINE_STRIP_ADJACENCY,
- [GPU_PRIM_TRIS_ADJ] = GL_TRIANGLES_ADJACENCY,
- };
-
- return table[prim_type];
+ DrawList *list_ptr = reinterpret_cast<DrawList *>(list);
+ list_ptr->submit();
}
diff --git a/source/blender/gpu/intern/gpu_drawlist_private.hh b/source/blender/gpu/intern/gpu_drawlist_private.hh
new file mode 100644
index 00000000000..ddb09fb0c89
--- /dev/null
+++ b/source/blender/gpu/intern/gpu_drawlist_private.hh
@@ -0,0 +1,44 @@
+/*
+ * 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"
+
+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(){};
+
+ virtual void append(GPUBatch *batch, int i_first, int i_count) = 0;
+ virtual void submit() = 0;
+};
+
+} // namespace gpu
+} // namespace blender
diff --git a/source/blender/gpu/intern/gpu_element.cc b/source/blender/gpu/intern/gpu_element.cc
index cf7cc1d214c..29c95c725fd 100644
--- a/source/blender/gpu/intern/gpu_element.cc
+++ b/source/blender/gpu/intern/gpu_element.cc
@@ -326,6 +326,11 @@ static void squeeze_indices_short(GPUIndexBufBuilder *builder,
#endif /* GPU_TRACK_INDEX_RANGE */
+GPUIndexBuf *GPU_indexbuf_calloc(void)
+{
+ return (GPUIndexBuf *)MEM_callocN(sizeof(GPUIndexBuf), __func__);
+}
+
GPUIndexBuf *GPU_indexbuf_build(GPUIndexBufBuilder *builder)
{
GPUIndexBuf *elem = (GPUIndexBuf *)MEM_callocN(sizeof(GPUIndexBuf), "GPUIndexBuf");
diff --git a/source/blender/gpu/intern/gpu_extensions.cc b/source/blender/gpu/intern/gpu_extensions.cc
index 8074e4b64f0..6fe08d81cda 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;
@@ -118,7 +116,7 @@ static void gpu_detect_mip_render_workaround(void)
glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_MAX_LEVEL, 0);
GPU_texture_unbind(tex);
- GPUFrameBuffer *fb = GPU_framebuffer_create();
+ GPUFrameBuffer *fb = GPU_framebuffer_create(__func__);
GPU_framebuffer_texture_attach(fb, tex, 0, 1);
GPU_framebuffer_bind(fb);
GPU_framebuffer_clear_color(fb, clear_color);
@@ -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 5f3089b2ffb..1b6fea56028 100644
--- a/source/blender/gpu/intern/gpu_framebuffer.cc
+++ b/source/blender/gpu/intern/gpu_framebuffer.cc
@@ -29,681 +29,396 @@
#include "GPU_batch.h"
#include "GPU_extensions.h"
-#include "GPU_framebuffer.h"
#include "GPU_shader.h"
#include "GPU_texture.h"
+#include "gpu_backend.hh"
#include "gpu_context_private.hh"
#include "gpu_private.h"
+#include "gpu_texture_private.hh"
-typedef enum {
- GPU_FB_DEPTH_ATTACHMENT = 0,
- GPU_FB_DEPTH_STENCIL_ATTACHMENT,
- GPU_FB_COLOR_ATTACHMENT0,
- GPU_FB_COLOR_ATTACHMENT1,
- GPU_FB_COLOR_ATTACHMENT2,
- GPU_FB_COLOR_ATTACHMENT3,
- GPU_FB_COLOR_ATTACHMENT4,
- GPU_FB_COLOR_ATTACHMENT5,
- /* Number of maximum output slots.
- * We support 6 outputs for now (usually we wouldn't need more to preserve fill rate). */
- /* Keep in mind that GL max is GL_MAX_DRAW_BUFFERS and is at least 8, corresponding to
- * the maximum number of COLOR attachments specified by glDrawBuffers. */
- GPU_FB_MAX_ATTACHEMENT,
-} GPUAttachmentType;
-
-#define FOREACH_ATTACHMENT_RANGE(att, _start, _end) \
- for (GPUAttachmentType att = static_cast<GPUAttachmentType>(_start); att < _end; \
- att = static_cast<GPUAttachmentType>(att + 1))
-
-#define GPU_FB_MAX_COLOR_ATTACHMENT (GPU_FB_MAX_ATTACHEMENT - GPU_FB_COLOR_ATTACHMENT0)
-
-#define GPU_FB_DIRTY_DRAWBUFFER (1 << 15)
-
-#define GPU_FB_ATTACHEMENT_IS_DIRTY(flag, type) ((flag & (1 << type)) != 0)
-#define GPU_FB_ATTACHEMENT_SET_DIRTY(flag, type) (flag |= (1 << type))
-
-struct GPUFrameBuffer {
- GPUContext *ctx;
- GLuint object;
- GPUAttachment attachments[GPU_FB_MAX_ATTACHEMENT];
- uint16_t dirty_flag;
- int width, height;
- bool multisample;
- /* TODO Check that we always use the right context when binding
- * (FBOs are not shared across ogl contexts). */
- // void *ctx;
-};
+#include "gpu_framebuffer_private.hh"
+
+namespace blender::gpu {
+
+/* -------------------------------------------------------------------- */
+/** \name Constructor / Destructor
+ * \{ */
-static GLenum convert_attachment_type_to_gl(GPUAttachmentType type)
+FrameBuffer::FrameBuffer(const char *name)
{
-#define ATTACHMENT(type) \
- case GPU_FB_##type: { \
- return GL_##type; \
- } \
- ((void)0)
-
- switch (type) {
- ATTACHMENT(DEPTH_ATTACHMENT);
- ATTACHMENT(DEPTH_STENCIL_ATTACHMENT);
- ATTACHMENT(COLOR_ATTACHMENT0);
- ATTACHMENT(COLOR_ATTACHMENT1);
- ATTACHMENT(COLOR_ATTACHMENT2);
- ATTACHMENT(COLOR_ATTACHMENT3);
- ATTACHMENT(COLOR_ATTACHMENT4);
- ATTACHMENT(COLOR_ATTACHMENT5);
- default:
- BLI_assert(0);
- return GL_COLOR_ATTACHMENT0;
+ if (name) {
+ BLI_strncpy(name_, name, sizeof(name_));
}
-}
+ else {
+ name_[0] = '\0';
+ }
+ /* Force config on first use. */
+ dirty_attachments_ = true;
+ dirty_state_ = true;
-static GPUAttachmentType attachment_type_from_tex(GPUTexture *tex, int slot)
-{
- switch (GPU_texture_format(tex)) {
- case GPU_DEPTH_COMPONENT32F:
- case GPU_DEPTH_COMPONENT24:
- case GPU_DEPTH_COMPONENT16:
- return GPU_FB_DEPTH_ATTACHMENT;
- case GPU_DEPTH24_STENCIL8:
- case GPU_DEPTH32F_STENCIL8:
- return GPU_FB_DEPTH_STENCIL_ATTACHMENT;
- default:
- return static_cast<GPUAttachmentType>(GPU_FB_COLOR_ATTACHMENT0 + slot);
+ for (int i = 0; i < ARRAY_SIZE(attachments_); i++) {
+ attachments_[i].tex = NULL;
+ attachments_[i].mip = -1;
+ attachments_[i].layer = -1;
}
}
-static GLenum convert_buffer_bits_to_gl(eGPUFrameBufferBits bits)
+FrameBuffer::~FrameBuffer()
{
- GLbitfield mask = 0;
- mask |= (bits & GPU_DEPTH_BIT) ? GL_DEPTH_BUFFER_BIT : 0;
- mask |= (bits & GPU_STENCIL_BIT) ? GL_STENCIL_BUFFER_BIT : 0;
- mask |= (bits & GPU_COLOR_BIT) ? GL_COLOR_BUFFER_BIT : 0;
- return mask;
+ GPUFrameBuffer *gpu_fb = reinterpret_cast<GPUFrameBuffer *>(this);
+ for (int i = 0; i < ARRAY_SIZE(attachments_); i++) {
+ if (attachments_[i].tex != NULL) {
+ GPU_texture_detach_framebuffer(attachments_[i].tex, gpu_fb);
+ }
+ }
}
-static GPUTexture *framebuffer_get_depth_tex(GPUFrameBuffer *fb)
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Attachments managment
+ * \{ */
+
+void FrameBuffer::attachment_set(GPUAttachmentType type, const GPUAttachment &new_attachment)
{
- if (fb->attachments[GPU_FB_DEPTH_ATTACHMENT].tex) {
- return fb->attachments[GPU_FB_DEPTH_ATTACHMENT].tex;
+ if (new_attachment.mip == -1) {
+ return; /* GPU_ATTACHMENT_LEAVE */
}
- return fb->attachments[GPU_FB_DEPTH_STENCIL_ATTACHMENT].tex;
-}
+ if (type >= GPU_FB_MAX_ATTACHEMENT) {
+ fprintf(stderr,
+ "GPUFramebuffer: Error: Trying to attach texture to type %d but maximum slot is %d.\n",
+ type - GPU_FB_COLOR_ATTACHMENT0,
+ GPU_FB_MAX_COLOR_ATTACHMENT);
+ return;
+ }
-static GPUTexture *framebuffer_get_color_tex(GPUFrameBuffer *fb, int slot)
-{
- return fb->attachments[GPU_FB_COLOR_ATTACHMENT0 + slot].tex;
-}
+ if (new_attachment.tex) {
+ if (new_attachment.layer > 0) {
+ BLI_assert(ELEM(GPU_texture_target(new_attachment.tex),
+ GL_TEXTURE_2D_ARRAY,
+ GL_TEXTURE_CUBE_MAP,
+ GL_TEXTURE_CUBE_MAP_ARRAY_ARB));
+ }
+ if (GPU_texture_stencil(new_attachment.tex)) {
+ BLI_assert(ELEM(type, GPU_FB_DEPTH_STENCIL_ATTACHMENT));
+ }
+ else if (GPU_texture_depth(new_attachment.tex)) {
+ BLI_assert(ELEM(type, GPU_FB_DEPTH_ATTACHMENT));
+ }
+ }
-static void gpu_print_framebuffer_error(GLenum status, char err_out[256])
-{
- const char *format = "GPUFrameBuffer: framebuffer status %s\n";
- const char *err = "unknown";
-
-#define FORMAT_STATUS(X) \
- case GL_FRAMEBUFFER_##X: { \
- err = "GL_FRAMEBUFFER_" #X; \
- break; \
- } \
- ((void)0)
-
- switch (status) {
- /* success */
- FORMAT_STATUS(COMPLETE);
- /* errors shared by OpenGL desktop & ES */
- FORMAT_STATUS(INCOMPLETE_ATTACHMENT);
- FORMAT_STATUS(INCOMPLETE_MISSING_ATTACHMENT);
- FORMAT_STATUS(UNSUPPORTED);
-#if 0 /* for OpenGL ES only */
- FORMAT_STATUS(INCOMPLETE_DIMENSIONS);
-#else /* for desktop GL only */
- FORMAT_STATUS(INCOMPLETE_DRAW_BUFFER);
- FORMAT_STATUS(INCOMPLETE_READ_BUFFER);
- FORMAT_STATUS(INCOMPLETE_MULTISAMPLE);
- FORMAT_STATUS(UNDEFINED);
-#endif
+ GPUAttachment &attachment = attachments_[type];
+
+ if (attachment.tex == new_attachment.tex && attachment.layer == new_attachment.layer &&
+ attachment.mip == new_attachment.mip) {
+ return; /* Exact same texture already bound here. */
+ }
+ /* Unbind previous and bind new. */
+ /* TODO(fclem) cleanup the casts. */
+ if (attachment.tex) {
+ GPU_texture_detach_framebuffer(attachment.tex, reinterpret_cast<GPUFrameBuffer *>(this));
}
-#undef FORMAT_STATUS
+ attachment = new_attachment;
- if (err_out) {
- BLI_snprintf(err_out, 256, format, err);
+ /* Might be null if this is for unbinding. */
+ if (attachment.tex) {
+ GPU_texture_attach_framebuffer(attachment.tex, reinterpret_cast<GPUFrameBuffer *>(this), type);
}
else {
- fprintf(stderr, format, err);
+ /* GPU_ATTACHMENT_NONE */
}
-}
-
-void gpu_framebuffer_module_init(void)
-{
-}
-void gpu_framebuffer_module_exit(void)
-{
+ dirty_attachments_ = true;
}
-GPUFrameBuffer *GPU_framebuffer_active_get(void)
+void FrameBuffer::recursive_downsample(int max_lvl,
+ void (*callback)(void *userData, int level),
+ void *userData)
{
GPUContext *ctx = GPU_context_active_get();
- if (ctx) {
- return gpu_context_active_framebuffer_get(ctx);
+ /* Bind to make sure the framebuffer is up to date. */
+ this->bind(true);
+
+ if (width_ == 1 && height_ == 1) {
+ return;
}
+ /* HACK: Make the framebuffer appear not bound to avoid assert in GPU_texture_bind. */
+ ctx->active_fb = NULL;
- return 0;
-}
+ int levels = floor(log2(max_ii(width_, height_)));
+ max_lvl = min_ii(max_lvl, levels);
-static void gpu_framebuffer_current_set(GPUFrameBuffer *fb)
-{
- GPUContext *ctx = GPU_context_active_get();
- if (ctx) {
- gpu_context_active_framebuffer_set(ctx, fb);
+ int current_dim[2] = {width_, height_};
+ int mip_lvl;
+ for (mip_lvl = 1; mip_lvl < max_lvl + 1; mip_lvl++) {
+ /* calculate next viewport size */
+ current_dim[0] = max_ii(current_dim[0] / 2, 1);
+ current_dim[1] = max_ii(current_dim[1] / 2, 1);
+ /* Replace attaached miplevel for each attachement. */
+ for (int att = 0; att < ARRAY_SIZE(attachments_); att++) {
+ GPUTexture *tex = attachments_[att].tex;
+ if (tex != NULL) {
+ /* Some Intel HDXXX have issue with rendering to a mipmap that is below
+ * the texture GL_TEXTURE_MAX_LEVEL. So even if it not correct, in this case
+ * we allow GL_TEXTURE_MAX_LEVEL to be one level lower. In practice it does work! */
+ int map_lvl = (GPU_mip_render_workaround()) ? mip_lvl : (mip_lvl - 1);
+ /* Restrict fetches only to previous level. */
+ GPU_texture_bind(tex, 0);
+ glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_BASE_LEVEL, mip_lvl - 1);
+ glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_MAX_LEVEL, map_lvl);
+ GPU_texture_unbind(tex);
+ /* Bind next level. */
+ attachments_[att].mip = mip_lvl;
+ }
+ }
+ /* Update the internal attachments and viewport size. */
+ dirty_attachments_ = true;
+ this->bind(true);
+ /* HACK: Make the framebuffer appear not bound to avoid assert in GPU_texture_bind. */
+ ctx->active_fb = NULL;
+
+ callback(userData, mip_lvl);
+
+ /* This is the last mipmap level. Exit loop without incrementing mip_lvl. */
+ if (current_dim[0] == 1 && current_dim[1] == 1) {
+ break;
+ }
+ }
+
+ for (int att = 0; att < ARRAY_SIZE(attachments_); att++) {
+ if (attachments_[att].tex != NULL) {
+ /* Reset mipmap level range. */
+ GPUTexture *tex = attachments_[att].tex;
+ GPU_texture_bind(tex, 0);
+ glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_BASE_LEVEL, 0);
+ glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_MAX_LEVEL, mip_lvl);
+ GPU_texture_unbind(tex);
+ /* Reset base level. NOTE: might not be the one bound at the start of this function. */
+ attachments_[att].mip = 0;
+ }
}
+ dirty_attachments_ = true;
}
-/* GPUFrameBuffer */
+/** \} */
+
+} // namespace blender::gpu
-GPUFrameBuffer *GPU_framebuffer_create(void)
+/* -------------------------------------------------------------------- */
+/** \name C-API
+ * \{ */
+
+using namespace blender;
+using namespace blender::gpu;
+
+GPUFrameBuffer *GPU_framebuffer_create(const char *name)
{
/* We generate the FB object later at first use in order to
* create the framebuffer in the right opengl context. */
- return (GPUFrameBuffer *)MEM_callocN(sizeof(GPUFrameBuffer), "GPUFrameBuffer");
+ return (GPUFrameBuffer *)GPUBackend::get()->framebuffer_alloc(name);
}
-static void gpu_framebuffer_init(GPUFrameBuffer *fb)
+void GPU_framebuffer_free(GPUFrameBuffer *gpu_fb)
{
- fb->object = GPU_fbo_alloc();
- fb->ctx = GPU_context_active_get();
- gpu_context_add_framebuffer(fb->ctx, fb);
+ delete reinterpret_cast<FrameBuffer *>(gpu_fb);
}
-void GPU_framebuffer_free(GPUFrameBuffer *fb)
-{
- for (int i_type = 0; i_type < GPU_FB_MAX_ATTACHEMENT; i_type++) {
- GPUAttachmentType type = static_cast<GPUAttachmentType>(i_type);
- if (fb->attachments[type].tex != NULL) {
- GPU_framebuffer_texture_detach(fb, fb->attachments[type].tex);
- }
- }
-
- if (fb->object != 0) {
- /* This restores the framebuffer if it was bound */
- GPU_fbo_free(fb->object, fb->ctx);
- gpu_context_remove_framebuffer(fb->ctx, fb);
- }
+/* ---------- Binding ----------- */
- if (GPU_framebuffer_active_get() == fb) {
- gpu_framebuffer_current_set(NULL);
- }
-
- MEM_freeN(fb);
+void GPU_framebuffer_bind(GPUFrameBuffer *gpu_fb)
+{
+ FrameBuffer *fb = reinterpret_cast<FrameBuffer *>(gpu_fb);
+ const bool enable_srgb = true;
+ fb->bind(enable_srgb);
}
-/* ---------- Attach ----------- */
-
-static void gpu_framebuffer_texture_attach_ex(
- GPUFrameBuffer *fb, GPUTexture *tex, int slot, int layer, int mip)
+/* Workaround for binding a srgb framebuffer without doing the srgb transform. */
+void GPU_framebuffer_bind_no_srgb(GPUFrameBuffer *gpu_fb)
{
- if (slot >= GPU_FB_MAX_COLOR_ATTACHMENT) {
- fprintf(stderr,
- "Attaching to index %d framebuffer slot unsupported. "
- "Use at most %d\n",
- slot,
- GPU_FB_MAX_COLOR_ATTACHMENT);
- return;
- }
+ FrameBuffer *fb = reinterpret_cast<FrameBuffer *>(gpu_fb);
+ const bool enable_srgb = false;
+ fb->bind(enable_srgb);
+}
- GPUAttachmentType type = attachment_type_from_tex(tex, slot);
- GPUAttachment *attachment = &fb->attachments[type];
+/* For stereo rendering. */
+void GPU_backbuffer_bind(eGPUBackBuffer buffer)
+{
+ GPUContext *ctx = GPU_context_active_get();
- if ((attachment->tex == tex) && (attachment->mip == mip) && (attachment->layer == layer)) {
- return; /* Exact same texture already bound here. */
- }
- if (attachment->tex != NULL) {
- GPU_framebuffer_texture_detach(fb, attachment->tex);
+ if (buffer == GPU_BACKBUFFER_LEFT) {
+ ctx->back_left->bind(false);
}
-
- if (attachment->tex == NULL) {
- GPU_texture_attach_framebuffer(tex, fb, type);
+ else {
+ ctx->back_right->bind(false);
}
-
- attachment->tex = tex;
- attachment->mip = mip;
- attachment->layer = layer;
- GPU_FB_ATTACHEMENT_SET_DIRTY(fb->dirty_flag, type);
}
-void GPU_framebuffer_texture_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, int mip)
+void GPU_framebuffer_restore(void)
{
- gpu_framebuffer_texture_attach_ex(fb, tex, slot, -1, mip);
+ GPU_context_active_get()->back_left->bind(false);
}
-void GPU_framebuffer_texture_layer_attach(
- GPUFrameBuffer *fb, GPUTexture *tex, int slot, int layer, int mip)
+GPUFrameBuffer *GPU_framebuffer_active_get(void)
{
- /* NOTE: We could support 1D ARRAY texture. */
- BLI_assert(GPU_texture_target(tex) == GL_TEXTURE_2D_ARRAY);
- gpu_framebuffer_texture_attach_ex(fb, tex, slot, layer, mip);
+ GPUContext *ctx = GPU_context_active_get();
+ return reinterpret_cast<GPUFrameBuffer *>(ctx ? ctx->active_fb : NULL);
}
-void GPU_framebuffer_texture_cubeface_attach(
- GPUFrameBuffer *fb, GPUTexture *tex, int slot, int face, int mip)
+/* Returns the default framebuffer. Will always exists even if it's just a dummy. */
+GPUFrameBuffer *GPU_framebuffer_back_get(void)
{
- BLI_assert(GPU_texture_cube(tex));
- gpu_framebuffer_texture_attach_ex(fb, tex, slot, face, mip);
+ GPUContext *ctx = GPU_context_active_get();
+ return reinterpret_cast<GPUFrameBuffer *>(ctx ? ctx->back_left : NULL);
}
-/* ---------- Detach ----------- */
-
-void GPU_framebuffer_texture_detach_slot(GPUFrameBuffer *fb, GPUTexture *tex, int type)
+bool GPU_framebuffer_bound(GPUFrameBuffer *gpu_fb)
{
- GPUAttachment *attachment = &fb->attachments[type];
+ return (gpu_fb == GPU_framebuffer_active_get());
+}
- if (attachment->tex != tex) {
- fprintf(stderr,
- "Warning, attempting to detach Texture %p from framebuffer %p "
- "but texture is not attached.\n",
- tex,
- fb);
- return;
- }
+/* ---------- Attachment Management ----------- */
- attachment->tex = NULL;
- GPU_FB_ATTACHEMENT_SET_DIRTY(fb->dirty_flag, type);
+bool GPU_framebuffer_check_valid(GPUFrameBuffer *gpu_fb, char err_out[256])
+{
+ return reinterpret_cast<FrameBuffer *>(gpu_fb)->check(err_out);
}
-void GPU_framebuffer_texture_detach(GPUFrameBuffer *fb, GPUTexture *tex)
+void GPU_framebuffer_texture_attach_ex(GPUFrameBuffer *gpu_fb, GPUAttachment attachement, int slot)
{
- GPUAttachmentType type = (GPUAttachmentType)GPU_texture_detach_framebuffer(tex, fb);
- GPU_framebuffer_texture_detach_slot(fb, tex, type);
+ GPUAttachmentType type = blender::gpu::Texture::attachment_type(attachement.tex, slot);
+ reinterpret_cast<FrameBuffer *>(gpu_fb)->attachment_set(type, attachement);
}
-/* ---------- Config (Attach & Detach) ----------- */
-
-/**
- * First GPUAttachment in *config is always the depth/depth_stencil buffer.
- * Following GPUAttachments are color buffers.
- * Setting GPUAttachment.mip to -1 will leave the texture in this slot.
- * Setting GPUAttachment.tex to NULL will detach the texture in this slot.
- */
-void GPU_framebuffer_config_array(GPUFrameBuffer *fb, const GPUAttachment *config, int config_len)
+void GPU_framebuffer_texture_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, int mip)
{
- if (config[0].tex) {
- BLI_assert(GPU_texture_depth(config[0].tex));
- gpu_framebuffer_texture_attach_ex(fb, config[0].tex, 0, config[0].layer, config[0].mip);
- }
- else if (config[0].mip == -1) {
- /* Leave texture attached */
- }
- else if (fb->attachments[GPU_FB_DEPTH_ATTACHMENT].tex != NULL) {
- GPU_framebuffer_texture_detach(fb, fb->attachments[GPU_FB_DEPTH_ATTACHMENT].tex);
- }
- else if (fb->attachments[GPU_FB_DEPTH_STENCIL_ATTACHMENT].tex != NULL) {
- GPU_framebuffer_texture_detach(fb, fb->attachments[GPU_FB_DEPTH_STENCIL_ATTACHMENT].tex);
- }
-
- int slot = 0;
- for (int i = 1; i < config_len; i++, slot++) {
- if (config[i].tex != NULL) {
- BLI_assert(GPU_texture_depth(config[i].tex) == false);
- gpu_framebuffer_texture_attach_ex(fb, config[i].tex, slot, config[i].layer, config[i].mip);
- }
- else if (config[i].mip != -1) {
- GPUTexture *tex = framebuffer_get_color_tex(fb, slot);
- if (tex != NULL) {
- GPU_framebuffer_texture_detach(fb, tex);
- }
- }
- }
+ GPUAttachment attachement = GPU_ATTACHMENT_TEXTURE_MIP(tex, mip);
+ GPU_framebuffer_texture_attach_ex(fb, attachement, slot);
}
-/* ---------- Bind / Restore ----------- */
-
-static void gpu_framebuffer_attachment_attach(GPUAttachment *attach, GPUAttachmentType attach_type)
+void GPU_framebuffer_texture_layer_attach(
+ GPUFrameBuffer *fb, GPUTexture *tex, int slot, int layer, int mip)
{
- int tex_bind = GPU_texture_opengl_bindcode(attach->tex);
- GLenum gl_attachment = convert_attachment_type_to_gl(attach_type);
-
- if (attach->layer > -1) {
- if (GPU_texture_cube(attach->tex)) {
- glFramebufferTexture2D(GL_FRAMEBUFFER,
- gl_attachment,
- GL_TEXTURE_CUBE_MAP_POSITIVE_X + attach->layer,
- tex_bind,
- attach->mip);
- }
- else {
- glFramebufferTextureLayer(
- GL_FRAMEBUFFER, gl_attachment, tex_bind, attach->mip, attach->layer);
- }
- }
- else {
- glFramebufferTexture(GL_FRAMEBUFFER, gl_attachment, tex_bind, attach->mip);
- }
+ GPUAttachment attachement = GPU_ATTACHMENT_TEXTURE_LAYER_MIP(tex, layer, mip);
+ GPU_framebuffer_texture_attach_ex(fb, attachement, slot);
}
-static void gpu_framebuffer_attachment_detach(GPUAttachment *UNUSED(attachment),
- GPUAttachmentType attach_type)
+void GPU_framebuffer_texture_cubeface_attach(
+ GPUFrameBuffer *fb, GPUTexture *tex, int slot, int face, int mip)
{
- GLenum gl_attachment = convert_attachment_type_to_gl(attach_type);
- glFramebufferTexture(GL_FRAMEBUFFER, gl_attachment, 0, 0);
+ GPUAttachment attachement = GPU_ATTACHMENT_TEXTURE_CUBEFACE_MIP(tex, face, mip);
+ GPU_framebuffer_texture_attach_ex(fb, attachement, slot);
}
-static void gpu_framebuffer_update_attachments(GPUFrameBuffer *fb)
+void GPU_framebuffer_texture_detach(GPUFrameBuffer *gpu_fb, GPUTexture *tex)
{
- GLenum gl_attachments[GPU_FB_MAX_COLOR_ATTACHMENT];
- int numslots = 0;
-
- BLI_assert(GPU_framebuffer_active_get() == fb);
-
- /* Update attachments */
- FOREACH_ATTACHMENT_RANGE(type, 0, GPU_FB_MAX_ATTACHEMENT)
- {
- if (type >= GPU_FB_COLOR_ATTACHMENT0) {
- if (fb->attachments[type].tex) {
- gl_attachments[numslots] = convert_attachment_type_to_gl(type);
- }
- else {
- gl_attachments[numslots] = GL_NONE;
- }
- numslots++;
- }
-
- if (GPU_FB_ATTACHEMENT_IS_DIRTY(fb->dirty_flag, type) == false) {
- continue;
- }
- if (fb->attachments[type].tex != NULL) {
- gpu_framebuffer_attachment_attach(&fb->attachments[type], type);
-
- fb->multisample = (GPU_texture_samples(fb->attachments[type].tex) > 0);
- fb->width = GPU_texture_width(fb->attachments[type].tex);
- fb->height = GPU_texture_height(fb->attachments[type].tex);
- }
- else {
- gpu_framebuffer_attachment_detach(&fb->attachments[type], type);
- }
- }
- fb->dirty_flag = 0;
-
- /* Update draw buffers (color targets)
- * This state is saved in the FBO */
- if (numslots) {
- glDrawBuffers(numslots, gl_attachments);
+ GPUAttachment attachement = GPU_ATTACHMENT_NONE;
+ int type = GPU_texture_framebuffer_attachement_get(tex, gpu_fb);
+ if (type != -1) {
+ reinterpret_cast<FrameBuffer *>(gpu_fb)->attachment_set((GPUAttachmentType)type, attachement);
}
else {
- glDrawBuffer(GL_NONE);
+ BLI_assert(!"Error: Texture: Framebuffer is not attached");
}
}
/**
- * Hack to solve the problem of some bugged AMD GPUs (see `GPU_unused_fb_slot_workaround`).
- * If there is an empty color slot between the color slots,
- * all textures after this slot are apparently skipped/discarded.
+ * First GPUAttachment in *config is always the depth/depth_stencil buffer.
+ * Following GPUAttachments are color buffers.
+ * Setting GPUAttachment.mip to -1 will leave the texture in this slot.
+ * Setting GPUAttachment.tex to NULL will detach the texture in this slot.
*/
-static void gpu_framebuffer_update_attachments_and_fill_empty_slots(GPUFrameBuffer *fb)
-{
- GLenum gl_attachments[GPU_FB_MAX_COLOR_ATTACHMENT];
- int dummy_tex = 0;
-
- BLI_assert(GPU_framebuffer_active_get() == fb);
-
- /* Update attachments */
- for (int i_type = GPU_FB_MAX_ATTACHEMENT - 1; i_type >= 0; --i_type) {
- GPUAttachmentType type = static_cast<GPUAttachmentType>(i_type);
- GPUTexture *tex = fb->attachments[type].tex;
-
- if (type >= GPU_FB_COLOR_ATTACHMENT0) {
- int slot = type - GPU_FB_COLOR_ATTACHMENT0;
- if (tex != NULL || (dummy_tex != 0)) {
- gl_attachments[slot] = convert_attachment_type_to_gl(type);
-
- if (dummy_tex == 0) {
- dummy_tex = GPU_texture_opengl_bindcode(tex);
- }
- }
- else {
- gl_attachments[slot] = GL_NONE;
- }
- }
- else {
- dummy_tex = 0;
- }
-
- if ((dummy_tex != 0) && tex == NULL) {
- /* Fill empty slot */
- glFramebufferTexture(GL_FRAMEBUFFER, convert_attachment_type_to_gl(type), dummy_tex, 0);
- }
- else if (GPU_FB_ATTACHEMENT_IS_DIRTY(fb->dirty_flag, type)) {
- if (tex != NULL) {
- gpu_framebuffer_attachment_attach(&fb->attachments[type], type);
-
- fb->multisample = (GPU_texture_samples(tex) > 0);
- fb->width = GPU_texture_width(tex);
- fb->height = GPU_texture_height(tex);
- }
- else {
- gpu_framebuffer_attachment_detach(&fb->attachments[type], type);
- }
- }
- }
- fb->dirty_flag = 0;
-
- /* Update draw buffers (color targets)
- * This state is saved in the FBO */
- glDrawBuffers(GPU_FB_MAX_COLOR_ATTACHMENT, gl_attachments);
-}
-
-#define FRAMEBUFFER_STACK_DEPTH 16
-
-static struct {
- GPUFrameBuffer *framebuffers[FRAMEBUFFER_STACK_DEPTH];
- uint top;
-} FrameBufferStack = {{0}};
-
-static void gpuPushFrameBuffer(GPUFrameBuffer *fbo)
+void GPU_framebuffer_config_array(GPUFrameBuffer *gpu_fb,
+ const GPUAttachment *config,
+ int config_len)
{
- BLI_assert(FrameBufferStack.top < FRAMEBUFFER_STACK_DEPTH);
- FrameBufferStack.framebuffers[FrameBufferStack.top] = fbo;
- FrameBufferStack.top++;
-}
-
-static GPUFrameBuffer *gpuPopFrameBuffer(void)
-{
- BLI_assert(FrameBufferStack.top > 0);
- FrameBufferStack.top--;
- return FrameBufferStack.framebuffers[FrameBufferStack.top];
-}
+ FrameBuffer *fb = reinterpret_cast<FrameBuffer *>(gpu_fb);
-#undef FRAMEBUFFER_STACK_DEPTH
+ const GPUAttachment &depth_attachement = config[0];
+ Span<GPUAttachment> color_attachments(config + 1, config_len - 1);
-void GPU_framebuffer_bind(GPUFrameBuffer *fb)
-{
- if (fb->object == 0) {
- gpu_framebuffer_init(fb);
+ if (depth_attachement.mip == -1) {
+ /* GPU_ATTACHMENT_LEAVE */
}
-
- if (GPU_framebuffer_active_get() != fb) {
- glBindFramebuffer(GL_FRAMEBUFFER, fb->object);
- glEnable(GL_FRAMEBUFFER_SRGB);
-
- GPUTexture *first_target = fb->attachments[GPU_FB_COLOR_ATTACHMENT0].tex;
- const bool is_srgb_target = (first_target &&
- (GPU_texture_format(first_target) == GPU_SRGB8_A8));
- GPU_shader_set_framebuffer_srgb_target(is_srgb_target);
+ else if (depth_attachement.tex == NULL) {
+ /* GPU_ATTACHMENT_NONE: Need to clear both targets. */
+ fb->attachment_set(GPU_FB_DEPTH_STENCIL_ATTACHMENT, depth_attachement);
+ fb->attachment_set(GPU_FB_DEPTH_ATTACHMENT, depth_attachement);
}
-
- gpu_framebuffer_current_set(fb);
-
- if (fb->dirty_flag != 0) {
- if (GPU_unused_fb_slot_workaround()) {
- /* XXX: Please AMD, fix this. */
- gpu_framebuffer_update_attachments_and_fill_empty_slots(fb);
- }
- else {
- gpu_framebuffer_update_attachments(fb);
- }
+ else {
+ GPUAttachmentType type = GPU_texture_stencil(depth_attachement.tex) ?
+ GPU_FB_DEPTH_STENCIL_ATTACHMENT :
+ GPU_FB_DEPTH_ATTACHMENT;
+ fb->attachment_set(type, depth_attachement);
}
- /* TODO manually check for errors? */
-#if 0
- char err_out[256];
- if (!GPU_framebuffer_check_valid(fb, err_out)) {
- printf("Invalid %s\n", err_out);
+ GPUAttachmentType type = GPU_FB_COLOR_ATTACHMENT0;
+ for (const GPUAttachment &attachement : color_attachments) {
+ fb->attachment_set(type, attachement);
+ ++type;
}
-#endif
-
- glViewport(0, 0, fb->width, fb->height);
}
-void GPU_framebuffer_restore(void)
+/* ---------- Viewport & Scissor Region ----------- */
+
+/* Viewport and scissor size is stored per framebuffer.
+ * It is only reset to its original dimensions explicitely OR when binding the framebuffer after
+ * modifiying its attachments. */
+void GPU_framebuffer_viewport_set(GPUFrameBuffer *gpu_fb, int x, int y, int width, int height)
{
- if (GPU_framebuffer_active_get() != NULL) {
- glBindFramebuffer(GL_FRAMEBUFFER, GPU_framebuffer_default());
- gpu_framebuffer_current_set(NULL);
- glDisable(GL_FRAMEBUFFER_SRGB);
- GPU_shader_set_framebuffer_srgb_target(false);
- }
+ int viewport_rect[4] = {x, y, width, height};
+ reinterpret_cast<FrameBuffer *>(gpu_fb)->viewport_set(viewport_rect);
}
-bool GPU_framebuffer_bound(GPUFrameBuffer *fb)
+void GPU_framebuffer_viewport_get(GPUFrameBuffer *gpu_fb, int r_viewport[4])
{
- return (fb == GPU_framebuffer_active_get()) && (fb->object != 0);
+ reinterpret_cast<FrameBuffer *>(gpu_fb)->viewport_get(r_viewport);
}
-bool GPU_framebuffer_check_valid(GPUFrameBuffer *fb, char err_out[256])
+/* Reset to its attachement(s) size. */
+void GPU_framebuffer_viewport_reset(GPUFrameBuffer *gpu_fb)
{
- if (!GPU_framebuffer_bound(fb)) {
- GPU_framebuffer_bind(fb);
- }
-
- GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
-
- if (status != GL_FRAMEBUFFER_COMPLETE) {
- GPU_framebuffer_restore();
- gpu_print_framebuffer_error(status, err_out);
- return false;
- }
-
- return true;
+ reinterpret_cast<FrameBuffer *>(gpu_fb)->viewport_reset();
}
/* ---------- Framebuffer Operations ----------- */
-#define CHECK_FRAMEBUFFER_IS_BOUND(_fb) \
- BLI_assert(GPU_framebuffer_bound(_fb)); \
- UNUSED_VARS_NDEBUG(_fb); \
- ((void)0)
-
-/* Needs to be done after binding. */
-void GPU_framebuffer_viewport_set(GPUFrameBuffer *fb, int x, int y, int w, int h)
-{
- CHECK_FRAMEBUFFER_IS_BOUND(fb);
-
- glViewport(x, y, w, h);
-}
-
-void GPU_framebuffer_clear(GPUFrameBuffer *fb,
+void GPU_framebuffer_clear(GPUFrameBuffer *gpu_fb,
eGPUFrameBufferBits buffers,
const float clear_col[4],
float clear_depth,
uint clear_stencil)
{
- CHECK_FRAMEBUFFER_IS_BOUND(fb);
-
- if (buffers & GPU_COLOR_BIT) {
- glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
- glClearColor(clear_col[0], clear_col[1], clear_col[2], clear_col[3]);
- }
- if (buffers & GPU_DEPTH_BIT) {
- glDepthMask(GL_TRUE);
- glClearDepth(clear_depth);
- }
- if (buffers & GPU_STENCIL_BIT) {
- glStencilMask(0xFF);
- glClearStencil(clear_stencil);
- }
-
- GLbitfield mask = convert_buffer_bits_to_gl(buffers);
- glClear(mask);
+ reinterpret_cast<FrameBuffer *>(gpu_fb)->clear(buffers, clear_col, clear_depth, clear_stencil);
}
-/* Clear all textures bound to this framebuffer with a different color. */
-void GPU_framebuffer_multi_clear(GPUFrameBuffer *fb, const float (*clear_cols)[4])
+/* Clear all textures attached to this framebuffer with a different color. */
+void GPU_framebuffer_multi_clear(GPUFrameBuffer *gpu_fb, const float (*clear_cols)[4])
{
- CHECK_FRAMEBUFFER_IS_BOUND(fb);
-
- glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
-
- int i_type = GPU_FB_COLOR_ATTACHMENT0;
- for (int i = 0; i_type < GPU_FB_MAX_ATTACHEMENT; i++, i_type++) {
- GPUAttachmentType type = static_cast<GPUAttachmentType>(i_type);
- if (fb->attachments[type].tex != NULL) {
- glClearBufferfv(GL_COLOR, i, clear_cols[i]);
- }
- }
-}
-
-void GPU_framebuffer_read_depth(GPUFrameBuffer *fb, int x, int y, int w, int h, float *data)
-{
- CHECK_FRAMEBUFFER_IS_BOUND(fb);
-
- GLenum type = GL_DEPTH_COMPONENT;
- glReadBuffer(GL_COLOR_ATTACHMENT0); /* This is OK! */
- glReadPixels(x, y, w, h, type, GL_FLOAT, data);
+ reinterpret_cast<FrameBuffer *>(gpu_fb)->clear_multi(clear_cols);
}
-static GLenum gpu_get_gl_datatype(eGPUDataFormat format)
+void GPU_clear_color(float red, float green, float blue, float alpha)
{
- switch (format) {
- case GPU_DATA_FLOAT:
- return GL_FLOAT;
- case GPU_DATA_INT:
- return GL_INT;
- case GPU_DATA_UNSIGNED_INT:
- return GL_UNSIGNED_INT;
- case GPU_DATA_UNSIGNED_BYTE:
- return GL_UNSIGNED_BYTE;
- case GPU_DATA_UNSIGNED_INT_24_8:
- return GL_UNSIGNED_INT_24_8;
- case GPU_DATA_10_11_11_REV:
- return GL_UNSIGNED_INT_10F_11F_11F_REV;
- default:
- BLI_assert(!"Unhandled data format");
- return GL_FLOAT;
- }
+ float clear_col[4] = {red, green, blue, alpha};
+ GPU_context_active_get()->active_fb->clear(GPU_COLOR_BIT, clear_col, 0.0f, 0x0);
}
-static GLenum gpu_get_gl_channel_type(int channels)
+void GPU_clear_depth(float depth)
{
- switch (channels) {
- case 1:
- return GL_RED;
- case 2:
- return GL_RG;
- case 3:
- return GL_RGB;
- case 4:
- return GL_RGBA;
- default:
- BLI_assert(!"Wrong number of read channels");
- return GL_RED;
- }
+ float clear_col[4] = {0};
+ GPU_context_active_get()->active_fb->clear(GPU_DEPTH_BIT, clear_col, depth, 0x0);
}
-static void gpu_framebuffer_read_color_ex(
- int x, int y, int w, int h, int channels, GLenum readfb, eGPUDataFormat format, float *data)
+void GPU_framebuffer_read_depth(GPUFrameBuffer *gpu_fb, int x, int y, int w, int h, float *data)
{
- GLenum type = gpu_get_gl_channel_type(channels);
- GLenum gl_format = gpu_get_gl_datatype(format);
- /* TODO: needed for selection buffers to work properly, this should be handled better. */
- if (type == GL_RED && gl_format == GL_UNSIGNED_INT) {
- type = GL_RED_INTEGER;
- }
- glReadBuffer(readfb);
- glReadPixels(x, y, w, h, type, gl_format, data);
+ int rect[4] = {x, y, w, h};
+ reinterpret_cast<FrameBuffer *>(gpu_fb)->read(GPU_DEPTH_BIT, GPU_DATA_FLOAT, rect, 1, 1, data);
}
-void GPU_framebuffer_read_color(GPUFrameBuffer *fb,
+void GPU_framebuffer_read_color(GPUFrameBuffer *gpu_fb,
int x,
int y,
int w,
@@ -713,90 +428,62 @@ void GPU_framebuffer_read_color(GPUFrameBuffer *fb,
eGPUDataFormat format,
void *data)
{
- CHECK_FRAMEBUFFER_IS_BOUND(fb);
- gpu_framebuffer_read_color_ex(
- x, y, w, h, channels, GL_COLOR_ATTACHMENT0 + slot, format, (float *)data);
+ int rect[4] = {x, y, w, h};
+ reinterpret_cast<FrameBuffer *>(gpu_fb)->read(GPU_COLOR_BIT, format, rect, channels, slot, data);
+}
+
+/* TODO(fclem) rename to read_color. */
+void GPU_frontbuffer_read_pixels(
+ int x, int y, int w, int h, int channels, eGPUDataFormat format, void *data)
+{
+ int rect[4] = {x, y, w, h};
+ GPU_context_active_get()->front_left->read(GPU_COLOR_BIT, format, rect, channels, 0, data);
}
/* read_slot and write_slot are only used for color buffers. */
-void GPU_framebuffer_blit(GPUFrameBuffer *fb_read,
+/* TODO(fclem) port as texture operation. */
+void GPU_framebuffer_blit(GPUFrameBuffer *gpufb_read,
int read_slot,
- GPUFrameBuffer *fb_write,
+ GPUFrameBuffer *gpufb_write,
int write_slot,
eGPUFrameBufferBits blit_buffers)
{
+ FrameBuffer *fb_read = reinterpret_cast<FrameBuffer *>(gpufb_read);
+ FrameBuffer *fb_write = reinterpret_cast<FrameBuffer *>(gpufb_write);
BLI_assert(blit_buffers != 0);
- GPUFrameBuffer *prev_fb = GPU_framebuffer_active_get();
+ FrameBuffer *prev_fb = GPU_context_active_get()->active_fb;
- /* Framebuffers must be up to date. This simplify this function. */
- if (fb_read->dirty_flag != 0 || fb_read->object == 0) {
- GPU_framebuffer_bind(fb_read);
+#ifndef NDEBUG
+ GPUTexture *read_tex, *write_tex;
+ if (blit_buffers & (GPU_DEPTH_BIT | GPU_STENCIL_BIT)) {
+ read_tex = fb_read->depth_tex();
+ write_tex = fb_write->depth_tex();
}
- if (fb_write->dirty_flag != 0 || fb_write->object == 0) {
- GPU_framebuffer_bind(fb_write);
+ else {
+ read_tex = fb_read->color_tex(read_slot);
+ write_tex = fb_write->color_tex(write_slot);
}
- const bool do_color = (blit_buffers & GPU_COLOR_BIT);
- const bool do_depth = (blit_buffers & GPU_DEPTH_BIT);
- const bool do_stencil = (blit_buffers & GPU_STENCIL_BIT);
-
- GPUTexture *read_tex = ((do_depth || do_stencil) ?
- framebuffer_get_depth_tex(fb_read) :
- framebuffer_get_color_tex(fb_read, read_slot));
- GPUTexture *write_tex = ((do_depth || do_stencil) ?
- framebuffer_get_depth_tex(fb_write) :
- framebuffer_get_color_tex(fb_write, read_slot));
-
- if (do_depth) {
+ if (blit_buffers & GPU_DEPTH_BIT) {
BLI_assert(GPU_texture_depth(read_tex) && GPU_texture_depth(write_tex));
BLI_assert(GPU_texture_format(read_tex) == GPU_texture_format(write_tex));
}
- if (do_stencil) {
+ if (blit_buffers & GPU_STENCIL_BIT) {
BLI_assert(GPU_texture_stencil(read_tex) && GPU_texture_stencil(write_tex));
BLI_assert(GPU_texture_format(read_tex) == GPU_texture_format(write_tex));
}
if (GPU_texture_samples(write_tex) != 0 || GPU_texture_samples(read_tex) != 0) {
/* Can only blit multisample textures to another texture of the same size. */
- BLI_assert((fb_read->width == fb_write->width) && (fb_read->height == fb_write->height));
+ BLI_assert((GPU_texture_width(write_tex) == GPU_texture_width(read_tex)) &&
+ (GPU_texture_height(write_tex) == GPU_texture_height(read_tex)));
}
+#endif
- glBindFramebuffer(GL_READ_FRAMEBUFFER, fb_read->object);
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb_write->object);
+ fb_read->blit_to(blit_buffers, read_slot, fb_write, write_slot, 0, 0);
- if (do_color) {
- glReadBuffer(GL_COLOR_ATTACHMENT0 + read_slot);
- glDrawBuffer(GL_COLOR_ATTACHMENT0 + write_slot);
- /* XXX we messed with the glDrawBuffer, this will reset the
- * glDrawBuffers the next time we bind fb_write. */
- fb_write->dirty_flag = GPU_FB_DIRTY_DRAWBUFFER;
- }
-
- GLbitfield mask = convert_buffer_bits_to_gl(blit_buffers);
-
- glBlitFramebuffer(0,
- 0,
- fb_read->width,
- fb_read->height,
- 0,
- 0,
- fb_write->width,
- fb_write->height,
- mask,
- GL_NEAREST);
-
- /* Restore previous framebuffer */
- if (fb_write == prev_fb) {
- GPU_framebuffer_bind(fb_write); /* To update drawbuffers */
- }
- else if (prev_fb) {
- glBindFramebuffer(GL_FRAMEBUFFER, prev_fb->object);
- gpu_framebuffer_current_set(prev_fb);
- }
- else {
- glBindFramebuffer(GL_FRAMEBUFFER, GPU_framebuffer_default());
- gpu_framebuffer_current_set(NULL);
- }
+ /* FIXME(fclem) sRGB is not saved. */
+ prev_fb->bind(true);
}
/**
@@ -804,80 +491,45 @@ void GPU_framebuffer_blit(GPUFrameBuffer *fb_read,
* input. This function only takes care of the correct texture handling. It execute the callback
* for each texture level.
*/
-void GPU_framebuffer_recursive_downsample(GPUFrameBuffer *fb,
+void GPU_framebuffer_recursive_downsample(GPUFrameBuffer *gpu_fb,
int max_lvl,
void (*callback)(void *userData, int level),
void *userData)
{
- /* Framebuffer must be up to date and bound. This simplify this function. */
- if (GPU_framebuffer_active_get() != fb || fb->dirty_flag != 0 || fb->object == 0) {
- GPU_framebuffer_bind(fb);
- }
- /* HACK: We make the framebuffer appear not bound in order to
- * not trigger any error in GPU_texture_bind(). */
- GPUFrameBuffer *prev_fb = GPU_framebuffer_active_get();
- gpu_framebuffer_current_set(NULL);
-
- int levels = floor(log2(max_ii(fb->width, fb->height)));
- max_lvl = min_ii(max_lvl, levels);
+ reinterpret_cast<FrameBuffer *>(gpu_fb)->recursive_downsample(max_lvl, callback, userData);
+}
- int i;
- int current_dim[2] = {fb->width, fb->height};
- for (i = 1; i < max_lvl + 1; i++) {
- /* calculate next viewport size */
- current_dim[0] = max_ii(current_dim[0] / 2, 1);
- current_dim[1] = max_ii(current_dim[1] / 2, 1);
+/** \} */
- for (int i_type = 0; i_type < GPU_FB_MAX_ATTACHEMENT; i_type++) {
- GPUAttachmentType type = static_cast<GPUAttachmentType>(i_type);
- if (fb->attachments[type].tex != NULL) {
- /* Some Intel HDXXX have issue with rendering to a mipmap that is below
- * the texture GL_TEXTURE_MAX_LEVEL. So even if it not correct, in this case
- * we allow GL_TEXTURE_MAX_LEVEL to be one level lower. In practice it does work! */
- int next_lvl = (GPU_mip_render_workaround()) ? i : i - 1;
- /* bind next level for rendering but first restrict fetches only to previous level */
- GPUTexture *tex = fb->attachments[type].tex;
- GPU_texture_bind(tex, 0);
- glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_BASE_LEVEL, i - 1);
- glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_MAX_LEVEL, next_lvl);
- GPU_texture_unbind(tex);
- /* copy attachment and replace miplevel. */
- GPUAttachment attachment = fb->attachments[type];
- attachment.mip = i;
- gpu_framebuffer_attachment_attach(&attachment, type);
- }
- }
-
- BLI_assert(GL_FRAMEBUFFER_COMPLETE == glCheckFramebufferStatus(GL_FRAMEBUFFER));
+/* -------------------------------------------------------------------- */
+/** \name GPUOffScreen
+ *
+ * Container that holds a framebuffer and its textures.
+ * Might be bound to multiple contexts.
+ * \{ */
- glViewport(0, 0, current_dim[0], current_dim[1]);
- callback(userData, i);
+#define FRAMEBUFFER_STACK_DEPTH 16
- if (current_dim[0] == 1 && current_dim[1] == 1) {
- break;
- }
- }
+static struct {
+ GPUFrameBuffer *framebuffers[FRAMEBUFFER_STACK_DEPTH];
+ uint top;
+} FrameBufferStack = {{0}};
- for (int i_type = 0; i_type < GPU_FB_MAX_ATTACHEMENT; i_type++) {
- GPUAttachmentType type = static_cast<GPUAttachmentType>(i_type);
- if (fb->attachments[type].tex != NULL) {
- /* reset mipmap level range */
- GPUTexture *tex = fb->attachments[type].tex;
- GPU_texture_bind(tex, 0);
- glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_BASE_LEVEL, 0);
- glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_MAX_LEVEL, i - 1);
- GPU_texture_unbind(tex);
- /* Reattach original level */
- /* NOTE: This is not necessary but this makes the FBO config
- * remain in sync with the GPUFrameBuffer config. */
- gpu_framebuffer_attachment_attach(&fb->attachments[type], type);
- }
- }
+static void gpuPushFrameBuffer(GPUFrameBuffer *fb)
+{
+ BLI_assert(FrameBufferStack.top < FRAMEBUFFER_STACK_DEPTH);
+ FrameBufferStack.framebuffers[FrameBufferStack.top] = fb;
+ FrameBufferStack.top++;
+}
- gpu_framebuffer_current_set(prev_fb);
+static GPUFrameBuffer *gpuPopFrameBuffer(void)
+{
+ BLI_assert(FrameBufferStack.top > 0);
+ FrameBufferStack.top--;
+ return FrameBufferStack.framebuffers[FrameBufferStack.top];
}
-/* GPUOffScreen */
+#undef FRAMEBUFFER_STACK_DEPTH
#define MAX_CTX_FB_LEN 3
@@ -952,21 +604,14 @@ GPUOffScreen *GPU_offscreen_create(
return NULL;
}
- gpuPushAttr(GPU_VIEWPORT_BIT);
-
GPUFrameBuffer *fb = gpu_offscreen_fb_get(ofs);
/* check validity at the very end! */
if (!GPU_framebuffer_check_valid(fb, err_out)) {
GPU_offscreen_free(ofs);
- gpuPopAttr();
return NULL;
}
-
GPU_framebuffer_restore();
-
- gpuPopAttr();
-
return ofs;
}
@@ -990,23 +635,16 @@ void GPU_offscreen_free(GPUOffScreen *ofs)
void GPU_offscreen_bind(GPUOffScreen *ofs, bool save)
{
if (save) {
- gpuPushAttr((eGPUAttrMask)(GPU_SCISSOR_BIT | GPU_VIEWPORT_BIT));
GPUFrameBuffer *fb = GPU_framebuffer_active_get();
- gpuPushFrameBuffer(fb);
+ gpuPushFrameBuffer(reinterpret_cast<GPUFrameBuffer *>(fb));
}
- GPUFrameBuffer *ofs_fb = gpu_offscreen_fb_get(ofs);
- GPU_framebuffer_bind(ofs_fb);
- glDisable(GL_FRAMEBUFFER_SRGB);
- GPU_scissor_test(false);
- GPU_shader_set_framebuffer_srgb_target(false);
+ reinterpret_cast<FrameBuffer *>(gpu_offscreen_fb_get(ofs))->bind(false);
}
void GPU_offscreen_unbind(GPUOffScreen *UNUSED(ofs), bool restore)
{
GPUFrameBuffer *fb = NULL;
-
if (restore) {
- gpuPopAttr();
fb = gpuPopFrameBuffer();
}
@@ -1020,33 +658,20 @@ void GPU_offscreen_unbind(GPUOffScreen *UNUSED(ofs), bool restore)
void GPU_offscreen_draw_to_screen(GPUOffScreen *ofs, int x, int y)
{
- const int w = GPU_texture_width(ofs->color);
- const int h = GPU_texture_height(ofs->color);
-
- GPUFrameBuffer *ofs_fb = gpu_offscreen_fb_get(ofs);
-
- glBindFramebuffer(GL_READ_FRAMEBUFFER, ofs_fb->object);
- GLenum status = glCheckFramebufferStatus(GL_READ_FRAMEBUFFER);
-
- if (status == GL_FRAMEBUFFER_COMPLETE) {
- glBlitFramebuffer(0, 0, w, h, x, y, x + w, y + h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
- }
- else {
- gpu_print_framebuffer_error(status, NULL);
- }
-
- glBindFramebuffer(GL_READ_FRAMEBUFFER, GPU_framebuffer_default());
+ GPUContext *ctx = GPU_context_active_get();
+ FrameBuffer *ofs_fb = reinterpret_cast<FrameBuffer *>(gpu_offscreen_fb_get(ofs));
+ ofs_fb->blit_to(GPU_COLOR_BIT, 0, ctx->active_fb, 0, x, y);
}
-void GPU_offscreen_read_pixels(GPUOffScreen *ofs, eGPUDataFormat type, void *pixels)
+void GPU_offscreen_read_pixels(GPUOffScreen *ofs, eGPUDataFormat format, void *pixels)
{
+ BLI_assert(ELEM(format, GPU_DATA_UNSIGNED_BYTE, GPU_DATA_FLOAT));
+
const int w = GPU_texture_width(ofs->color);
const int h = GPU_texture_height(ofs->color);
- BLI_assert(ELEM(type, GPU_DATA_UNSIGNED_BYTE, GPU_DATA_FLOAT));
- GLenum gl_type = (type == GPU_DATA_FLOAT) ? GL_FLOAT : GL_UNSIGNED_BYTE;
-
- glReadPixels(0, 0, w, h, GL_RGBA, gl_type, pixels);
+ GPUFrameBuffer *ofs_fb = gpu_offscreen_fb_get(ofs);
+ GPU_framebuffer_read_color(ofs_fb, 0, 0, w, h, 4, 0, format, pixels);
}
int GPU_offscreen_width(const GPUOffScreen *ofs)
@@ -1075,37 +700,4 @@ void GPU_offscreen_viewport_data_get(GPUOffScreen *ofs,
*r_depth = ofs->depth;
}
-void GPU_clear_color(float red, float green, float blue, float alpha)
-{
- glClearColor(red, green, blue, alpha);
-}
-
-void GPU_clear_depth(float depth)
-{
- glClearDepth(depth);
-}
-
-void GPU_clear(eGPUFrameBufferBits flags)
-{
- glClear(convert_buffer_bits_to_gl(flags));
-}
-
-void GPU_frontbuffer_read_pixels(
- int x, int y, int w, int h, int channels, eGPUDataFormat format, void *data)
-{
- gpu_framebuffer_read_color_ex(x, y, w, h, channels, GL_FRONT, format, (float *)data);
-}
-
-/* For stereo rendering. */
-void GPU_backbuffer_bind(eGPUBackBuffer buffer)
-{
- if (buffer == GPU_BACKBUFFER) {
- glDrawBuffer(GL_BACK);
- }
- else if (buffer == GPU_BACKBUFFER_LEFT) {
- glDrawBuffer(GL_BACK_LEFT);
- }
- else if (buffer == GPU_BACKBUFFER_RIGHT) {
- glDrawBuffer(GL_BACK_RIGHT);
- }
-}
+/** \} */ \ No newline at end of file
diff --git a/source/blender/gpu/intern/gpu_framebuffer_private.hh b/source/blender/gpu/intern/gpu_framebuffer_private.hh
new file mode 100644
index 00000000000..3fba0c8de92
--- /dev/null
+++ b/source/blender/gpu/intern/gpu_framebuffer_private.hh
@@ -0,0 +1,211 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup gpu
+ *
+ * GPU Framebuffer
+ * - this is a wrapper for an OpenGL framebuffer object (FBO). in practice
+ * multiple FBO's may be created.
+ * - actual FBO creation & config is deferred until GPU_framebuffer_bind or
+ * GPU_framebuffer_check_valid to allow creation & config while another
+ * opengl context is bound (since FBOs are not shared between ogl contexts).
+ */
+
+#pragma once
+
+#include "BLI_math_vector.h"
+#include "BLI_span.hh"
+
+#include "MEM_guardedalloc.h"
+
+#include "GPU_framebuffer.h"
+
+struct GPUTexture;
+
+typedef enum GPUAttachmentType : int {
+ GPU_FB_DEPTH_ATTACHMENT = 0,
+ GPU_FB_DEPTH_STENCIL_ATTACHMENT,
+ GPU_FB_COLOR_ATTACHMENT0,
+ GPU_FB_COLOR_ATTACHMENT1,
+ GPU_FB_COLOR_ATTACHMENT2,
+ GPU_FB_COLOR_ATTACHMENT3,
+ GPU_FB_COLOR_ATTACHMENT4,
+ GPU_FB_COLOR_ATTACHMENT5,
+ /* Number of maximum output slots.
+ * We support 6 outputs for now (usually we wouldn't need more to preserve fill rate). */
+ /* Keep in mind that GL max is GL_MAX_DRAW_BUFFERS and is at least 8, corresponding to
+ * the maximum number of COLOR attachments specified by glDrawBuffers. */
+ GPU_FB_MAX_ATTACHEMENT,
+
+ GPU_FB_MAX_COLOR_ATTACHMENT = (GPU_FB_MAX_ATTACHEMENT - GPU_FB_COLOR_ATTACHMENT0),
+} GPUAttachmentType;
+
+inline constexpr GPUAttachmentType operator-(GPUAttachmentType a, int b)
+{
+ return static_cast<GPUAttachmentType>(static_cast<int>(a) - b);
+}
+
+inline constexpr GPUAttachmentType operator+(GPUAttachmentType a, int b)
+{
+ return static_cast<GPUAttachmentType>(static_cast<int>(a) + b);
+}
+
+inline GPUAttachmentType &operator++(GPUAttachmentType &a)
+{
+ a = a + 1;
+ return a;
+}
+
+inline GPUAttachmentType &operator--(GPUAttachmentType &a)
+{
+ a = a - 1;
+ return a;
+}
+
+namespace blender {
+namespace gpu {
+
+#ifdef DEBUG
+# define DEBUG_NAME_LEN 64
+#else
+# define DEBUG_NAME_LEN 16
+#endif
+
+class FrameBuffer {
+ protected:
+ /** Set of texture attachements to render to. DEPTH and DEPTH_STENCIL are mutualy exclusive. */
+ GPUAttachment attachments_[GPU_FB_MAX_ATTACHEMENT];
+ /** Is true if internal representation need to be updated. */
+ bool dirty_attachments_;
+ /** Size of attachement textures. */
+ int width_, height_;
+ /** Debug name. */
+ char name_[DEBUG_NAME_LEN];
+ /** Framebuffer state. */
+ int viewport_[4];
+ int scissor_[4];
+ bool scissor_test_ = false;
+ bool dirty_state_;
+
+ public:
+ FrameBuffer(const char *name);
+ virtual ~FrameBuffer();
+
+ virtual void bind(bool enabled_srgb) = 0;
+ virtual bool check(char err_out[256]) = 0;
+ virtual void clear(eGPUFrameBufferBits buffers,
+ const float clear_col[4],
+ float clear_depth,
+ uint clear_stencil) = 0;
+ virtual void clear_multi(const float (*clear_col)[4]) = 0;
+
+ virtual void read(eGPUFrameBufferBits planes,
+ eGPUDataFormat format,
+ const int area[4],
+ int channel_len,
+ int slot,
+ void *r_data) = 0;
+
+ virtual void blit_to(eGPUFrameBufferBits planes,
+ int src_slot,
+ FrameBuffer *dst,
+ int dst_slot,
+ int dst_offset_x,
+ int dst_offset_y) = 0;
+
+ void attachment_set(GPUAttachmentType type, const GPUAttachment &new_attachment);
+
+ void recursive_downsample(int max_lvl,
+ void (*callback)(void *userData, int level),
+ void *userData);
+
+ inline void size_set(int width, int height)
+ {
+ width_ = width;
+ height_ = height;
+ dirty_state_ = true;
+ }
+
+ inline void viewport_set(const int viewport[4])
+ {
+ if (!equals_v4v4_int(viewport_, viewport)) {
+ copy_v4_v4_int(viewport_, viewport);
+ dirty_state_ = true;
+ }
+ }
+
+ inline void scissor_set(const int scissor[4])
+ {
+ if (!equals_v4v4_int(scissor_, scissor)) {
+ copy_v4_v4_int(scissor_, scissor);
+ dirty_state_ = true;
+ }
+ }
+
+ inline void scissor_test_set(bool test)
+ {
+ scissor_test_ = test;
+ }
+
+ inline void viewport_get(int r_viewport[4]) const
+ {
+ copy_v4_v4_int(r_viewport, viewport_);
+ }
+
+ inline void scissor_get(int r_scissor[4]) const
+ {
+ copy_v4_v4_int(r_scissor, scissor_);
+ }
+
+ inline bool scissor_test_get(void) const
+ {
+ return scissor_test_;
+ }
+
+ inline void viewport_reset(void)
+ {
+ int viewport_rect[4] = {0, 0, width_, height_};
+ viewport_set(viewport_rect);
+ }
+
+ inline void scissor_reset(void)
+ {
+ int scissor_rect[4] = {0, 0, width_, height_};
+ scissor_set(scissor_rect);
+ }
+
+ inline GPUTexture *depth_tex(void) const
+ {
+ if (attachments_[GPU_FB_DEPTH_ATTACHMENT].tex) {
+ return attachments_[GPU_FB_DEPTH_ATTACHMENT].tex;
+ }
+ return attachments_[GPU_FB_DEPTH_STENCIL_ATTACHMENT].tex;
+ };
+
+ inline GPUTexture *color_tex(int slot) const
+ {
+ return attachments_[GPU_FB_COLOR_ATTACHMENT0 + slot].tex;
+ };
+};
+
+#undef DEBUG_NAME_LEN
+
+} // namespace gpu
+} // namespace blender
diff --git a/source/blender/gpu/intern/gpu_immediate.cc b/source/blender/gpu/intern/gpu_immediate.cc
index 9cededa54f7..c5dd84ddbd0 100644
--- a/source/blender/gpu/intern/gpu_immediate.cc
+++ b/source/blender/gpu/intern/gpu_immediate.cc
@@ -20,147 +20,66 @@
/** \file
* \ingroup gpu
*
- * GPU immediate mode work-alike
+ * Mimics old style opengl immediate mode drawing.
*/
#ifndef GPU_STANDALONE
# include "UI_resources.h"
#endif
-#include "GPU_attr_binding.h"
#include "GPU_immediate.h"
#include "GPU_matrix.h"
#include "GPU_texture.h"
-#include "gpu_attr_binding_private.h"
#include "gpu_context_private.hh"
-#include "gpu_primitive_private.h"
-#include "gpu_shader_private.h"
+#include "gpu_immediate_private.hh"
+#include "gpu_shader_private.hh"
#include "gpu_vertex_format_private.h"
-#include <stdlib.h>
-#include <string.h>
+using namespace blender::gpu;
-typedef struct ImmediateDrawBuffer {
- GLuint vbo_id;
- GLubyte *buffer_data;
- uint buffer_offset;
- uint buffer_size;
-} ImmediateDrawBuffer;
-
-typedef struct {
- /* TODO: organize this struct by frequency of change (run-time) */
-
- GPUBatch *batch;
- GPUContext *context;
-
- /* current draw call */
- bool strict_vertex_len;
- uint vertex_len;
- uint buffer_bytes_mapped;
- ImmediateDrawBuffer *active_buffer;
- GPUPrimType prim_type;
- GPUVertFormat vertex_format;
- ImmediateDrawBuffer draw_buffer;
- ImmediateDrawBuffer draw_buffer_strict;
-
- /* current vertex */
- uint vertex_idx;
- GLubyte *vertex_data;
- uint16_t
- unassigned_attr_bits; /* which attributes of current vertex have not been given values? */
-
- 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;
-
-/* size of internal buffer */
-#define DEFAULT_INTERNAL_BUFFER_SIZE (4 * 1024 * 1024)
-
-static bool initialized = false;
-static Immediate imm;
+static Immediate *imm = NULL;
void immInit(void)
{
-#if TRUST_NO_ONE
- assert(!initialized);
-#endif
- memset(&imm, 0, sizeof(Immediate));
-
- imm.draw_buffer.vbo_id = GPU_buf_alloc();
- imm.draw_buffer.buffer_size = DEFAULT_INTERNAL_BUFFER_SIZE;
- glBindBuffer(GL_ARRAY_BUFFER, imm.draw_buffer.vbo_id);
- glBufferData(GL_ARRAY_BUFFER, imm.draw_buffer.buffer_size, NULL, GL_DYNAMIC_DRAW);
- imm.draw_buffer_strict.vbo_id = GPU_buf_alloc();
- imm.draw_buffer_strict.buffer_size = DEFAULT_INTERNAL_BUFFER_SIZE;
- glBindBuffer(GL_ARRAY_BUFFER, imm.draw_buffer_strict.vbo_id);
- glBufferData(GL_ARRAY_BUFFER, imm.draw_buffer_strict.buffer_size, NULL, GL_DYNAMIC_DRAW);
-
- imm.prim_type = GPU_PRIM_NONE;
- imm.strict_vertex_len = true;
-
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- initialized = true;
+ /* TODO Remove */
}
void immActivate(void)
{
-#if TRUST_NO_ONE
- assert(initialized);
- assert(imm.prim_type == GPU_PRIM_NONE); /* make sure we're not between a Begin/End pair */
- assert(imm.vao_id == 0);
-#endif
- imm.vao_id = GPU_vao_alloc();
- imm.context = GPU_context_active_get();
+ imm = GPU_context_active_get()->imm;
}
void immDeactivate(void)
{
-#if TRUST_NO_ONE
- assert(initialized);
- assert(imm.prim_type == GPU_PRIM_NONE); /* make sure we're not between a Begin/End pair */
- assert(imm.vao_id != 0);
-#endif
- GPU_vao_free(imm.vao_id, imm.context);
- imm.vao_id = 0;
- imm.prev_enabled_attr_bits = 0;
+ imm = NULL;
}
void immDestroy(void)
{
- GPU_buf_free(imm.draw_buffer.vbo_id);
- GPU_buf_free(imm.draw_buffer_strict.vbo_id);
- initialized = false;
+ /* TODO Remove */
}
GPUVertFormat *immVertexFormat(void)
{
- GPU_vertformat_clear(&imm.vertex_format);
- return &imm.vertex_format;
+ GPU_vertformat_clear(&imm->vertex_format);
+ return &imm->vertex_format;
}
void immBindShader(GPUShader *shader)
{
-#if TRUST_NO_ONE
- assert(imm.bound_program == NULL);
- assert(glIsProgram(shader->program));
-#endif
+ BLI_assert(imm->shader == NULL);
- imm.bound_program = shader;
- imm.shader_interface = shader->interface;
+ imm->shader = shader;
- if (!imm.vertex_format.packed) {
- VertexFormat_pack(&imm.vertex_format);
+ if (!imm->vertex_format.packed) {
+ VertexFormat_pack(&imm->vertex_format);
+ imm->enabled_attr_bits = 0xFFFFu & ~(0xFFFFu << imm->vertex_format.attr_len);
}
GPU_shader_bind(shader);
- get_attr_locations(&imm.vertex_format, &imm.attr_binding, imm.shader_interface);
- GPU_matrix_bind(imm.shader_interface);
- GPU_shader_set_srgb_uniform(imm.shader_interface);
+ GPU_matrix_bind(shader);
+ GPU_shader_set_srgb_uniform(shader);
}
void immBindBuiltinProgram(eGPUBuiltinShader shader_id)
@@ -171,22 +90,19 @@ void immBindBuiltinProgram(eGPUBuiltinShader shader_id)
void immUnbindProgram(void)
{
-#if TRUST_NO_ONE
- assert(imm.bound_program != NULL);
-#endif
-#if PROGRAM_NO_OPTI
- glUseProgram(0);
-#endif
- imm.bound_program = NULL;
+ BLI_assert(imm->shader != NULL);
+
+ GPU_shader_unbind();
+ imm->shader = NULL;
}
/* XXX do not use it. Special hack to use OCIO with batch API. */
GPUShader *immGetShader(void)
{
- return imm.bound_program;
+ return imm->shader;
}
-#if TRUST_NO_ONE
+#ifndef NDEBUG
static bool vertex_count_makes_sense_for_primitive(uint vertex_len, GPUPrimType prim_type)
{
/* does vertex_len make sense for this primitive type? */
@@ -217,285 +133,122 @@ static bool vertex_count_makes_sense_for_primitive(uint vertex_len, GPUPrimType
void immBegin(GPUPrimType prim_type, uint vertex_len)
{
-#if TRUST_NO_ONE
- assert(initialized);
- assert(imm.prim_type == GPU_PRIM_NONE); /* make sure we haven't already begun */
- assert(vertex_count_makes_sense_for_primitive(vertex_len, prim_type));
- assert(imm.active_buffer == NULL);
-#endif
- imm.prim_type = prim_type;
- imm.vertex_len = vertex_len;
- imm.vertex_idx = 0;
- imm.unassigned_attr_bits = imm.attr_binding.enabled_bits;
-
- /* how many bytes do we need for this draw call? */
- const uint bytes_needed = vertex_buffer_size(&imm.vertex_format, vertex_len);
- ImmediateDrawBuffer *active_buffer = imm.strict_vertex_len ? &imm.draw_buffer_strict :
- &imm.draw_buffer;
- imm.active_buffer = active_buffer;
-
- glBindBuffer(GL_ARRAY_BUFFER, active_buffer->vbo_id);
-
- /* does the current buffer have enough room? */
- const uint available_bytes = active_buffer->buffer_size - active_buffer->buffer_offset;
-
- bool recreate_buffer = false;
- if (bytes_needed > active_buffer->buffer_size) {
- /* expand the internal buffer */
- active_buffer->buffer_size = bytes_needed;
- recreate_buffer = true;
- }
- else if (bytes_needed < DEFAULT_INTERNAL_BUFFER_SIZE &&
- active_buffer->buffer_size > DEFAULT_INTERNAL_BUFFER_SIZE) {
- /* shrink the internal buffer */
- active_buffer->buffer_size = DEFAULT_INTERNAL_BUFFER_SIZE;
- recreate_buffer = true;
- }
-
- /* ensure vertex data is aligned */
- /* Might waste a little space, but it's safe. */
- const uint pre_padding = padding(active_buffer->buffer_offset, imm.vertex_format.stride);
-
- if (!recreate_buffer && ((bytes_needed + pre_padding) <= available_bytes)) {
- active_buffer->buffer_offset += pre_padding;
- }
- else {
- /* orphan this buffer & start with a fresh one */
- /* this method works on all platforms, old & new */
- glBufferData(GL_ARRAY_BUFFER, active_buffer->buffer_size, NULL, GL_DYNAMIC_DRAW);
-
- active_buffer->buffer_offset = 0;
- }
-
- /* printf("mapping %u to %u\n", imm.buffer_offset, imm.buffer_offset + bytes_needed - 1); */
-
-#if TRUST_NO_ONE
- {
- GLint bufsize;
- glGetBufferParameteriv(GL_ARRAY_BUFFER, GL_BUFFER_SIZE, &bufsize);
- assert(active_buffer->buffer_offset + bytes_needed <= bufsize);
- }
-#endif
-
- active_buffer->buffer_data = (GLubyte *)glMapBufferRange(
- GL_ARRAY_BUFFER,
- active_buffer->buffer_offset,
- bytes_needed,
- GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT |
- (imm.strict_vertex_len ? 0 : GL_MAP_FLUSH_EXPLICIT_BIT));
+ BLI_assert(imm->prim_type == GPU_PRIM_NONE); /* Make sure we haven't already begun. */
+ BLI_assert(vertex_count_makes_sense_for_primitive(vertex_len, prim_type));
-#if TRUST_NO_ONE
- assert(active_buffer->buffer_data != NULL);
-#endif
+ imm->prim_type = prim_type;
+ imm->vertex_len = vertex_len;
+ imm->vertex_idx = 0;
+ imm->unassigned_attr_bits = imm->enabled_attr_bits;
- imm.buffer_bytes_mapped = bytes_needed;
- imm.vertex_data = active_buffer->buffer_data;
+ imm->vertex_data = imm->begin();
}
void immBeginAtMost(GPUPrimType prim_type, uint vertex_len)
{
-#if TRUST_NO_ONE
- assert(vertex_len > 0);
-#endif
-
- imm.strict_vertex_len = false;
+ BLI_assert(vertex_len > 0);
+ imm->strict_vertex_len = false;
immBegin(prim_type, vertex_len);
}
GPUBatch *immBeginBatch(GPUPrimType prim_type, uint vertex_len)
{
-#if TRUST_NO_ONE
- assert(initialized);
- assert(imm.prim_type == GPU_PRIM_NONE); /* make sure we haven't already begun */
- assert(vertex_count_makes_sense_for_primitive(vertex_len, prim_type));
-#endif
- imm.prim_type = prim_type;
- imm.vertex_len = vertex_len;
- imm.vertex_idx = 0;
- imm.unassigned_attr_bits = imm.attr_binding.enabled_bits;
+ BLI_assert(imm->prim_type == GPU_PRIM_NONE); /* Make sure we haven't already begun. */
+ BLI_assert(vertex_count_makes_sense_for_primitive(vertex_len, prim_type));
+
+ imm->prim_type = prim_type;
+ imm->vertex_len = vertex_len;
+ imm->vertex_idx = 0;
+ imm->unassigned_attr_bits = imm->enabled_attr_bits;
- GPUVertBuf *verts = GPU_vertbuf_create_with_format(&imm.vertex_format);
+ GPUVertBuf *verts = GPU_vertbuf_create_with_format(&imm->vertex_format);
GPU_vertbuf_data_alloc(verts, vertex_len);
- imm.buffer_bytes_mapped = GPU_vertbuf_size_get(verts);
- imm.vertex_data = verts->data;
+ imm->vertex_data = verts->data;
- imm.batch = GPU_batch_create_ex(prim_type, verts, NULL, GPU_BATCH_OWNS_VBO);
- imm.batch->phase = GPU_BATCH_BUILDING;
+ imm->batch = GPU_batch_create_ex(prim_type, verts, NULL, GPU_BATCH_OWNS_VBO);
+ imm->batch->flag |= GPU_BATCH_BUILDING;
- return imm.batch;
+ return imm->batch;
}
GPUBatch *immBeginBatchAtMost(GPUPrimType prim_type, uint vertex_len)
{
- imm.strict_vertex_len = false;
+ BLI_assert(vertex_len > 0);
+ imm->strict_vertex_len = false;
return immBeginBatch(prim_type, vertex_len);
}
-static void immDrawSetup(void)
-{
- /* set up VAO -- can be done during Begin or End really */
- glBindVertexArray(imm.vao_id);
-
- /* Enable/Disable vertex attributes as needed. */
- if (imm.attr_binding.enabled_bits != imm.prev_enabled_attr_bits) {
- for (uint loc = 0; loc < GPU_VERT_ATTR_MAX_LEN; loc++) {
- bool is_enabled = imm.attr_binding.enabled_bits & (1 << loc);
- bool was_enabled = imm.prev_enabled_attr_bits & (1 << loc);
-
- if (is_enabled && !was_enabled) {
- glEnableVertexAttribArray(loc);
- }
- else if (was_enabled && !is_enabled) {
- glDisableVertexAttribArray(loc);
- }
- }
-
- imm.prev_enabled_attr_bits = imm.attr_binding.enabled_bits;
- }
-
- const uint stride = imm.vertex_format.stride;
-
- for (uint a_idx = 0; a_idx < imm.vertex_format.attr_len; a_idx++) {
- const GPUVertAttr *a = &imm.vertex_format.attrs[a_idx];
-
- const uint offset = imm.active_buffer->buffer_offset + a->offset;
- const GLvoid *pointer = (const GLubyte *)0 + offset;
-
- const uint loc = read_attr_location(&imm.attr_binding, a_idx);
- const GLenum type = convert_comp_type_to_gl(static_cast<GPUVertCompType>(a->comp_type));
-
- switch (a->fetch_mode) {
- case GPU_FETCH_FLOAT:
- case GPU_FETCH_INT_TO_FLOAT:
- glVertexAttribPointer(loc, a->comp_len, type, GL_FALSE, stride, pointer);
- break;
- case GPU_FETCH_INT_TO_FLOAT_UNIT:
- glVertexAttribPointer(loc, a->comp_len, type, GL_TRUE, stride, pointer);
- break;
- case GPU_FETCH_INT:
- glVertexAttribIPointer(loc, a->comp_len, type, stride, pointer);
- }
- }
-
- if (GPU_matrix_dirty_get()) {
- GPU_matrix_bind(imm.shader_interface);
- }
-}
-
void immEnd(void)
{
-#if TRUST_NO_ONE
- assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
- assert(imm.active_buffer || imm.batch);
-#endif
+ BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* Make sure we're between a Begin/End pair. */
+ BLI_assert(imm->vertex_data || imm->batch);
- uint buffer_bytes_used;
- if (imm.strict_vertex_len) {
-#if TRUST_NO_ONE
- assert(imm.vertex_idx == imm.vertex_len); /* with all vertices defined */
-#endif
- buffer_bytes_used = imm.buffer_bytes_mapped;
+ if (imm->strict_vertex_len) {
+ BLI_assert(imm->vertex_idx == imm->vertex_len); /* With all vertices defined. */
}
else {
-#if TRUST_NO_ONE
- assert(imm.vertex_idx <= imm.vertex_len);
-#endif
- if (imm.vertex_idx == imm.vertex_len) {
- buffer_bytes_used = imm.buffer_bytes_mapped;
- }
- else {
-#if TRUST_NO_ONE
- assert(imm.vertex_idx == 0 ||
- vertex_count_makes_sense_for_primitive(imm.vertex_idx, imm.prim_type));
-#endif
- imm.vertex_len = imm.vertex_idx;
- buffer_bytes_used = vertex_buffer_size(&imm.vertex_format, imm.vertex_len);
- /* unused buffer bytes are available to the next immBegin */
- }
- /* tell OpenGL what range was modified so it doesn't copy the whole mapped range */
- glFlushMappedBufferRange(GL_ARRAY_BUFFER, 0, buffer_bytes_used);
+ BLI_assert(imm->vertex_idx <= imm->vertex_len);
+ BLI_assert(imm->vertex_idx == 0 ||
+ vertex_count_makes_sense_for_primitive(imm->vertex_idx, imm->prim_type));
}
- if (imm.batch) {
- if (buffer_bytes_used != imm.buffer_bytes_mapped) {
- GPU_vertbuf_data_resize(imm.batch->verts[0], imm.vertex_len);
+ if (imm->batch) {
+ if (imm->vertex_idx < imm->vertex_len) {
+ GPU_vertbuf_data_resize(imm->batch->verts[0], imm->vertex_len);
/* TODO: resize only if vertex count is much smaller */
}
- GPU_batch_set_shader(imm.batch, imm.bound_program);
- imm.batch->phase = GPU_BATCH_READY_TO_DRAW;
- imm.batch = NULL; /* don't free, batch belongs to caller */
+ GPU_batch_set_shader(imm->batch, imm->shader);
+ imm->batch->flag &= ~GPU_BATCH_BUILDING;
+ imm->batch = NULL; /* don't free, batch belongs to caller */
}
else {
- glUnmapBuffer(GL_ARRAY_BUFFER);
-
- if (imm.vertex_len > 0) {
- immDrawSetup();
-#ifdef __APPLE__
- glDisable(GL_PRIMITIVE_RESTART);
-#endif
- glDrawArrays(convert_prim_type_to_gl(imm.prim_type), 0, imm.vertex_len);
-#ifdef __APPLE__
- glEnable(GL_PRIMITIVE_RESTART);
-#endif
- }
- /* These lines are causing crash on startup on some old GPU + drivers.
- * They are not required so just comment them. (T55722) */
- // glBindBuffer(GL_ARRAY_BUFFER, 0);
- // glBindVertexArray(0);
- /* prep for next immBegin */
- imm.active_buffer->buffer_offset += buffer_bytes_used;
+ imm->end();
}
- /* prep for next immBegin */
- imm.prim_type = GPU_PRIM_NONE;
- imm.strict_vertex_len = true;
- imm.active_buffer = NULL;
+ /* Prepare for next immBegin. */
+ imm->prim_type = GPU_PRIM_NONE;
+ imm->strict_vertex_len = true;
+ imm->vertex_data = NULL;
}
static void setAttrValueBit(uint attr_id)
{
uint16_t mask = 1 << attr_id;
-#if TRUST_NO_ONE
- assert(imm.unassigned_attr_bits & mask); /* not already set */
-#endif
- imm.unassigned_attr_bits &= ~mask;
+ BLI_assert(imm->unassigned_attr_bits & mask); /* not already set */
+ imm->unassigned_attr_bits &= ~mask;
}
/* --- generic attribute functions --- */
void immAttr1f(uint attr_id, float x)
{
- GPUVertAttr *attr = &imm.vertex_format.attrs[attr_id];
-#if TRUST_NO_ONE
- assert(attr_id < imm.vertex_format.attr_len);
- assert(attr->comp_type == GPU_COMP_F32);
- assert(attr->comp_len == 1);
- assert(imm.vertex_idx < imm.vertex_len);
- assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
-#endif
+ GPUVertAttr *attr = &imm->vertex_format.attrs[attr_id];
+ BLI_assert(attr_id < imm->vertex_format.attr_len);
+ BLI_assert(attr->comp_type == GPU_COMP_F32);
+ BLI_assert(attr->comp_len == 1);
+ BLI_assert(imm->vertex_idx < imm->vertex_len);
+ BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
setAttrValueBit(attr_id);
- float *data = (float *)(imm.vertex_data + attr->offset);
- /* printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm.buffer_data, data); */
+ float *data = (float *)(imm->vertex_data + attr->offset);
+ /* printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm->buffer_data, data); */
data[0] = x;
}
void immAttr2f(uint attr_id, float x, float y)
{
- GPUVertAttr *attr = &imm.vertex_format.attrs[attr_id];
-#if TRUST_NO_ONE
- assert(attr_id < imm.vertex_format.attr_len);
- assert(attr->comp_type == GPU_COMP_F32);
- assert(attr->comp_len == 2);
- assert(imm.vertex_idx < imm.vertex_len);
- assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
-#endif
+ GPUVertAttr *attr = &imm->vertex_format.attrs[attr_id];
+ BLI_assert(attr_id < imm->vertex_format.attr_len);
+ BLI_assert(attr->comp_type == GPU_COMP_F32);
+ BLI_assert(attr->comp_len == 2);
+ BLI_assert(imm->vertex_idx < imm->vertex_len);
+ BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
setAttrValueBit(attr_id);
- float *data = (float *)(imm.vertex_data + attr->offset);
- /* printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm.buffer_data, data); */
+ float *data = (float *)(imm->vertex_data + attr->offset);
+ /* printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm->buffer_data, data); */
data[0] = x;
data[1] = y;
@@ -503,18 +256,16 @@ void immAttr2f(uint attr_id, float x, float y)
void immAttr3f(uint attr_id, float x, float y, float z)
{
- GPUVertAttr *attr = &imm.vertex_format.attrs[attr_id];
-#if TRUST_NO_ONE
- assert(attr_id < imm.vertex_format.attr_len);
- assert(attr->comp_type == GPU_COMP_F32);
- assert(attr->comp_len == 3);
- assert(imm.vertex_idx < imm.vertex_len);
- assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
-#endif
+ GPUVertAttr *attr = &imm->vertex_format.attrs[attr_id];
+ BLI_assert(attr_id < imm->vertex_format.attr_len);
+ BLI_assert(attr->comp_type == GPU_COMP_F32);
+ BLI_assert(attr->comp_len == 3);
+ BLI_assert(imm->vertex_idx < imm->vertex_len);
+ BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
setAttrValueBit(attr_id);
- float *data = (float *)(imm.vertex_data + attr->offset);
- /* printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm.buffer_data, data); */
+ float *data = (float *)(imm->vertex_data + attr->offset);
+ /* printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm->buffer_data, data); */
data[0] = x;
data[1] = y;
@@ -523,18 +274,16 @@ void immAttr3f(uint attr_id, float x, float y, float z)
void immAttr4f(uint attr_id, float x, float y, float z, float w)
{
- GPUVertAttr *attr = &imm.vertex_format.attrs[attr_id];
-#if TRUST_NO_ONE
- assert(attr_id < imm.vertex_format.attr_len);
- assert(attr->comp_type == GPU_COMP_F32);
- assert(attr->comp_len == 4);
- assert(imm.vertex_idx < imm.vertex_len);
- assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
-#endif
+ GPUVertAttr *attr = &imm->vertex_format.attrs[attr_id];
+ BLI_assert(attr_id < imm->vertex_format.attr_len);
+ BLI_assert(attr->comp_type == GPU_COMP_F32);
+ BLI_assert(attr->comp_len == 4);
+ BLI_assert(imm->vertex_idx < imm->vertex_len);
+ BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
setAttrValueBit(attr_id);
- float *data = (float *)(imm.vertex_data + attr->offset);
- /* printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm.buffer_data, data); */
+ float *data = (float *)(imm->vertex_data + attr->offset);
+ /* printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm->buffer_data, data); */
data[0] = x;
data[1] = y;
@@ -544,34 +293,30 @@ void immAttr4f(uint attr_id, float x, float y, float z, float w)
void immAttr1u(uint attr_id, uint x)
{
- GPUVertAttr *attr = &imm.vertex_format.attrs[attr_id];
-#if TRUST_NO_ONE
- assert(attr_id < imm.vertex_format.attr_len);
- assert(attr->comp_type == GPU_COMP_U32);
- assert(attr->comp_len == 1);
- assert(imm.vertex_idx < imm.vertex_len);
- assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
-#endif
+ GPUVertAttr *attr = &imm->vertex_format.attrs[attr_id];
+ BLI_assert(attr_id < imm->vertex_format.attr_len);
+ BLI_assert(attr->comp_type == GPU_COMP_U32);
+ BLI_assert(attr->comp_len == 1);
+ BLI_assert(imm->vertex_idx < imm->vertex_len);
+ BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
setAttrValueBit(attr_id);
- uint *data = (uint *)(imm.vertex_data + attr->offset);
+ uint *data = (uint *)(imm->vertex_data + attr->offset);
data[0] = x;
}
void immAttr2i(uint attr_id, int x, int y)
{
- GPUVertAttr *attr = &imm.vertex_format.attrs[attr_id];
-#if TRUST_NO_ONE
- assert(attr_id < imm.vertex_format.attr_len);
- assert(attr->comp_type == GPU_COMP_I32);
- assert(attr->comp_len == 2);
- assert(imm.vertex_idx < imm.vertex_len);
- assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
-#endif
+ GPUVertAttr *attr = &imm->vertex_format.attrs[attr_id];
+ BLI_assert(attr_id < imm->vertex_format.attr_len);
+ BLI_assert(attr->comp_type == GPU_COMP_I32);
+ BLI_assert(attr->comp_len == 2);
+ BLI_assert(imm->vertex_idx < imm->vertex_len);
+ BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
setAttrValueBit(attr_id);
- int *data = (int *)(imm.vertex_data + attr->offset);
+ int *data = (int *)(imm->vertex_data + attr->offset);
data[0] = x;
data[1] = y;
@@ -579,17 +324,15 @@ void immAttr2i(uint attr_id, int x, int y)
void immAttr2s(uint attr_id, short x, short y)
{
- GPUVertAttr *attr = &imm.vertex_format.attrs[attr_id];
-#if TRUST_NO_ONE
- assert(attr_id < imm.vertex_format.attr_len);
- assert(attr->comp_type == GPU_COMP_I16);
- assert(attr->comp_len == 2);
- assert(imm.vertex_idx < imm.vertex_len);
- assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
-#endif
+ GPUVertAttr *attr = &imm->vertex_format.attrs[attr_id];
+ BLI_assert(attr_id < imm->vertex_format.attr_len);
+ BLI_assert(attr->comp_type == GPU_COMP_I16);
+ BLI_assert(attr->comp_len == 2);
+ BLI_assert(imm->vertex_idx < imm->vertex_len);
+ BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
setAttrValueBit(attr_id);
- short *data = (short *)(imm.vertex_data + attr->offset);
+ short *data = (short *)(imm->vertex_data + attr->offset);
data[0] = x;
data[1] = y;
@@ -612,18 +355,16 @@ void immAttr4fv(uint attr_id, const float data[4])
void immAttr3ub(uint attr_id, uchar r, uchar g, uchar b)
{
- GPUVertAttr *attr = &imm.vertex_format.attrs[attr_id];
-#if TRUST_NO_ONE
- assert(attr_id < imm.vertex_format.attr_len);
- assert(attr->comp_type == GPU_COMP_U8);
- assert(attr->comp_len == 3);
- assert(imm.vertex_idx < imm.vertex_len);
- assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
-#endif
+ GPUVertAttr *attr = &imm->vertex_format.attrs[attr_id];
+ BLI_assert(attr_id < imm->vertex_format.attr_len);
+ BLI_assert(attr->comp_type == GPU_COMP_U8);
+ BLI_assert(attr->comp_len == 3);
+ BLI_assert(imm->vertex_idx < imm->vertex_len);
+ BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
setAttrValueBit(attr_id);
- GLubyte *data = imm.vertex_data + attr->offset;
- /* printf("%s %td %p\n", __FUNCTION__, data - imm.buffer_data, data); */
+ uchar *data = imm->vertex_data + attr->offset;
+ /* printf("%s %td %p\n", __FUNCTION__, data - imm->buffer_data, data); */
data[0] = r;
data[1] = g;
@@ -632,18 +373,16 @@ void immAttr3ub(uint attr_id, uchar r, uchar g, uchar b)
void immAttr4ub(uint attr_id, uchar r, uchar g, uchar b, uchar a)
{
- GPUVertAttr *attr = &imm.vertex_format.attrs[attr_id];
-#if TRUST_NO_ONE
- assert(attr_id < imm.vertex_format.attr_len);
- assert(attr->comp_type == GPU_COMP_U8);
- assert(attr->comp_len == 4);
- assert(imm.vertex_idx < imm.vertex_len);
- assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
-#endif
+ GPUVertAttr *attr = &imm->vertex_format.attrs[attr_id];
+ BLI_assert(attr_id < imm->vertex_format.attr_len);
+ BLI_assert(attr->comp_type == GPU_COMP_U8);
+ BLI_assert(attr->comp_len == 4);
+ BLI_assert(imm->vertex_idx < imm->vertex_len);
+ BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
setAttrValueBit(attr_id);
- GLubyte *data = imm.vertex_data + attr->offset;
- /* printf("%s %td %p\n", __FUNCTION__, data - imm.buffer_data, data); */
+ uchar *data = imm->vertex_data + attr->offset;
+ /* printf("%s %td %p\n", __FUNCTION__, data - imm->buffer_data, data); */
data[0] = r;
data[1] = g;
@@ -663,45 +402,39 @@ void immAttr4ubv(uint attr_id, const uchar data[4])
void immAttrSkip(uint attr_id)
{
-#if TRUST_NO_ONE
- assert(attr_id < imm.vertex_format.attr_len);
- assert(imm.vertex_idx < imm.vertex_len);
- assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
-#endif
+ BLI_assert(attr_id < imm->vertex_format.attr_len);
+ BLI_assert(imm->vertex_idx < imm->vertex_len);
+ BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
setAttrValueBit(attr_id);
}
static void immEndVertex(void) /* and move on to the next vertex */
{
-#if TRUST_NO_ONE
- assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
- assert(imm.vertex_idx < imm.vertex_len);
-#endif
+ BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
+ BLI_assert(imm->vertex_idx < imm->vertex_len);
/* Have all attributes been assigned values?
* If not, copy value from previous vertex. */
- if (imm.unassigned_attr_bits) {
-#if TRUST_NO_ONE
- assert(imm.vertex_idx > 0); /* first vertex must have all attributes specified */
-#endif
- for (uint a_idx = 0; a_idx < imm.vertex_format.attr_len; a_idx++) {
- if ((imm.unassigned_attr_bits >> a_idx) & 1) {
- const GPUVertAttr *a = &imm.vertex_format.attrs[a_idx];
+ if (imm->unassigned_attr_bits) {
+ BLI_assert(imm->vertex_idx > 0); /* first vertex must have all attributes specified */
+ for (uint a_idx = 0; a_idx < imm->vertex_format.attr_len; a_idx++) {
+ if ((imm->unassigned_attr_bits >> a_idx) & 1) {
+ const GPUVertAttr *a = &imm->vertex_format.attrs[a_idx];
#if 0
- printf("copying %s from vertex %u to %u\n", a->name, imm.vertex_idx - 1, imm.vertex_idx);
+ printf("copying %s from vertex %u to %u\n", a->name, imm->vertex_idx - 1, imm->vertex_idx);
#endif
- GLubyte *data = imm.vertex_data + a->offset;
- memcpy(data, data - imm.vertex_format.stride, a->sz);
+ GLubyte *data = imm->vertex_data + a->offset;
+ memcpy(data, data - imm->vertex_format.stride, a->sz);
/* TODO: consolidate copy of adjacent attributes */
}
}
}
- imm.vertex_idx++;
- imm.vertex_data += imm.vertex_format.stride;
- imm.unassigned_attr_bits = imm.attr_binding.enabled_bits;
+ imm->vertex_idx++;
+ imm->vertex_data += imm->vertex_format.stride;
+ imm->unassigned_attr_bits = imm->enabled_attr_bits;
}
void immVertex2f(uint attr_id, float x, float y)
@@ -754,123 +487,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->shader, name, x);
}
void immUniform2f(const char *name, float x, float y)
{
- GET_UNIFORM
- glUniform2f(uniform->location, x, y);
+ GPU_shader_uniform_2f(imm->shader, name, x, y);
}
void immUniform2fv(const char *name, const float data[2])
{
- GET_UNIFORM
- glUniform2fv(uniform->location, 1, data);
+ GPU_shader_uniform_2fv(imm->shader, 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->shader, 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->shader, 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->shader, 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->shader, 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->shader, 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->shader, 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->shader, 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->shader, 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->shader, 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->shader, 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->shader, uniform_loc, 4, 1, data);
}
void immUniformColor4fv(const float rgba[4])
@@ -893,8 +580,6 @@ void immUniformColor3fvAlpha(const float rgb[3], float a)
immUniformColor4f(rgb[0], rgb[1], rgb[2], a);
}
-/* TODO: v-- treat as sRGB? --v */
-
void immUniformColor3ub(uchar r, uchar g, uchar b)
{
const float scale = 1.0f / 255.0f;
diff --git a/source/blender/gpu/intern/gpu_immediate_private.hh b/source/blender/gpu/intern/gpu_immediate_private.hh
new file mode 100644
index 00000000000..aa99fb9a438
--- /dev/null
+++ b/source/blender/gpu/intern/gpu_immediate_private.hh
@@ -0,0 +1,66 @@
+/*
+ * 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
+ *
+ * Mimics old style opengl immediate mode drawing.
+ */
+
+#pragma once
+
+#include "GPU_batch.h"
+#include "GPU_primitive.h"
+#include "GPU_shader.h"
+#include "GPU_vertex_format.h"
+
+namespace blender::gpu {
+
+class Immediate {
+ public:
+ /** Pointer to the mapped buffer data for the currect vertex. */
+ uchar *vertex_data = NULL;
+ /** Current vertex index. */
+ uint vertex_idx = 0;
+ /** Length of the buffer in vertices. */
+ uint vertex_len = 0;
+ /** Which attributes of current vertex have not been given values? */
+ uint16_t unassigned_attr_bits = 0;
+ /** Attributes that needs to be set. One bit per attribute. */
+ uint16_t enabled_attr_bits = 0;
+
+ /** Current draw call specification. */
+ GPUPrimType prim_type = GPU_PRIM_NONE;
+ GPUVertFormat vertex_format;
+ GPUShader *shader = NULL;
+ /** Enforce strict vertex count (disabled when using immBeginAtMost). */
+ bool strict_vertex_len = true;
+
+ /** Batch in construction when using immBeginBatch. */
+ GPUBatch *batch = NULL;
+
+ public:
+ Immediate(){};
+ virtual ~Immediate(){};
+
+ virtual uchar *begin(void) = 0;
+ virtual void end(void) = 0;
+};
+
+} // namespace blender::gpu \ No newline at end of file
diff --git a/source/blender/gpu/intern/gpu_init_exit.c b/source/blender/gpu/intern/gpu_init_exit.c
index ba0da95eb9d..4cb43db9bce 100644
--- a/source/blender/gpu/intern/gpu_init_exit.c
+++ b/source/blender/gpu/intern/gpu_init_exit.c
@@ -53,11 +53,6 @@ void GPU_init(void)
gpu_codegen_init();
gpu_material_library_init();
- gpu_framebuffer_module_init();
-
- if (G.debug & G_DEBUG_GPU) {
- gpu_debug_init();
- }
gpu_batch_init();
@@ -82,11 +77,6 @@ void GPU_exit(void)
gpu_batch_exit();
- if (G.debug & G_DEBUG_GPU) {
- gpu_debug_exit();
- }
-
- gpu_framebuffer_module_exit();
gpu_material_library_exit();
gpu_codegen_exit();
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 5d8d77bbf1c..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"
@@ -643,47 +641,44 @@ const float (*GPU_matrix_normal_inverse_get(float m[3][3]))[3]
return m;
}
-void GPU_matrix_bind(const GPUShaderInterface *shaderface)
+void GPU_matrix_bind(GPUShader *shader)
{
/* set uniform values to matrix stack values
* call this before a draw call if desired matrices are dirty
* call glUseProgram before this, as glUniform expects program to be bound
*/
+ 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 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 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);
- /* XXX(fclem) this works but this assumes shader is unused inside GPU_shader_uniform_vector. */
- GPUShader *sh = NULL;
if (MV != -1) {
- GPU_shader_uniform_vector(sh, MV, 16, 1, (const float *)GPU_matrix_model_view_get(NULL));
+ GPU_shader_uniform_vector(shader, MV, 16, 1, (const float *)GPU_matrix_model_view_get(NULL));
}
if (P != -1) {
- GPU_shader_uniform_vector(sh, P, 16, 1, (const float *)GPU_matrix_projection_get(NULL));
+ GPU_shader_uniform_vector(shader, P, 16, 1, (const float *)GPU_matrix_projection_get(NULL));
}
if (MVP != -1) {
GPU_shader_uniform_vector(
- sh, MVP, 16, 1, (const float *)GPU_matrix_model_view_projection_get(NULL));
+ shader, MVP, 16, 1, (const float *)GPU_matrix_model_view_projection_get(NULL));
}
if (N != -1) {
- GPU_shader_uniform_vector(sh, N, 9, 1, (const float *)GPU_matrix_normal_get(NULL));
+ GPU_shader_uniform_vector(shader, N, 9, 1, (const float *)GPU_matrix_normal_get(NULL));
}
if (MV_inv != -1) {
Mat4 m;
GPU_matrix_model_view_get(m);
invert_m4(m);
- GPU_shader_uniform_vector(sh, MV_inv, 16, 1, (const float *)m);
+ GPU_shader_uniform_vector(shader, MV_inv, 16, 1, (const float *)m);
}
if (P_inv != -1) {
Mat4 m;
GPU_matrix_projection_get(m);
invert_m4(m);
- GPU_shader_uniform_vector(sh, P_inv, 16, 1, (const float *)m);
+ GPU_shader_uniform_vector(shader, P_inv, 16, 1, (const float *)m);
}
gpu_matrix_state_active_set_dirty(false);
@@ -734,8 +729,8 @@ float GPU_polygon_offset_calc(const float (*winmat)[4], float viewdist, float di
#else
static float depth_fac = 0.0f;
if (depth_fac == 0.0f) {
- int depthbits;
- glGetIntegerv(GL_DEPTH_BITS, &depthbits);
+ /* Hardcode for 24 bit precision. */
+ int depthbits = 24;
depth_fac = 1.0f / (float)((1 << depthbits) - 1);
}
offs = (-1.0 / winmat[2][2]) * dist * depth_fac;
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_private.h b/source/blender/gpu/intern/gpu_private.h
index ef96bedae4a..505ac3b0278 100644
--- a/source/blender/gpu/intern/gpu_private.h
+++ b/source/blender/gpu/intern/gpu_private.h
@@ -32,14 +32,6 @@ void gpu_platform_exit(void);
void gpu_extensions_init(void);
void gpu_extensions_exit(void);
-/* gpu_debug.c */
-void gpu_debug_init(void);
-void gpu_debug_exit(void);
-
-/* gpu_framebuffer.c */
-void gpu_framebuffer_module_init(void);
-void gpu_framebuffer_module_exit(void);
-
/* gpu_pbvh.c */
void gpu_pbvh_init(void);
void gpu_pbvh_exit(void);
diff --git a/source/blender/gpu/intern/gpu_select_pick.c b/source/blender/gpu/intern/gpu_select_pick.c
index 0f6f29fab40..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"
@@ -282,6 +283,12 @@ typedef struct GPUPickState {
uint *rect_id;
} nearest;
};
+
+ /* Previous state to restore after drawing. */
+ int viewport[4];
+ int scissor[4];
+ eGPUWriteMask write_mask;
+ eGPUDepthTest depth_test;
} GPUPickState;
static GPUPickState g_pick_state = {0};
@@ -304,17 +311,18 @@ 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) {
- gpuPushAttr(GPU_DEPTH_BUFFER_BIT | GPU_VIEWPORT_BIT);
+ ps->write_mask = GPU_write_mask_get();
+ 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);
@@ -331,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);
@@ -510,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);
}
}
}
@@ -535,8 +548,9 @@ uint gpu_select_pick_end(void)
/* force finishing last pass */
gpu_select_pick_load_id(ps->gl.prev_id, true);
}
- gpuPopAttr();
- GPU_color_mask(true, true, true, true);
+ GPU_write_mask(ps->write_mask);
+ GPU_depth_test(ps->depth_test);
+ GPU_viewport(UNPACK4(ps->viewport));
}
/* assign but never free directly since it may be in cache */
diff --git a/source/blender/gpu/intern/gpu_select_sample_query.c b/source/blender/gpu/intern/gpu_select_sample_query.c
index f67c9c36a6b..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"
@@ -60,6 +61,12 @@ typedef struct GPUQueryState {
char mode;
uint index;
int oldhits;
+
+ /* Previous state to restore after drawing. */
+ int viewport[4];
+ int scissor[4];
+ eGPUWriteMask write_mask;
+ eGPUDepthTest depth_test;
} GPUQueryState;
static GPUQueryState g_query_state = {0};
@@ -67,8 +74,6 @@ static GPUQueryState g_query_state = {0};
void gpu_select_query_begin(
uint (*buffer)[4], uint bufsize, const rcti *input, char mode, int oldhits)
{
- float viewport[4];
-
g_query_state.query_issued = false;
g_query_state.active_query = 0;
g_query_state.num_of_queries = 0;
@@ -86,36 +91,42 @@ void gpu_select_query_begin(
"gpu selection ids");
glGenQueries(g_query_state.num_of_queries, g_query_state.queries);
- gpuPushAttr(GPU_DEPTH_BUFFER_BIT | GPU_VIEWPORT_BIT | GPU_SCISSOR_BIT);
- /* disable writing to the framebuffer */
- GPU_color_mask(false, false, false, false);
+ g_query_state.write_mask = GPU_write_mask_get();
+ 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);
+
+ /* 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_f(viewport);
- GPU_viewport(viewport[0], 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);
}
}
@@ -204,8 +215,10 @@ uint gpu_select_query_end(void)
glDeleteQueries(g_query_state.num_of_queries, g_query_state.queries);
MEM_freeN(g_query_state.queries);
MEM_freeN(g_query_state.id);
- gpuPopAttr();
- GPU_color_mask(true, true, true, true);
+
+ GPU_write_mask(g_query_state.write_mask);
+ GPU_depth_test(g_query_state.depth_test);
+ GPU_viewport(UNPACK4(g_query_state.viewport));
return hits;
}
diff --git a/source/blender/gpu/intern/gpu_shader.cc b/source/blender/gpu/intern/gpu_shader.cc
index 03b7d5402f5..360feb9a8c8 100644
--- a/source/blender/gpu/intern/gpu_shader.cc
+++ b/source/blender/gpu/intern/gpu_shader.cc
@@ -29,6 +29,7 @@
#include "BLI_string.h"
#include "BLI_string_utils.h"
#include "BLI_utildefines.h"
+#include "BLI_vector.hh"
#include "BKE_appdir.h"
#include "BKE_global.h"
@@ -40,259 +41,229 @@
#include "GPU_platform.h"
#include "GPU_shader.h"
#include "GPU_texture.h"
-#include "GPU_uniformbuffer.h"
+#include "GPU_uniform_buffer.h"
-#include "gpu_shader_private.h"
+#include "gpu_backend.hh"
+#include "gpu_context_private.hh"
+#include "gpu_shader_private.hh"
extern "C" char datatoc_gpu_shader_colorspace_lib_glsl[];
-/* Adjust these constants as needed. */
-#define MAX_DEFINE_LENGTH 256
-#define MAX_EXT_DEFINE_LENGTH 512
+using namespace blender;
+using namespace blender::gpu;
-#ifndef NDEBUG
-static uint g_shaderid = 0;
-#endif
+/** Opaque type hidding blender::gpu::Shader */
+struct GPUShader {
+ char _pad[1];
+};
/* -------------------------------------------------------------------- */
-/** \name Convenience functions
+/** \name Debug functions
* \{ */
-static void shader_print_errors(const char *task, const char *log, const char **code, int totcode)
+void Shader::print_errors(Span<const char *> sources, char *log, const char *stage)
{
- int line = 1;
-
- fprintf(stderr, "GPUShader: %s error:\n", task);
-
- for (int i = 0; i < totcode; i++) {
- const char *c, *pos, *end = code[i] + strlen(code[i]);
-
- if (G.debug & G_DEBUG) {
- fprintf(stderr, "===== shader string %d ====\n", i + 1);
-
- c = code[i];
- while ((c < end) && (pos = strchr(c, '\n'))) {
- fprintf(stderr, "%2d ", line);
- fwrite(c, (pos + 1) - c, 1, stderr);
- c = pos + 1;
- line++;
+ const char line_prefix[] = " | ";
+ char *sources_combined = BLI_string_join_arrayN((const char **)sources.data(), sources.size());
+
+ fprintf(stderr, "GPUShader: Compilation Log : %s : %s\n", this->name, stage);
+
+ char *log_line = log, *line_end;
+ char *error_line_number_end;
+ int error_line, error_char, last_error_line = -2, last_error_char = -1;
+ bool found_line_id = false;
+ while ((line_end = strchr(log_line, '\n'))) {
+ /* Skip empty lines. */
+ if (line_end == log_line) {
+ log_line++;
+ continue;
+ }
+ /* 0 = error, 1 = warning. */
+ int type = -1;
+ /* Skip ERROR: or WARNING:. */
+ const char *prefix[] = {"ERROR", "WARNING"};
+ for (int i = 0; i < ARRAY_SIZE(prefix); i++) {
+ if (STREQLEN(log_line, prefix[i], strlen(prefix[i]))) {
+ log_line += strlen(prefix[i]);
+ type = i;
+ break;
}
-
- fprintf(stderr, "%s", c);
}
- }
-
- fprintf(stderr, "%s\n", log);
+ /* Skip whitespaces and separators. */
+ while (ELEM(log_line[0], ':', '(', ' ')) {
+ log_line++;
+ }
+ /* Parse error line & char numbers. */
+ error_line = error_char = -1;
+ if (log_line[0] >= '0' && log_line[0] <= '9') {
+ error_line = (int)strtol(log_line, &error_line_number_end, 10);
+ /* Try to fetch the error caracter (not always available). */
+ if (ELEM(error_line_number_end[0], '(', ':') && error_line_number_end[1] != ' ') {
+ error_char = (int)strtol(error_line_number_end + 1, &log_line, 10);
+ }
+ else {
+ log_line = error_line_number_end;
+ }
+ /* There can be a 3rd number (case of mesa driver). */
+ if (ELEM(log_line[0], '(', ':') && log_line[1] >= '0' && log_line[1] <= '9') {
+ error_line = error_char;
+ error_char = (int)strtol(log_line + 1, &error_line_number_end, 10);
+ log_line = error_line_number_end;
+ }
+ }
+ /* Skip whitespaces and separators. */
+ while (ELEM(log_line[0], ':', ')', ' ')) {
+ log_line++;
+ }
+ if (error_line == -1) {
+ found_line_id = false;
+ }
+ const char *src_line = sources_combined;
+ if ((error_line != -1) && (error_char != -1)) {
+ if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_OFFICIAL)) {
+ /* source:line */
+ int error_source = error_line;
+ if (error_source < sources.size()) {
+ src_line = sources[error_source];
+ error_line = error_char;
+ error_char = -1;
+ }
+ }
+ else if (GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_OFFICIAL) ||
+ GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_MAC, GPU_DRIVER_OFFICIAL)) {
+ /* 0:line */
+ error_line = error_char;
+ error_char = -1;
+ }
+ else {
+ /* line:char */
+ }
+ }
+ /* Separate from previous block. */
+ if (last_error_line != error_line) {
+ fprintf(stderr, "\033[90m%s\033[39m\n", line_prefix);
+ }
+ else if (error_char != last_error_char) {
+ fprintf(stderr, "%s\n", line_prefix);
+ }
+ /* Print line from the source file that is producing the error. */
+ if ((error_line != -1) && (error_line != last_error_line || error_char != last_error_char)) {
+ const char *src_line_end = src_line;
+ found_line_id = false;
+ /* error_line is 1 based in this case. */
+ int src_line_index = 1;
+ while ((src_line_end = strchr(src_line, '\n'))) {
+ if (src_line_index == error_line) {
+ found_line_id = true;
+ break;
+ }
+ /* Continue to next line. */
+ src_line = src_line_end + 1;
+ src_line_index++;
+ }
+ /* Print error source. */
+ if (found_line_id) {
+ if (error_line != last_error_line) {
+ fprintf(stderr, "%5d | ", src_line_index);
+ }
+ else {
+ fprintf(stderr, line_prefix);
+ }
+ fwrite(src_line, (src_line_end + 1) - src_line, 1, stderr);
+ /* Print char offset. */
+ fprintf(stderr, line_prefix);
+ if (error_char != -1) {
+ for (int i = 0; i < error_char; i++) {
+ fprintf(stderr, " ");
+ }
+ fprintf(stderr, "^");
+ }
+ fprintf(stderr, "\n");
+ }
+ }
+ fprintf(stderr, line_prefix);
+ /* Skip to message. Avoid redundant info. */
+ const char *keywords[] = {"error", "warning"};
+ for (int i = 0; i < ARRAY_SIZE(prefix); i++) {
+ if (STREQLEN(log_line, keywords[i], strlen(keywords[i]))) {
+ log_line += strlen(keywords[i]);
+ type = i;
+ break;
+ }
+ }
+ /* Skip and separators. */
+ while (ELEM(log_line[0], ':', ')')) {
+ log_line++;
+ }
+ if (type == 0) {
+ fprintf(stderr, "\033[31;1mError\033[0;2m: ");
+ }
+ else if (type == 1) {
+ fprintf(stderr, "\033[33;1mWarning\033[0;2m: ");
+ }
+ /* Print the error itself. */
+ fprintf(stderr, "\033[2m");
+ fwrite(log_line, (line_end + 1) - log_line, 1, stderr);
+ fprintf(stderr, "\033[0m");
+ /* Continue to next line. */
+ log_line = line_end + 1;
+ last_error_line = error_line;
+ last_error_char = error_char;
+ }
+ fprintf(stderr, "\n");
+ MEM_freeN(sources_combined);
}
-static const char *gpu_shader_version(void)
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Creation / Destruction
+ * \{ */
+
+Shader::Shader(const char *sh_name)
{
- return "#version 330\n";
+ BLI_strncpy(this->name, sh_name, sizeof(this->name));
}
-static void gpu_shader_standard_extensions(char defines[MAX_EXT_DEFINE_LENGTH])
+Shader::~Shader()
{
- /* enable extensions for features that are not part of our base GLSL version
- * don't use an extension for something already available!
- */
-
- if (GLEW_ARB_texture_gather) {
- /* There is a bug on older Nvidia GPU where GL_ARB_texture_gather
- * is reported to be supported but yield a compile error (see T55802). */
- if (!GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY) || GLEW_VERSION_4_0) {
- strcat(defines, "#extension GL_ARB_texture_gather: enable\n");
-
- /* Some drivers don't agree on GLEW_ARB_texture_gather and the actual support in the
- * shader so double check the preprocessor define (see T56544). */
- if (!GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY) && !GLEW_VERSION_4_0) {
- strcat(defines, "#ifdef GL_ARB_texture_gather\n");
- strcat(defines, "# define GPU_ARB_texture_gather\n");
- strcat(defines, "#endif\n");
- }
- else {
- strcat(defines, "#define GPU_ARB_texture_gather\n");
- }
- }
- }
- if (GLEW_ARB_texture_query_lod) {
- /* a #version 400 feature, but we use #version 330 maximum so use extension */
- strcat(defines, "#extension GL_ARB_texture_query_lod: enable\n");
- }
- if (GLEW_ARB_shader_draw_parameters) {
- strcat(defines, "#extension GL_ARB_shader_draw_parameters : enable\n");
- strcat(defines, "#define GPU_ARB_shader_draw_parameters\n");
- }
- if (GPU_arb_texture_cube_map_array_is_supported()) {
- strcat(defines, "#extension GL_ARB_texture_cube_map_array : enable\n");
- strcat(defines, "#define GPU_ARB_texture_cube_map_array\n");
- }
+ delete interface;
}
-static void gpu_shader_standard_defines(char defines[MAX_DEFINE_LENGTH])
+static void standard_defines(Vector<const char *> &sources)
{
+ BLI_assert(sources.size() == 0);
+ /* Version needs to be first. Exact values will be added by implementation. */
+ sources.append("version");
/* some useful defines to detect GPU type */
if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_ANY)) {
- strcat(defines, "#define GPU_ATI\n");
- if (GPU_crappy_amd_driver()) {
- strcat(defines, "#define GPU_DEPRECATED_AMD_DRIVER\n");
- }
+ sources.append("#define GPU_ATI\n");
}
else if (GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY)) {
- strcat(defines, "#define GPU_NVIDIA\n");
+ sources.append("#define GPU_NVIDIA\n");
}
else if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_ANY, GPU_DRIVER_ANY)) {
- strcat(defines, "#define GPU_INTEL\n");
+ sources.append("#define GPU_INTEL\n");
}
-
/* some useful defines to detect OS type */
if (GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_WIN, GPU_DRIVER_ANY)) {
- strcat(defines, "#define OS_WIN\n");
+ sources.append("#define OS_WIN\n");
}
else if (GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_MAC, GPU_DRIVER_ANY)) {
- strcat(defines, "#define OS_MAC\n");
+ sources.append("#define OS_MAC\n");
}
else if (GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_UNIX, GPU_DRIVER_ANY)) {
- strcat(defines, "#define OS_UNIX\n");
- }
-
- float derivatives_factors[2];
- GPU_get_dfdy_factors(derivatives_factors);
- if (derivatives_factors[0] == 1.0f) {
- strcat(defines, "#define DFDX_SIGN 1.0\n");
- }
- else {
- strcat(defines, "#define DFDX_SIGN -1.0\n");
- }
-
- if (derivatives_factors[1] == 1.0f) {
- strcat(defines, "#define DFDY_SIGN 1.0\n");
- }
- else {
- strcat(defines, "#define DFDY_SIGN -1.0\n");
- }
-}
-
-#define DEBUG_SHADER_NONE ""
-#define DEBUG_SHADER_VERTEX "vert"
-#define DEBUG_SHADER_FRAGMENT "frag"
-#define DEBUG_SHADER_GEOMETRY "geom"
-
-/**
- * Dump GLSL shaders to disk
- *
- * This is used for profiling shader performance externally and debug if shader code is correct.
- * If called with no code, it simply bumps the shader index, so different shaders for the same
- * program share the same index.
- */
-static void gpu_dump_shaders(const char **code, const int num_shaders, const char *extension)
-{
- if ((G.debug & G_DEBUG_GPU_SHADERS) == 0) {
- return;
- }
-
- /* We use the same shader index for shaders in the same program.
- * So we call this function once before calling for the individual shaders. */
- static int shader_index = 0;
- if (code == NULL) {
- shader_index++;
- BLI_assert(STREQ(DEBUG_SHADER_NONE, extension));
- return;
- }
-
- /* Determine the full path of the new shader. */
- char shader_path[FILE_MAX];
-
- char file_name[512] = {'\0'};
- sprintf(file_name, "%04d.%s", shader_index, extension);
-
- BLI_join_dirfile(shader_path, sizeof(shader_path), BKE_tempdir_session(), file_name);
-
- /* Write shader to disk. */
- FILE *f = fopen(shader_path, "w");
- if (f == NULL) {
- printf("Error writing to file: %s\n", shader_path);
- }
- for (int j = 0; j < num_shaders; j++) {
- fprintf(f, "%s", code[j]);
- }
- fclose(f);
- printf("Shader file written to disk: %s\n", shader_path);
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Creation / Destruction
- * \{ */
-
-GPUShader *GPU_shader_create(const char *vertexcode,
- const char *fragcode,
- const char *geocode,
- const char *libcode,
- const char *defines,
- const char *shname)
-{
- return GPU_shader_create_ex(
- vertexcode, fragcode, geocode, libcode, defines, GPU_SHADER_TFB_NONE, NULL, 0, shname);
-}
-
-GPUShader *GPU_shader_create_from_python(const char *vertexcode,
- const char *fragcode,
- const char *geocode,
- const char *libcode,
- const char *defines)
-{
- char *libcodecat = NULL;
-
- if (libcode == NULL) {
- libcode = datatoc_gpu_shader_colorspace_lib_glsl;
- }
- else {
- libcode = libcodecat = BLI_strdupcat(libcode, datatoc_gpu_shader_colorspace_lib_glsl);
+ sources.append("#define OS_UNIX\n");
}
- GPUShader *sh = GPU_shader_create_ex(
- vertexcode, fragcode, geocode, libcode, defines, GPU_SHADER_TFB_NONE, NULL, 0, NULL);
-
- MEM_SAFE_FREE(libcodecat);
- return sh;
-}
-
-GPUShader *GPU_shader_load_from_binary(const char *binary,
- const int binary_format,
- const int binary_len,
- const char *shname)
-{
- BLI_assert(GL_ARB_get_program_binary);
- int success;
- int program = glCreateProgram();
-
- glProgramBinary(program, binary_format, binary, binary_len);
- glGetProgramiv(program, GL_LINK_STATUS, &success);
-
- if (success) {
- glUseProgram(program);
-
- GPUShader *shader = (GPUShader *)MEM_callocN(sizeof(*shader), __func__);
- shader->interface = GPU_shaderinterface_create(program);
- shader->program = program;
-
-#ifndef NDEBUG
- BLI_snprintf(shader->name, sizeof(shader->name), "%s_%u", shname, g_shaderid++);
-#else
- UNUSED_VARS(shname);
-#endif
-
- return shader;
+ if (GPU_crappy_amd_driver()) {
+ sources.append("#define GPU_DEPRECATED_AMD_DRIVER\n");
}
-
- glDeleteProgram(program);
- return NULL;
}
-GPUShader *GPU_shader_create_ex(const char *vertexcode,
+GPUShader *GPU_shader_create_ex(const char *vertcode,
const char *fragcode,
- const char *geocode,
+ const char *geomcode,
const char *libcode,
const char *defines,
const eGPUShaderTFBType tf_type,
@@ -300,223 +271,113 @@ GPUShader *GPU_shader_create_ex(const char *vertexcode,
const int tf_count,
const char *shname)
{
- GLint status;
- GLchar log[5000];
- GLsizei length = 0;
- GPUShader *shader;
- char standard_defines[MAX_DEFINE_LENGTH] = "";
- char standard_extensions[MAX_EXT_DEFINE_LENGTH] = "";
-
- shader = (GPUShader *)MEM_callocN(sizeof(GPUShader), "GPUShader");
- gpu_dump_shaders(NULL, 0, DEBUG_SHADER_NONE);
-
-#ifndef NDEBUG
- BLI_snprintf(shader->name, sizeof(shader->name), "%s_%u", shname, g_shaderid++);
-#else
- UNUSED_VARS(shname);
-#endif
-
/* At least a vertex shader and a fragment shader are required. */
- BLI_assert((fragcode != NULL) && (vertexcode != NULL));
-
- if (vertexcode) {
- shader->vertex = glCreateShader(GL_VERTEX_SHADER);
- }
- if (fragcode) {
- shader->fragment = glCreateShader(GL_FRAGMENT_SHADER);
- }
- if (geocode) {
- shader->geometry = glCreateShader(GL_GEOMETRY_SHADER);
- }
-
- shader->program = glCreateProgram();
-
- if (!shader->program || (vertexcode && !shader->vertex) || (fragcode && !shader->fragment) ||
- (geocode && !shader->geometry)) {
- fprintf(stderr, "GPUShader, object creation failed.\n");
- GPU_shader_free(shader);
- return NULL;
- }
+ BLI_assert((fragcode != NULL) && (vertcode != NULL));
- gpu_shader_standard_defines(standard_defines);
- gpu_shader_standard_extensions(standard_extensions);
+ Shader *shader = GPUBackend::get()->shader_alloc(shname);
- if (vertexcode) {
- const char *source[7];
- /* custom limit, may be too small, beware */
- int num_source = 0;
-
- source[num_source++] = gpu_shader_version();
- source[num_source++] =
- "#define GPU_VERTEX_SHADER\n"
- "#define IN_OUT out\n";
- source[num_source++] = standard_extensions;
- source[num_source++] = standard_defines;
-
- if (geocode) {
- source[num_source++] = "#define USE_GEOMETRY_SHADER\n";
+ if (vertcode) {
+ Vector<const char *> sources;
+ standard_defines(sources);
+ sources.append("#define GPU_VERTEX_SHADER\n");
+ sources.append("#define IN_OUT out\n");
+ if (geomcode) {
+ sources.append("#define USE_GEOMETRY_SHADER\n");
}
if (defines) {
- source[num_source++] = defines;
+ sources.append(defines);
}
- source[num_source++] = vertexcode;
-
- gpu_dump_shaders(source, num_source, DEBUG_SHADER_VERTEX);
-
- glAttachShader(shader->program, shader->vertex);
- glShaderSource(shader->vertex, num_source, source, NULL);
+ sources.append(vertcode);
- glCompileShader(shader->vertex);
- glGetShaderiv(shader->vertex, GL_COMPILE_STATUS, &status);
-
- if (!status) {
- glGetShaderInfoLog(shader->vertex, sizeof(log), &length, log);
- shader_print_errors("compile", log, source, num_source);
-
- GPU_shader_free(shader);
- return NULL;
- }
+ shader->vertex_shader_from_glsl(sources);
}
if (fragcode) {
- const char *source[8];
- int num_source = 0;
-
- source[num_source++] = gpu_shader_version();
- source[num_source++] =
- "#define GPU_FRAGMENT_SHADER\n"
- "#define IN_OUT in\n";
- source[num_source++] = standard_extensions;
- source[num_source++] = standard_defines;
-
- if (geocode) {
- source[num_source++] = "#define USE_GEOMETRY_SHADER\n";
+ Vector<const char *> sources;
+ standard_defines(sources);
+ sources.append("#define GPU_FRAGMENT_SHADER\n");
+ sources.append("#define IN_OUT in\n");
+ if (geomcode) {
+ sources.append("#define USE_GEOMETRY_SHADER\n");
}
if (defines) {
- source[num_source++] = defines;
+ sources.append(defines);
}
if (libcode) {
- source[num_source++] = libcode;
+ sources.append(libcode);
}
- source[num_source++] = fragcode;
-
- gpu_dump_shaders(source, num_source, DEBUG_SHADER_FRAGMENT);
+ sources.append(fragcode);
- glAttachShader(shader->program, shader->fragment);
- glShaderSource(shader->fragment, num_source, source, NULL);
-
- glCompileShader(shader->fragment);
- glGetShaderiv(shader->fragment, GL_COMPILE_STATUS, &status);
-
- if (!status) {
- glGetShaderInfoLog(shader->fragment, sizeof(log), &length, log);
- shader_print_errors("compile", log, source, num_source);
-
- GPU_shader_free(shader);
- return NULL;
- }
+ shader->fragment_shader_from_glsl(sources);
}
- if (geocode) {
- const char *source[6];
- int num_source = 0;
-
- source[num_source++] = gpu_shader_version();
- source[num_source++] = "#define GPU_GEOMETRY_SHADER\n";
- source[num_source++] = standard_extensions;
- source[num_source++] = standard_defines;
-
+ if (geomcode) {
+ Vector<const char *> sources;
+ standard_defines(sources);
+ sources.append("#define GPU_GEOMETRY_SHADER\n");
if (defines) {
- source[num_source++] = defines;
+ sources.append(defines);
}
- source[num_source++] = geocode;
-
- gpu_dump_shaders(source, num_source, DEBUG_SHADER_GEOMETRY);
-
- glAttachShader(shader->program, shader->geometry);
- glShaderSource(shader->geometry, num_source, source, NULL);
+ sources.append(geomcode);
- glCompileShader(shader->geometry);
- glGetShaderiv(shader->geometry, GL_COMPILE_STATUS, &status);
-
- if (!status) {
- glGetShaderInfoLog(shader->geometry, sizeof(log), &length, log);
- shader_print_errors("compile", log, source, num_source);
-
- GPU_shader_free(shader);
- return NULL;
- }
+ shader->geometry_shader_from_glsl(sources);
}
- if (tf_names != NULL) {
- glTransformFeedbackVaryings(shader->program, tf_count, tf_names, GL_INTERLEAVED_ATTRIBS);
- /* Primitive type must be setup */
+ if (tf_names != NULL && tf_count > 0) {
BLI_assert(tf_type != GPU_SHADER_TFB_NONE);
- shader->feedback_transform_type = tf_type;
+ shader->transform_feedback_names_set(Span<const char *>(tf_names, tf_count), tf_type);
}
- glLinkProgram(shader->program);
- glGetProgramiv(shader->program, GL_LINK_STATUS, &status);
- if (!status) {
- glGetProgramInfoLog(shader->program, sizeof(log), &length, log);
- /* print attached shaders in pipeline order */
- if (defines) {
- shader_print_errors("linking", log, &defines, 1);
- }
- if (vertexcode) {
- shader_print_errors("linking", log, &vertexcode, 1);
- }
- if (geocode) {
- shader_print_errors("linking", log, &geocode, 1);
- }
- if (libcode) {
- shader_print_errors("linking", log, &libcode, 1);
- }
- if (fragcode) {
- shader_print_errors("linking", log, &fragcode, 1);
- }
-
- GPU_shader_free(shader);
+ if (!shader->finalize()) {
+ delete shader;
return NULL;
- }
+ };
- glUseProgram(shader->program);
- shader->interface = GPU_shaderinterface_create(shader->program);
+ return reinterpret_cast<GPUShader *>(shader);
+}
- return shader;
+void GPU_shader_free(GPUShader *shader)
+{
+ delete reinterpret_cast<Shader *>(shader);
}
-#undef DEBUG_SHADER_GEOMETRY
-#undef DEBUG_SHADER_FRAGMENT
-#undef DEBUG_SHADER_VERTEX
-#undef DEBUG_SHADER_NONE
+/** \} */
-void GPU_shader_free(GPUShader *shader)
+/* -------------------------------------------------------------------- */
+/** \name Creation utils
+ * \{ */
+
+GPUShader *GPU_shader_create(const char *vertcode,
+ const char *fragcode,
+ const char *geomcode,
+ const char *libcode,
+ const char *defines,
+ const char *shname)
{
-#if 0 /* Would be nice to have, but for now the Deferred compilation \
- * does not have a GPUContext. */
- BLI_assert(GPU_context_active_get() != NULL);
-#endif
- BLI_assert(shader);
+ return GPU_shader_create_ex(
+ vertcode, fragcode, geomcode, libcode, defines, GPU_SHADER_TFB_NONE, NULL, 0, shname);
+}
- if (shader->vertex) {
- glDeleteShader(shader->vertex);
- }
- if (shader->geometry) {
- glDeleteShader(shader->geometry);
- }
- if (shader->fragment) {
- glDeleteShader(shader->fragment);
+GPUShader *GPU_shader_create_from_python(const char *vertcode,
+ const char *fragcode,
+ const char *geomcode,
+ const char *libcode,
+ const char *defines)
+{
+ char *libcodecat = NULL;
+
+ if (libcode == NULL) {
+ libcode = datatoc_gpu_shader_colorspace_lib_glsl;
}
- if (shader->program) {
- glDeleteProgram(shader->program);
+ else {
+ libcode = libcodecat = BLI_strdupcat(libcode, datatoc_gpu_shader_colorspace_lib_glsl);
}
- if (shader->interface) {
- GPU_shaderinterface_discard(shader->interface);
- }
+ GPUShader *sh = GPU_shader_create_ex(
+ vertcode, fragcode, geomcode, libcode, defines, GPU_SHADER_TFB_NONE, NULL, 0, "pyGPUShader");
- MEM_freeN(shader);
+ MEM_SAFE_FREE(libcodecat);
+ return sh;
}
static const char *string_join_array_maybe_alloc(const char **str_arr, bool *r_is_alloc)
@@ -565,7 +426,7 @@ static const char *string_join_array_maybe_alloc(const char **str_arr, bool *r_i
* \endcode
*/
struct GPUShader *GPU_shader_create_from_arrays_impl(
- const struct GPU_ShaderCreateFromArray_Params *params)
+ const struct GPU_ShaderCreateFromArray_Params *params, const char *func, int line)
{
struct {
const char *str;
@@ -577,8 +438,11 @@ struct GPUShader *GPU_shader_create_from_arrays_impl(
str_dst[i].str = string_join_array_maybe_alloc(str_src[i], &str_dst[i].is_alloc);
}
+ char name[64];
+ BLI_snprintf(name, sizeof(name), "%s_%d", func, line);
+
GPUShader *sh = GPU_shader_create(
- str_dst[0].str, str_dst[1].str, str_dst[2].str, NULL, str_dst[3].str, __func__);
+ str_dst[0].str, str_dst[1].str, str_dst[2].str, NULL, str_dst[3].str, name);
for (int i = 0; i < ARRAY_SIZE(str_dst); i++) {
if (str_dst[i].is_alloc) {
@@ -594,52 +458,51 @@ struct GPUShader *GPU_shader_create_from_arrays_impl(
/** \name Binding
* \{ */
-void GPU_shader_bind(GPUShader *shader)
+void GPU_shader_bind(GPUShader *gpu_shader)
{
- BLI_assert(shader && shader->program);
+ 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(gpu_shader);
+ GPU_shader_set_srgb_uniform(gpu_shader);
+ }
- glUseProgram(shader->program);
- GPU_matrix_bind(shader->interface);
- GPU_shader_set_srgb_uniform(shader->interface);
+ if (GPU_matrix_dirty_get()) {
+ GPU_matrix_bind(gpu_shader);
+ }
}
void GPU_shader_unbind(void)
{
- glUseProgram(0);
+#ifndef NDEBUG
+ GPUContext *ctx = GPU_context_active_get();
+ if (ctx->shader) {
+ reinterpret_cast<Shader *>(ctx->shader)->unbind();
+ }
+ ctx->shader = NULL;
+#endif
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Transform feedback
+ *
+ * TODO(fclem) Should be replaced by compute shaders.
* \{ */
-bool GPU_shader_transform_feedback_enable(GPUShader *shader, uint vbo_id)
+bool GPU_shader_transform_feedback_enable(GPUShader *shader, GPUVertBuf *vertbuf)
{
- if (shader->feedback_transform_type == GPU_SHADER_TFB_NONE) {
- return false;
- }
-
- glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, vbo_id);
-
- switch (shader->feedback_transform_type) {
- case GPU_SHADER_TFB_POINTS:
- glBeginTransformFeedback(GL_POINTS);
- return true;
- case GPU_SHADER_TFB_LINES:
- glBeginTransformFeedback(GL_LINES);
- return true;
- case GPU_SHADER_TFB_TRIANGLES:
- glBeginTransformFeedback(GL_TRIANGLES);
- return true;
- default:
- return false;
- }
+ return reinterpret_cast<Shader *>(shader)->transform_feedback_enable(vertbuf);
}
-void GPU_shader_transform_feedback_disable(GPUShader *UNUSED(shader))
+void GPU_shader_transform_feedback_disable(GPUShader *shader)
{
- glEndTransformFeedback();
+ reinterpret_cast<Shader *>(shader)->transform_feedback_disable();
}
/** \} */
@@ -650,50 +513,49 @@ void GPU_shader_transform_feedback_disable(GPUShader *UNUSED(shader))
int GPU_shader_get_uniform(GPUShader *shader, const char *name)
{
- BLI_assert(shader && shader->program);
- 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)
{
- BLI_assert(shader && shader->program);
- 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)
{
- BLI_assert(shader && shader->program);
- 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)
{
- BLI_assert(shader && shader->program);
- 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)
{
- BLI_assert(shader && shader->program);
- 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)
{
- BLI_assert(shader && shader->program);
- 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)
{
- BLI_assert(shader && shader->program);
- 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;
}
@@ -704,108 +566,109 @@ int GPU_shader_get_attribute(GPUShader *shader, const char *name)
* \{ */
/* Clement : Temp */
-int GPU_shader_get_program(GPUShader *shader)
+int GPU_shader_get_program(GPUShader *UNUSED(shader))
{
- return (int)shader->program;
+ /* TODO fixme */
+ return (int)0;
}
-char *GPU_shader_get_binary(GPUShader *shader, uint *r_binary_format, int *r_binary_len)
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Uniforms setters
+ * \{ */
+
+void GPU_shader_uniform_vector(
+ GPUShader *shader, int loc, int len, int arraysize, const float *value)
{
- BLI_assert(GLEW_ARB_get_program_binary);
- char *r_binary;
- int binary_len = 0;
+ reinterpret_cast<Shader *>(shader)->uniform_float(loc, len, arraysize, value);
+}
- glGetProgramiv(shader->program, GL_PROGRAM_BINARY_LENGTH, &binary_len);
- r_binary = (char *)MEM_mallocN(binary_len, __func__);
- glGetProgramBinary(shader->program, binary_len, NULL, r_binary_format, r_binary);
+void GPU_shader_uniform_vector_int(
+ GPUShader *shader, int loc, int len, int arraysize, const int *value)
+{
+ reinterpret_cast<Shader *>(shader)->uniform_int(loc, len, arraysize, value);
+}
- if (r_binary_len) {
- *r_binary_len = binary_len;
- }
+void GPU_shader_uniform_int(GPUShader *shader, int location, int value)
+{
+ GPU_shader_uniform_vector_int(shader, location, 1, 1, &value);
+}
- return r_binary;
+void GPU_shader_uniform_float(GPUShader *shader, int location, float value)
+{
+ GPU_shader_uniform_vector(shader, location, 1, 1, &value);
}
-/** \} */
+void GPU_shader_uniform_1i(GPUShader *sh, const char *name, int value)
+{
+ const int loc = GPU_shader_get_uniform(sh, name);
+ GPU_shader_uniform_int(sh, loc, value);
+}
-/* -------------------------------------------------------------------- */
-/** \name Uniforms setters
- * \{ */
+void GPU_shader_uniform_1b(GPUShader *sh, const char *name, bool value)
+{
+ GPU_shader_uniform_1i(sh, name, value ? 1 : 0);
+}
-void GPU_shader_uniform_float(GPUShader *UNUSED(shader), int location, float value)
+void GPU_shader_uniform_2f(GPUShader *sh, const char *name, float x, float y)
{
- if (location == -1) {
- return;
- }
+ const float data[2] = {x, y};
+ GPU_shader_uniform_2fv(sh, name, data);
+}
- glUniform1f(location, value);
+void GPU_shader_uniform_3f(GPUShader *sh, const char *name, float x, float y, float z)
+{
+ const float data[3] = {x, y, z};
+ GPU_shader_uniform_3fv(sh, name, data);
}
-void GPU_shader_uniform_vector(
- GPUShader *UNUSED(shader), int location, int length, int arraysize, const float *value)
+void GPU_shader_uniform_4f(GPUShader *sh, const char *name, float x, float y, float z, float w)
{
- if (location == -1 || value == NULL) {
- return;
- }
+ const float data[4] = {x, y, z, w};
+ GPU_shader_uniform_4fv(sh, name, data);
+}
- switch (length) {
- case 1:
- glUniform1fv(location, arraysize, value);
- break;
- case 2:
- glUniform2fv(location, arraysize, value);
- break;
- case 3:
- glUniform3fv(location, arraysize, value);
- break;
- case 4:
- glUniform4fv(location, arraysize, value);
- break;
- case 9:
- glUniformMatrix3fv(location, arraysize, 0, value);
- break;
- case 16:
- glUniformMatrix4fv(location, arraysize, 0, value);
- break;
- default:
- BLI_assert(0);
- break;
- }
+void GPU_shader_uniform_1f(GPUShader *sh, const char *name, float x)
+{
+ const int loc = GPU_shader_get_uniform(sh, name);
+ GPU_shader_uniform_float(sh, loc, x);
}
-void GPU_shader_uniform_int(GPUShader *UNUSED(shader), int location, int value)
+void GPU_shader_uniform_2fv(GPUShader *sh, const char *name, const float data[2])
{
- if (location == -1) {
- return;
- }
+ const int loc = GPU_shader_get_uniform(sh, name);
+ GPU_shader_uniform_vector(sh, loc, 2, 1, data);
+}
- glUniform1i(location, value);
+void GPU_shader_uniform_3fv(GPUShader *sh, const char *name, const float data[3])
+{
+ const int loc = GPU_shader_get_uniform(sh, name);
+ GPU_shader_uniform_vector(sh, loc, 3, 1, data);
}
-void GPU_shader_uniform_vector_int(
- GPUShader *UNUSED(shader), int location, int length, int arraysize, const int *value)
+void GPU_shader_uniform_4fv(GPUShader *sh, const char *name, const float data[4])
{
- if (location == -1) {
- return;
- }
+ const int loc = GPU_shader_get_uniform(sh, name);
+ GPU_shader_uniform_vector(sh, loc, 4, 1, data);
+}
- switch (length) {
- case 1:
- glUniform1iv(location, arraysize, value);
- break;
- case 2:
- glUniform2iv(location, arraysize, value);
- break;
- case 3:
- glUniform3iv(location, arraysize, value);
- break;
- case 4:
- glUniform4iv(location, arraysize, value);
- break;
- default:
- BLI_assert(0);
- break;
- }
+void GPU_shader_uniform_mat4(GPUShader *sh, const char *name, const float data[4][4])
+{
+ const int loc = GPU_shader_get_uniform(sh, name);
+ GPU_shader_uniform_vector(sh, loc, 16, 1, (const float *)data);
+}
+
+void GPU_shader_uniform_2fv_array(GPUShader *sh, const char *name, int len, const float (*val)[2])
+{
+ 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])
+{
+ const int loc = GPU_shader_get_uniform(sh, name);
+ GPU_shader_uniform_vector(sh, loc, 4, len, (const float *)val);
}
/** \} */
@@ -823,11 +686,11 @@ void GPU_shader_uniform_vector_int(
static int g_shader_builtin_srgb_transform = 0;
-void GPU_shader_set_srgb_uniform(const GPUShaderInterface *interface)
+void GPU_shader_set_srgb_uniform(GPUShader *shader)
{
- int32_t loc = GPU_shaderinterface_uniform_builtin(interface, GPU_UNIFORM_SRGB_TRANSFORM);
+ int32_t loc = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_SRGB_TRANSFORM);
if (loc != -1) {
- glUniform1i(loc, g_shader_builtin_srgb_transform);
+ 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 9c0692b76e2..ed95a236da5 100644
--- a/source/blender/gpu/intern/gpu_shader_builtin.c
+++ b/source/blender/gpu/intern/gpu_shader_builtin.c
@@ -40,9 +40,7 @@
#include "GPU_platform.h"
#include "GPU_shader.h"
#include "GPU_texture.h"
-#include "GPU_uniformbuffer.h"
-
-#include "gpu_shader_private.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 4511d4a199d..dc59dca9f78 100644
--- a/source/blender/gpu/intern/gpu_shader_interface.cc
+++ b/source/blender/gpu/intern/gpu_shader_interface.cc
@@ -23,157 +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.h"
-#include "gpu_context_private.hh"
-
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
+#include "BLI_span.hh"
+#include "BLI_vector.hh"
-#define DEBUG_SHADER_INTERFACE 0
-
-#if DEBUG_SHADER_INTERFACE
-# include <stdio.h>
-#endif
-
-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";
-
- 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];
}
@@ -183,358 +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);
+ 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";
- 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;
+ printf(" \033[1mGPUShaderInterface : \033[0m\n");
+ if (attrs.size() > 0) {
+ printf("\n Attributes :\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;
- }
- 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 = (GPUBatch **)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 (enabled_tex_mask_ > 0) {
+ printf("\n Samplers :\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 (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);
- }
- }
- 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) {
- gpu_batch_remove_interface_ref(shaderface->batches[i], shaderface);
+ for (const ShaderInput &samp : uniforms) {
+ /* Bypass uniforms. */
+ if (samp.binding != -1) {
+ printf(format, samp.name_hash, samp.binding, name_buf + samp.name_offset);
}
}
- MEM_freeN(shaderface->batches);
- /* Free memory used by shader interface by its self. */
- MEM_freeN(shaderface);
-}
-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);
+ printf("\n");
}
-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, GPUBatch *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 = (GPUBatch **)MEM_recallocN(shaderface->batches,
- sizeof(GPUBatch *) * shaderface->batches_len);
- }
- shaderface->batches[i] = batch;
-}
-
-void GPU_shaderinterface_remove_batch_ref(GPUShaderInterface *shaderface, GPUBatch *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..265fe90fc76
--- /dev/null
+++ b/source/blender/gpu/intern/gpu_shader_interface.hh
@@ -0,0 +1,255 @@
+/*
+ * 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 *ubo_get(const int binding) const
+ {
+ return input_lookup(inputs_ + attr_len_, ubo_len_, binding);
+ }
+
+ inline const ShaderInput *uniform_get(const char *name) const
+ {
+ return input_lookup(inputs_ + attr_len_ + ubo_len_, uniform_len_, name);
+ }
+
+ inline const char *input_name_get(const ShaderInput *input) const
+ {
+ return name_buffer_ + input->name_offset;
+ }
+
+ /* 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 ShaderInput *input_lookup(const ShaderInput *const inputs,
+ const uint inputs_len,
+ const int binding) 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 */
+}
+
+inline const ShaderInput *ShaderInterface::input_lookup(const ShaderInput *const inputs,
+ const uint inputs_len,
+ const int binding) const
+{
+ /* Simple linear search for now. */
+ for (int i = inputs_len - 1; i >= 0; i--) {
+ if (inputs[i].binding == binding) {
+ 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
new file mode 100644
index 00000000000..9c9aa835b97
--- /dev/null
+++ b/source/blender/gpu/intern/gpu_shader_private.hh
@@ -0,0 +1,80 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#pragma once
+
+#include "BLI_span.hh"
+
+#include "GPU_shader.h"
+#include "GPU_vertex_buffer.h"
+#include "gpu_shader_interface.hh"
+
+namespace blender {
+namespace gpu {
+
+/**
+ * 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();
+
+ virtual void vertex_shader_from_glsl(MutableSpan<const char *> sources) = 0;
+ virtual void geometry_shader_from_glsl(MutableSpan<const char *> sources) = 0;
+ virtual void fragment_shader_from_glsl(MutableSpan<const char *> sources) = 0;
+ virtual bool finalize(void) = 0;
+
+ virtual void transform_feedback_names_set(Span<const char *> name_list,
+ const eGPUShaderTFBType geom_type) = 0;
+ virtual bool transform_feedback_enable(GPUVertBuf *) = 0;
+ virtual void transform_feedback_disable(void) = 0;
+
+ virtual void bind(void) = 0;
+ virtual void unbind(void) = 0;
+
+ virtual void uniform_float(int location, int comp_len, int array_size, const float *data) = 0;
+ virtual void uniform_int(int location, int comp_len, int array_size, const int *data) = 0;
+
+ virtual void vertformat_from_shader(GPUVertFormat *) const = 0;
+
+ inline const char *const name_get(void) const
+ {
+ return name;
+ };
+
+ protected:
+ void print_errors(Span<const char *> sources, char *log, const char *stage);
+};
+
+} // namespace gpu
+} // namespace blender
+
+/* XXX do not use it. Special hack to use OCIO with batch API. */
+GPUShader *immGetShader(void);
diff --git a/source/blender/gpu/intern/gpu_state.cc b/source/blender/gpu/intern/gpu_state.cc
index 794c7a3eb97..478fd639cdd 100644
--- a/source/blender/gpu/intern/gpu_state.cc
+++ b/source/blender/gpu/intern/gpu_state.cc
@@ -25,6 +25,7 @@
# define PIXELSIZE (1.0f)
#endif
+#include "BLI_math_vector.h"
#include "BLI_utildefines.h"
#include "BKE_global.h"
@@ -33,239 +34,252 @@
#include "GPU_glew.h"
#include "GPU_state.h"
-static GLenum gpu_get_gl_blendfunction(eGPUBlendFunction blend)
-{
- switch (blend) {
- case GPU_ONE:
- return GL_ONE;
- case GPU_SRC_ALPHA:
- return GL_SRC_ALPHA;
- case GPU_ONE_MINUS_SRC_ALPHA:
- return GL_ONE_MINUS_SRC_ALPHA;
- case GPU_DST_COLOR:
- return GL_DST_COLOR;
- case GPU_ZERO:
- return GL_ZERO;
- default:
- BLI_assert(!"Unhandled blend mode");
- return GL_ZERO;
- }
+#include "gpu_context_private.hh"
+
+#include "gpu_state_private.hh"
+
+using namespace blender::gpu;
+
+#define SET_STATE(_prefix, _state, _value) \
+ do { \
+ GPUStateManager *stack = GPU_context_active_get()->state_manager; \
+ auto &state_object = stack->_prefix##state; \
+ state_object._state = (_value); \
+ } while (0)
+
+#define SET_IMMUTABLE_STATE(_state, _value) SET_STATE(, _state, _value)
+#define SET_MUTABLE_STATE(_state, _value) SET_STATE(mutable_, _state, _value)
+
+/* -------------------------------------------------------------------- */
+/** \name Immutable state Setters
+ * \{ */
+
+void GPU_blend(eGPUBlend blend)
+{
+ SET_IMMUTABLE_STATE(blend, blend);
}
-void GPU_blend(bool enable)
+void GPU_face_culling(eGPUFaceCullTest culling)
{
- if (enable) {
- glEnable(GL_BLEND);
- }
- else {
- glDisable(GL_BLEND);
- }
+ SET_IMMUTABLE_STATE(culling_test, culling);
}
-void GPU_blend_set_func(eGPUBlendFunction sfactor, eGPUBlendFunction dfactor)
+void GPU_front_facing(bool invert)
{
- glBlendFunc(gpu_get_gl_blendfunction(sfactor), gpu_get_gl_blendfunction(dfactor));
+ SET_IMMUTABLE_STATE(invert_facing, invert);
}
-void GPU_blend_set_func_separate(eGPUBlendFunction src_rgb,
- eGPUBlendFunction dst_rgb,
- eGPUBlendFunction src_alpha,
- eGPUBlendFunction dst_alpha)
+void GPU_provoking_vertex(eGPUProvokingVertex vert)
{
- glBlendFuncSeparate(gpu_get_gl_blendfunction(src_rgb),
- gpu_get_gl_blendfunction(dst_rgb),
- gpu_get_gl_blendfunction(src_alpha),
- gpu_get_gl_blendfunction(dst_alpha));
+ SET_IMMUTABLE_STATE(provoking_vert, vert);
}
-void GPU_face_culling(eGPUFaceCull culling)
+void GPU_depth_test(eGPUDepthTest test)
{
- if (culling == GPU_CULL_NONE) {
- glDisable(GL_CULL_FACE);
- }
- else {
- glEnable(GL_CULL_FACE);
- glCullFace((culling == GPU_CULL_FRONT) ? GL_FRONT : GL_BACK);
- }
+ SET_IMMUTABLE_STATE(depth_test, test);
}
-void GPU_front_facing(bool invert)
+void GPU_stencil_test(eGPUStencilTest test)
{
- glFrontFace((invert) ? GL_CW : GL_CCW);
+ SET_IMMUTABLE_STATE(stencil_test, test);
}
-void GPU_provoking_vertex(eGPUProvokingVertex vert)
+void GPU_line_smooth(bool enable)
{
- glProvokingVertex((vert == GPU_VERTEX_FIRST) ? GL_FIRST_VERTEX_CONVENTION :
- GL_LAST_VERTEX_CONVENTION);
+ SET_IMMUTABLE_STATE(line_smooth, enable);
}
-void GPU_depth_range(float near, float far)
+void GPU_polygon_smooth(bool enable)
{
- /* glDepthRangef is only for OpenGL 4.1 or higher */
- glDepthRange(near, far);
+ SET_IMMUTABLE_STATE(polygon_smooth, enable);
}
-void GPU_depth_test(bool enable)
+void GPU_logic_op_xor_set(bool enable)
{
- if (enable) {
- glEnable(GL_DEPTH_TEST);
- }
- else {
- glDisable(GL_DEPTH_TEST);
- }
+ SET_IMMUTABLE_STATE(logic_op_xor, enable);
}
-bool GPU_depth_test_enabled()
+void GPU_write_mask(eGPUWriteMask mask)
{
- return glIsEnabled(GL_DEPTH_TEST);
+ SET_IMMUTABLE_STATE(write_mask, mask);
}
-void GPU_line_smooth(bool enable)
+void GPU_color_mask(bool r, bool g, bool b, bool a)
{
- if (enable && ((G.debug & G_DEBUG_GPU) == 0)) {
- glEnable(GL_LINE_SMOOTH);
- }
- else {
- glDisable(GL_LINE_SMOOTH);
- }
+ GPUStateManager *stack = GPU_context_active_get()->state_manager;
+ auto &state = stack->state;
+ 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;
}
-void GPU_line_width(float width)
+void GPU_depth_mask(bool depth)
{
- float max_size = GPU_max_line_width();
- float final_size = width * PIXELSIZE;
- /* Fix opengl errors on certain platform / drivers. */
- CLAMP(final_size, 1.0f, max_size);
- glLineWidth(final_size);
+ GPUStateManager *stack = GPU_context_active_get()->state_manager;
+ auto &state = stack->state;
+ uint32_t write_mask = state.write_mask;
+ SET_FLAG_FROM_TEST(write_mask, depth, (uint32_t)GPU_WRITE_DEPTH);
+ state.write_mask = write_mask;
}
-void GPU_point_size(float size)
+void GPU_shadow_offset(bool enable)
{
- glPointSize(size * PIXELSIZE);
+ SET_IMMUTABLE_STATE(shadow_bias, enable);
}
-void GPU_polygon_smooth(bool enable)
+void GPU_clip_distances(int distances_enabled)
{
- if (enable && ((G.debug & G_DEBUG_GPU) == 0)) {
- glEnable(GL_POLYGON_SMOOTH);
- }
- else {
- glDisable(GL_POLYGON_SMOOTH);
- }
+ SET_IMMUTABLE_STATE(clip_distances, distances_enabled);
+}
+
+void GPU_state_set(eGPUWriteMask write_mask,
+ eGPUBlend blend,
+ eGPUFaceCullTest culling_test,
+ eGPUDepthTest depth_test,
+ eGPUStencilTest stencil_test,
+ eGPUStencilOp stencil_op,
+ eGPUProvokingVertex provoking_vert)
+{
+ GPUStateManager *stack = GPU_context_active_get()->state_manager;
+ auto &state = stack->state;
+ 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;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Mutable State Setters
+ * \{ */
+
+void GPU_depth_range(float near, float far)
+{
+ GPUStateManager *stack = GPU_context_active_get()->state_manager;
+ auto &state = stack->mutable_state;
+ copy_v2_fl2(state.depth_range, near, far);
+}
+
+void GPU_line_width(float width)
+{
+ SET_MUTABLE_STATE(line_width, width * PIXELSIZE);
+}
+
+void GPU_point_size(float size)
+{
+ SET_MUTABLE_STATE(point_size, size * PIXELSIZE);
}
/* Programmable point size
* - shaders set their own point size when enabled
* - use glPointSize when disabled */
+/* TODO remove and use program point size everywhere */
void GPU_program_point_size(bool enable)
{
- if (enable) {
- glEnable(GL_PROGRAM_POINT_SIZE);
- }
- else {
- glDisable(GL_PROGRAM_POINT_SIZE);
- }
+ GPUStateManager *stack = GPU_context_active_get()->state_manager;
+ auto &state = stack->mutable_state;
+ /* Set point size sign negative to disable. */
+ state.point_size = fabsf(state.point_size) * (enable ? 1 : -1);
}
void GPU_scissor_test(bool enable)
{
- if (enable) {
- glEnable(GL_SCISSOR_TEST);
- }
- else {
- glDisable(GL_SCISSOR_TEST);
- }
+ GPU_context_active_get()->active_fb->scissor_test_set(enable);
}
void GPU_scissor(int x, int y, int width, int height)
{
- glScissor(x, y, width, height);
+ int scissor_rect[4] = {x, y, width, height};
+ GPU_context_active_get()->active_fb->scissor_set(scissor_rect);
}
void GPU_viewport(int x, int y, int width, int height)
{
- glViewport(x, y, width, height);
+ int viewport_rect[4] = {x, y, width, height};
+ GPU_context_active_get()->active_fb->viewport_set(viewport_rect);
}
-void GPU_scissor_get_f(float coords[4])
+void GPU_stencil_reference_set(uint reference)
{
- glGetFloatv(GL_SCISSOR_BOX, coords);
+ SET_MUTABLE_STATE(stencil_reference, (uint8_t)reference);
}
-void GPU_scissor_get_i(int coords[4])
+void GPU_stencil_write_mask_set(uint write_mask)
{
- glGetIntegerv(GL_SCISSOR_BOX, coords);
+ SET_MUTABLE_STATE(stencil_write_mask, (uint8_t)write_mask);
}
-void GPU_viewport_size_get_f(float coords[4])
+void GPU_stencil_compare_mask_set(uint compare_mask)
{
- glGetFloatv(GL_VIEWPORT, coords);
+ SET_MUTABLE_STATE(stencil_compare_mask, (uint8_t)compare_mask);
}
-void GPU_viewport_size_get_i(int coords[4])
-{
- glGetIntegerv(GL_VIEWPORT, coords);
-}
+/** \} */
-void GPU_flush(void)
+/* -------------------------------------------------------------------- */
+/** \name State Getters
+ * \{ */
+
+eGPUBlend GPU_blend_get()
{
- glFlush();
+ GPUState &state = GPU_context_active_get()->state_manager->state;
+ return (eGPUBlend)state.blend;
}
-void GPU_finish(void)
+eGPUWriteMask GPU_write_mask_get()
{
- glFinish();
+ GPUState &state = GPU_context_active_get()->state_manager->state;
+ return (eGPUWriteMask)state.write_mask;
}
-void GPU_unpack_row_length_set(uint len)
+uint GPU_stencil_mask_get()
{
- glPixelStorei(GL_UNPACK_ROW_LENGTH, len);
+ GPUStateMutable &state = GPU_context_active_get()->state_manager->mutable_state;
+ return state.stencil_write_mask;
}
-void GPU_logic_op_xor_set(bool enable)
+eGPUDepthTest GPU_depth_test_get()
{
- if (enable) {
- glLogicOp(GL_XOR);
- glEnable(GL_COLOR_LOGIC_OP);
- }
- else {
- glDisable(GL_COLOR_LOGIC_OP);
- }
+ GPUState &state = GPU_context_active_get()->state_manager->state;
+ return (eGPUDepthTest)state.depth_test;
}
-void GPU_color_mask(bool r, bool g, bool b, bool a)
+eGPUStencilTest GPU_stencil_test_get()
{
- glColorMask(r, g, b, a);
+ GPUState &state = GPU_context_active_get()->state_manager->state;
+ return (eGPUStencilTest)state.stencil_test;
}
-void GPU_depth_mask(bool depth)
+void GPU_scissor_get(int coords[4])
{
- glDepthMask(depth);
+ GPU_context_active_get()->active_fb->scissor_get(coords);
}
-bool GPU_depth_mask_get(void)
+void GPU_viewport_size_get_f(float coords[4])
{
- GLint mask;
- glGetIntegerv(GL_DEPTH_WRITEMASK, &mask);
- return mask == GL_TRUE;
+ int viewport[4];
+ GPU_context_active_get()->active_fb->viewport_get(viewport);
+ for (int i = 0; i < 4; i++) {
+ coords[i] = viewport[i];
+ }
}
-void GPU_stencil_mask(uint stencil)
+void GPU_viewport_size_get_i(int coords[4])
{
- glStencilMask(stencil);
+ GPU_context_active_get()->active_fb->viewport_get(coords);
}
-void GPU_clip_distances(int distances_new)
+bool GPU_depth_mask_get(void)
{
- static int distances_enabled = 0;
- for (int i = 0; i < distances_new; i++) {
- glEnable(GL_CLIP_DISTANCE0 + i);
- }
- for (int i = distances_new; i < distances_enabled; i++) {
- glDisable(GL_CLIP_DISTANCE0 + i);
- }
- distances_enabled = distances_new;
+ GPUState &state = GPU_context_active_get()->state_manager->state;
+ return (state.write_mask & GPU_WRITE_DEPTH) != 0;
}
bool GPU_mipmap_enabled(void)
@@ -274,163 +288,61 @@ bool GPU_mipmap_enabled(void)
return true;
}
-/** \name GPU Push/Pop State
- * \{ */
-
-#define STATE_STACK_DEPTH 16
-
-typedef struct {
- eGPUAttrMask mask;
-
- /* GL_BLEND_BIT */
- uint is_blend : 1;
-
- /* GL_DEPTH_BUFFER_BIT */
- uint is_depth_test : 1;
- int depth_func;
- double depth_clear_value;
- bool depth_write_mask;
-
- /* GL_SCISSOR_BIT */
- int scissor_box[4];
- uint is_scissor_test : 1;
-
- /* GL_VIEWPORT_BIT */
- int viewport[4];
- double near_far[2];
-} GPUAttrValues;
-
-typedef struct {
- GPUAttrValues attr_stack[STATE_STACK_DEPTH];
- uint top;
-} GPUAttrStack;
-
-static GPUAttrStack state = {
- {},
- 0,
-};
+/** \} */
-#define AttrStack state
-#define Attr state.attr_stack[state.top]
+/* -------------------------------------------------------------------- */
+/** \name Context Utils
+ * \{ */
-/**
- * Replacement for glPush/PopAttributes
- *
- * We don't need to cover all the options of legacy OpenGL
- * but simply the ones used by Blender.
- */
-void gpuPushAttr(eGPUAttrMask mask)
+void GPU_flush(void)
{
- Attr.mask = mask;
-
- if ((mask & GPU_DEPTH_BUFFER_BIT) != 0) {
- Attr.is_depth_test = glIsEnabled(GL_DEPTH_TEST);
- glGetIntegerv(GL_DEPTH_FUNC, &Attr.depth_func);
- glGetDoublev(GL_DEPTH_CLEAR_VALUE, &Attr.depth_clear_value);
- glGetBooleanv(GL_DEPTH_WRITEMASK, (GLboolean *)&Attr.depth_write_mask);
- }
-
- if ((mask & GPU_SCISSOR_BIT) != 0) {
- Attr.is_scissor_test = glIsEnabled(GL_SCISSOR_TEST);
- glGetIntegerv(GL_SCISSOR_BOX, (GLint *)&Attr.scissor_box);
- }
-
- if ((mask & GPU_VIEWPORT_BIT) != 0) {
- glGetDoublev(GL_DEPTH_RANGE, (GLdouble *)&Attr.near_far);
- glGetIntegerv(GL_VIEWPORT, (GLint *)&Attr.viewport);
- }
-
- if ((mask & GPU_BLEND_BIT) != 0) {
- Attr.is_blend = glIsEnabled(GL_BLEND);
- }
-
- BLI_assert(AttrStack.top < STATE_STACK_DEPTH);
- AttrStack.top++;
+ glFlush();
}
-static void restore_mask(GLenum cap, const bool value)
+void GPU_finish(void)
{
- if (value) {
- glEnable(cap);
- }
- else {
- glDisable(cap);
- }
+ glFinish();
}
-void gpuPopAttr(void)
+void GPU_unpack_row_length_set(uint len)
{
- BLI_assert(AttrStack.top > 0);
- AttrStack.top--;
-
- GLint mask = Attr.mask;
-
- if ((mask & GPU_DEPTH_BUFFER_BIT) != 0) {
- restore_mask(GL_DEPTH_TEST, Attr.is_depth_test);
- glDepthFunc(Attr.depth_func);
- glClearDepth(Attr.depth_clear_value);
- glDepthMask(Attr.depth_write_mask);
- }
-
- if ((mask & GPU_VIEWPORT_BIT) != 0) {
- glViewport(Attr.viewport[0], Attr.viewport[1], Attr.viewport[2], Attr.viewport[3]);
- glDepthRange(Attr.near_far[0], Attr.near_far[1]);
- }
-
- if ((mask & GPU_SCISSOR_BIT) != 0) {
- restore_mask(GL_SCISSOR_TEST, Attr.is_scissor_test);
- glScissor(Attr.scissor_box[0], Attr.scissor_box[1], Attr.scissor_box[2], Attr.scissor_box[3]);
- }
-
- if ((mask & GPU_BLEND_BIT) != 0) {
- restore_mask(GL_BLEND, Attr.is_blend);
- }
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, len);
}
-#undef Attr
-#undef AttrStack
+/** \} */
-/* Default OpenGL State
+/* -------------------------------------------------------------------- */
+/** \name Default OpenGL State
*
* This is called on startup, for opengl offscreen render.
* Generally we should always return to this state when
* temporarily modifying the state for drawing, though that are (undocumented)
- * exceptions that we should try to get rid of. */
-
-void GPU_state_init(void)
-{
- GPU_program_point_size(false);
-
- glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
-
- glDisable(GL_BLEND);
- glDisable(GL_DEPTH_TEST);
- glDisable(GL_COLOR_LOGIC_OP);
- glDisable(GL_STENCIL_TEST);
- glDisable(GL_DITHER);
-
- glDepthFunc(GL_LEQUAL);
- glDepthRange(0.0, 1.0);
-
- glFrontFace(GL_CCW);
- glCullFace(GL_BACK);
- glDisable(GL_CULL_FACE);
-
- glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
- glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
-
- /* Is default but better be explicit. */
- glEnable(GL_MULTISAMPLE);
-
- /* This is a bit dangerous since addons could change this. */
- glEnable(GL_PRIMITIVE_RESTART);
- glPrimitiveRestartIndex((GLuint)0xFFFFFFFF);
+ * exceptions that we should try to get rid of.
+ * \{ */
- /* TODO: Should become default. But needs at least GL 4.3 */
- if (GLEW_ARB_ES3_compatibility) {
- /* Takes predecence over GL_PRIMITIVE_RESTART */
- glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
- }
+GPUStateManager::GPUStateManager(void)
+{
+ /* Set default state. */
+ state.write_mask = GPU_WRITE_COLOR;
+ state.blend = GPU_BLEND_NONE;
+ state.culling_test = GPU_CULL_NONE;
+ state.depth_test = GPU_DEPTH_NONE;
+ state.stencil_test = GPU_STENCIL_NONE;
+ state.stencil_op = GPU_STENCIL_OP_NONE;
+ state.provoking_vert = GPU_VERTEX_LAST;
+ state.logic_op_xor = false;
+ state.invert_facing = false;
+ state.shadow_bias = false;
+ state.polygon_smooth = false;
+ state.clip_distances = 0;
+
+ mutable_state.depth_range[0] = 0.0f;
+ mutable_state.depth_range[1] = 1.0f;
+ mutable_state.point_size = 1.0f;
+ mutable_state.line_width = 1.0f;
+ mutable_state.stencil_write_mask = 0x00;
+ mutable_state.stencil_compare_mask = 0x00;
+ mutable_state.stencil_reference = 0x00;
}
/** \} */
diff --git a/source/blender/gpu/intern/gpu_state_private.hh b/source/blender/gpu/intern/gpu_state_private.hh
new file mode 100644
index 00000000000..61234c4612c
--- /dev/null
+++ b/source/blender/gpu/intern/gpu_state_private.hh
@@ -0,0 +1,166 @@
+/*
+ * 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_utildefines.h"
+
+#include "GPU_state.h"
+
+#include <cstring>
+
+namespace blender {
+namespace gpu {
+
+/* Encapsulate all pipeline state that we need to track.
+ * Try to keep small to reduce validation time. */
+union GPUState {
+ struct {
+ /** 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;
+ uint32_t shadow_bias : 1;
+ /** Number of clip distances enabled. */
+ /* TODO(fclem) This should be a shader property. */
+ uint32_t clip_distances : 3;
+ /* TODO(fclem) remove, old opengl features. */
+ uint32_t polygon_smooth : 1;
+ uint32_t line_smooth : 1;
+ };
+ /* Here to allow fast bitwise ops. */
+ uint64_t data;
+};
+
+BLI_STATIC_ASSERT(sizeof(GPUState) == sizeof(uint64_t), "GPUState is too big.");
+
+inline bool operator==(const GPUState &a, const GPUState &b)
+{
+ return a.data == b.data;
+}
+
+inline bool operator!=(const GPUState &a, const GPUState &b)
+{
+ return !(a == b);
+}
+
+inline GPUState operator^(const GPUState &a, const GPUState &b)
+{
+ GPUState r;
+ r.data = a.data ^ b.data;
+ return r;
+}
+
+inline GPUState operator~(const GPUState &a)
+{
+ GPUState r;
+ r.data = ~a.data;
+ return r;
+}
+
+/* Mutable state that does not require pipeline change. */
+union GPUStateMutable {
+ struct {
+ /* Viewport State */
+ /** TODO remove */
+ float depth_range[2];
+ /** TODO remove, use explicit clear calls. */
+ float clear_color[4];
+ float clear_depth;
+ /** Negative if using program point size. */
+ /* TODO(fclem) should be passed as uniform to all shaders. */
+ float point_size;
+ /** Not supported on every platform. Prefer using wideline shader. */
+ float line_width;
+ /** Mutable stencil states. */
+ uint8_t stencil_write_mask;
+ uint8_t stencil_compare_mask;
+ uint8_t stencil_reference;
+ uint8_t _pad0;
+ /* IMPORTANT: ensure x64 stuct alignment. */
+ };
+ /* Here to allow fast bitwise ops. */
+ uint64_t data[9];
+};
+
+BLI_STATIC_ASSERT(sizeof(GPUStateMutable) == sizeof(GPUStateMutable::data),
+ "GPUStateMutable is too big.");
+
+inline bool operator==(const GPUStateMutable &a, const GPUStateMutable &b)
+{
+ return memcmp(&a, &b, sizeof(GPUStateMutable)) == 0;
+}
+
+inline bool operator!=(const GPUStateMutable &a, const GPUStateMutable &b)
+{
+ return !(a == b);
+}
+
+inline GPUStateMutable operator^(const GPUStateMutable &a, const GPUStateMutable &b)
+{
+ GPUStateMutable r;
+ for (int i = 0; i < ARRAY_SIZE(a.data); i++) {
+ r.data[i] = a.data[i] ^ b.data[i];
+ }
+ return r;
+}
+
+inline GPUStateMutable operator~(const GPUStateMutable &a)
+{
+ GPUStateMutable r;
+ for (int i = 0; i < ARRAY_SIZE(a.data); i++) {
+ r.data[i] = ~a.data[i];
+ }
+ 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;
+ GPUStateMutable mutable_state;
+
+ public:
+ GPUStateManager();
+ virtual ~GPUStateManager(){};
+
+ virtual void apply_state(void) = 0;
+};
+
+} // namespace gpu
+} // namespace blender
diff --git a/source/blender/gpu/intern/gpu_texture.cc b/source/blender/gpu/intern/gpu_texture.cc
index a45bd222664..1b7e1d4fd6a 100644
--- a/source/blender/gpu/intern/gpu_texture.cc
+++ b/source/blender/gpu/intern/gpu_texture.cc
@@ -44,6 +44,7 @@
#include "GPU_texture.h"
#include "gpu_context_private.hh"
+#include "gpu_framebuffer_private.hh"
#define WARN_NOT_BOUND(_tex) \
{ \
@@ -109,6 +110,9 @@ struct GPUTexture {
GPUContext *copy_fb_ctx;
};
+using namespace blender;
+using namespace blender::gpu;
+
static uint gpu_get_bytesize(eGPUTextureFormat data_type);
static void gpu_texture_framebuffer_ensure(GPUTexture *tex);
@@ -615,7 +619,7 @@ static bool gpu_texture_check_capacity(
GPUTexture *tex, GLenum proxy, GLenum internalformat, GLenum data_format, GLenum data_type)
{
if (proxy == GL_PROXY_TEXTURE_CUBE_MAP_ARRAY_ARB &&
- GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_MAC, GPU_DRIVER_ANY)) {
+ GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_MAC, GPU_DRIVER_ANY)) {
/* Special fix for T79703. */
/* Depth has already been checked. */
return tex->w <= GPU_max_cube_map_size();
@@ -1158,9 +1162,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:
@@ -1611,6 +1615,9 @@ void GPU_texture_clear(GPUTexture *tex, eGPUDataFormat gpu_data_format, const vo
/* This means that this function can only be used in one context for each texture. */
BLI_assert(tex->copy_fb_ctx == GPU_context_active_get());
+ int viewport[4];
+ GPU_viewport_size_get_i(viewport);
+
glBindFramebuffer(GL_FRAMEBUFFER, tex->copy_fb);
glViewport(0, 0, tex->w, tex->h);
@@ -1675,6 +1682,8 @@ void GPU_texture_clear(GPUTexture *tex, eGPUDataFormat gpu_data_format, const vo
glClear(GL_COLOR_BUFFER_BIT);
}
+ glViewport(UNPACK4(viewport));
+
if (prev_fb) {
GPU_framebuffer_bind(prev_fb);
}
@@ -2015,7 +2024,8 @@ void GPU_texture_free(GPUTexture *tex)
if (tex->refcount == 0) {
for (int i = 0; i < GPU_TEX_MAX_FBO_ATTACHED; i++) {
if (tex->fb[i] != NULL) {
- GPU_framebuffer_texture_detach_slot(tex->fb[i], tex, tex->fb_attachment[i]);
+ FrameBuffer *framebuffer = reinterpret_cast<FrameBuffer *>(tex->fb[i]);
+ framebuffer->attachment_set((GPUAttachmentType)tex->fb_attachment[i], GPU_ATTACHMENT_NONE);
}
}
@@ -2127,17 +2137,26 @@ void GPU_texture_attach_framebuffer(GPUTexture *tex, GPUFrameBuffer *fb, int att
}
/* Return previous attachment point */
-int GPU_texture_detach_framebuffer(GPUTexture *tex, GPUFrameBuffer *fb)
+void GPU_texture_detach_framebuffer(GPUTexture *tex, GPUFrameBuffer *fb)
{
for (int i = 0; i < GPU_TEX_MAX_FBO_ATTACHED; i++) {
if (tex->fb[i] == fb) {
tex->fb[i] = NULL;
- return tex->fb_attachment[i];
+ return;
}
}
-
BLI_assert(!"Error: Texture: Framebuffer is not attached");
- return 0;
+}
+
+/* Return attachment type for the given framebuffer or -1 if not attached. */
+int GPU_texture_framebuffer_attachement_get(GPUTexture *tex, GPUFrameBuffer *fb)
+{
+ for (int i = 0; i < GPU_TEX_MAX_FBO_ATTACHED; i++) {
+ if (tex->fb[i] == fb) {
+ return tex->fb_attachment[i];
+ }
+ }
+ return -1;
}
void GPU_texture_get_mipmap_size(GPUTexture *tex, int lvl, int *size)
@@ -2174,6 +2193,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);
@@ -2188,7 +2212,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_texture_private.hh b/source/blender/gpu/intern/gpu_texture_private.hh
new file mode 100644
index 00000000000..6aa2a39046e
--- /dev/null
+++ b/source/blender/gpu/intern/gpu_texture_private.hh
@@ -0,0 +1,53 @@
+/*
+ * 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 "BLI_assert.h"
+
+namespace blender {
+namespace gpu {
+
+class Texture {
+ public:
+ /** TODO(fclem): make it a non-static function. */
+ static GPUAttachmentType attachment_type(GPUTexture *tex, int slot)
+ {
+ switch (GPU_texture_format(tex)) {
+ case GPU_DEPTH_COMPONENT32F:
+ case GPU_DEPTH_COMPONENT24:
+ case GPU_DEPTH_COMPONENT16:
+ BLI_assert(slot == 0);
+ return GPU_FB_DEPTH_ATTACHMENT;
+ case GPU_DEPTH24_STENCIL8:
+ case GPU_DEPTH32F_STENCIL8:
+ BLI_assert(slot == 0);
+ return GPU_FB_DEPTH_STENCIL_ATTACHMENT;
+ default:
+ return static_cast<GPUAttachmentType>(GPU_FB_COLOR_ATTACHMENT0 + slot);
+ }
+ }
+};
+
+} // namespace gpu
+} // namespace blender
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..cf6447ccd37
--- /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 64
+#else
+# define DEBUG_NAME_LEN 8
+#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_buffer.cc b/source/blender/gpu/intern/gpu_vertex_buffer.cc
index 67ad8835b6a..debf9835c90 100644
--- a/source/blender/gpu/intern/gpu_vertex_buffer.cc
+++ b/source/blender/gpu/intern/gpu_vertex_buffer.cc
@@ -77,6 +77,7 @@ void GPU_vertbuf_init(GPUVertBuf *verts, GPUUsageType usage)
memset(verts, 0, sizeof(GPUVertBuf));
verts->usage = usage;
verts->dirty = true;
+ verts->handle_refcount = 1;
}
void GPU_vertbuf_init_with_format_ex(GPUVertBuf *verts,
@@ -137,7 +138,23 @@ void GPU_vertbuf_clear(GPUVertBuf *verts)
void GPU_vertbuf_discard(GPUVertBuf *verts)
{
GPU_vertbuf_clear(verts);
- MEM_freeN(verts);
+ GPU_vertbuf_handle_ref_remove(verts);
+}
+
+void GPU_vertbuf_handle_ref_add(GPUVertBuf *verts)
+{
+ verts->handle_refcount++;
+}
+
+void GPU_vertbuf_handle_ref_remove(GPUVertBuf *verts)
+{
+ BLI_assert(verts->handle_refcount > 0);
+ verts->handle_refcount--;
+ if (verts->handle_refcount == 0) {
+ /* Should already have been cleared. */
+ BLI_assert(verts->vbo_id == 0 && verts->data == NULL);
+ MEM_freeN(verts);
+ }
}
uint GPU_vertbuf_size_get(const GPUVertBuf *verts)
diff --git a/source/blender/gpu/intern/gpu_vertex_format.cc b/source/blender/gpu/intern/gpu_vertex_format.cc
index 2e8017660d0..ed317b3a0df 100644
--- a/source/blender/gpu/intern/gpu_vertex_format.cc
+++ b/source/blender/gpu/intern/gpu_vertex_format.cc
@@ -24,7 +24,9 @@
*/
#include "GPU_vertex_format.h"
+#include "gpu_shader_private.hh"
#include "gpu_vertex_format_private.h"
+
#include <stddef.h>
#include <string.h>
@@ -32,15 +34,14 @@
#include "BLI_string.h"
#include "BLI_utildefines.h"
-#include "GPU_shader.h"
-#include "gpu_shader_private.h"
-
#define PACK_DEBUG 0
#if PACK_DEBUG
# include <stdio.h>
#endif
+using namespace blender::gpu;
+
void GPU_vertformat_clear(GPUVertFormat *format)
{
#if TRUST_NO_ONE
@@ -404,112 +405,8 @@ void VertexFormat_pack(GPUVertFormat *format)
format->packed = true;
}
-static uint calc_component_size(const GLenum gl_type)
+void GPU_vertformat_from_shader(GPUVertFormat *format, const struct GPUShader *gpushader)
{
- switch (gl_type) {
- case GL_FLOAT_VEC2:
- case GL_INT_VEC2:
- case GL_UNSIGNED_INT_VEC2:
- return 2;
- case GL_FLOAT_VEC3:
- case GL_INT_VEC3:
- case GL_UNSIGNED_INT_VEC3:
- return 3;
- case GL_FLOAT_VEC4:
- case GL_FLOAT_MAT2:
- case GL_INT_VEC4:
- case GL_UNSIGNED_INT_VEC4:
- return 4;
- case GL_FLOAT_MAT3:
- return 9;
- case GL_FLOAT_MAT4:
- return 16;
- case GL_FLOAT_MAT2x3:
- case GL_FLOAT_MAT3x2:
- return 6;
- case GL_FLOAT_MAT2x4:
- case GL_FLOAT_MAT4x2:
- return 8;
- case GL_FLOAT_MAT3x4:
- case GL_FLOAT_MAT4x3:
- return 12;
- default:
- return 1;
- }
-}
-
-static void get_fetch_mode_and_comp_type(int gl_type,
- GPUVertCompType *r_comp_type,
- GPUVertFetchMode *r_fetch_mode)
-{
- switch (gl_type) {
- case GL_FLOAT:
- case GL_FLOAT_VEC2:
- case GL_FLOAT_VEC3:
- case GL_FLOAT_VEC4:
- case GL_FLOAT_MAT2:
- case GL_FLOAT_MAT3:
- case GL_FLOAT_MAT4:
- case GL_FLOAT_MAT2x3:
- case GL_FLOAT_MAT2x4:
- case GL_FLOAT_MAT3x2:
- case GL_FLOAT_MAT3x4:
- case GL_FLOAT_MAT4x2:
- case GL_FLOAT_MAT4x3:
- *r_comp_type = GPU_COMP_F32;
- *r_fetch_mode = GPU_FETCH_FLOAT;
- break;
- case GL_INT:
- case GL_INT_VEC2:
- case GL_INT_VEC3:
- case GL_INT_VEC4:
- *r_comp_type = GPU_COMP_I32;
- *r_fetch_mode = GPU_FETCH_INT;
- break;
- case GL_UNSIGNED_INT:
- case GL_UNSIGNED_INT_VEC2:
- case GL_UNSIGNED_INT_VEC3:
- case GL_UNSIGNED_INT_VEC4:
- *r_comp_type = GPU_COMP_U32;
- *r_fetch_mode = GPU_FETCH_INT;
- break;
- default:
- BLI_assert(0);
- }
-}
-
-void GPU_vertformat_from_shader(GPUVertFormat *format, const GPUShader *shader)
-{
- GPU_vertformat_clear(format);
- GPUVertAttr *attr = &format->attrs[0];
-
- GLint attr_len;
- glGetProgramiv(shader->program, GL_ACTIVE_ATTRIBUTES, &attr_len);
-
- for (int i = 0; i < attr_len; i++) {
- char name[256];
- GLenum gl_type;
- GLint size;
- glGetActiveAttrib(shader->program, i, sizeof(name), NULL, &size, &gl_type, name);
-
- /* Ignore OpenGL names like `gl_BaseInstanceARB`, `gl_InstanceID` and `gl_VertexID`. */
- if (glGetAttribLocation(shader->program, name) == -1) {
- continue;
- }
-
- format->name_len++; /* multiname support */
- format->attr_len++;
-
- GPUVertCompType comp_type;
- GPUVertFetchMode fetch_mode;
- get_fetch_mode_and_comp_type(gl_type, &comp_type, &fetch_mode);
-
- attr->names[attr->name_len++] = copy_attr_name(format, name);
- attr->offset = 0; /* offsets & stride are calculated later (during pack) */
- attr->comp_len = calc_component_size(gl_type) * size;
- attr->sz = attr->comp_len * 4;
- attr->fetch_mode = fetch_mode;
- attr->comp_type = comp_type;
- attr += 1;
- }
+ 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 f7c01b2f184..332350e47b5 100644
--- a/source/blender/gpu/opengl/gl_backend.hh
+++ b/source/blender/gpu/opengl/gl_backend.hh
@@ -27,7 +27,12 @@
#include "BLI_vector.hh"
+#include "gl_batch.hh"
#include "gl_context.hh"
+#include "gl_drawlist.hh"
+#include "gl_framebuffer.hh"
+#include "gl_shader.hh"
+#include "gl_uniform_buffer.hh"
namespace blender {
namespace gpu {
@@ -37,11 +42,41 @@ 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_);
};
+ Batch *batch_alloc(void)
+ {
+ return new GLBatch();
+ };
+
+ DrawList *drawlist_alloc(int list_length)
+ {
+ return new GLDrawList(list_length);
+ };
+
+ FrameBuffer *framebuffer_alloc(const char *name)
+ {
+ return new GLFrameBuffer(name);
+ };
+
+ Shader *shader_alloc(const char *name)
+ {
+ 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
new file mode 100644
index 00000000000..bb7d5654efd
--- /dev/null
+++ b/source/blender/gpu/opengl/gl_batch.cc
@@ -0,0 +1,381 @@
+/*
+ * 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
+ *
+ * GL implementation of GPUBatch.
+ * The only specificity of GL here is that it caches a list of
+ * Vertex Array Objects based on the bound shader interface.
+ */
+
+#include "BLI_assert.h"
+
+#include "glew-mx.h"
+
+#include "GPU_extensions.h"
+
+#include "gpu_batch_private.hh"
+#include "gpu_shader_private.hh"
+
+#include "gl_batch.hh"
+#include "gl_context.hh"
+#include "gl_debug.hh"
+#include "gl_primitive.hh"
+#include "gl_vertex_array.hh"
+
+using namespace blender::gpu;
+
+/* -------------------------------------------------------------------- */
+/** \name Vao cache
+ *
+ * Each GLBatch has a small cache of VAO objects that are used to avoid VAO reconfiguration.
+ * TODO(fclem) Could be revisited to avoid so much cross references.
+ * \{ */
+
+GLVaoCache::GLVaoCache(void)
+{
+ init();
+}
+
+GLVaoCache::~GLVaoCache()
+{
+ this->clear();
+}
+
+void GLVaoCache::init(void)
+{
+ context_ = NULL;
+ interface_ = NULL;
+ is_dynamic_vao_count = false;
+ for (int i = 0; i < GPU_VAO_STATIC_LEN; i++) {
+ static_vaos.interfaces[i] = NULL;
+ static_vaos.vao_ids[i] = 0;
+ }
+ 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 GLShaderInterface *interface, GLuint vao)
+{
+ /* Now insert the cache. */
+ if (!is_dynamic_vao_count) {
+ int i; /* find first unused slot */
+ for (i = 0; i < GPU_VAO_STATIC_LEN; i++) {
+ if (static_vaos.vao_ids[i] == 0) {
+ break;
+ }
+ }
+
+ if (i < GPU_VAO_STATIC_LEN) {
+ static_vaos.interfaces[i] = interface;
+ static_vaos.vao_ids[i] = vao;
+ }
+ else {
+ /* 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) {
+ const_cast<GLShaderInterface *>(static_vaos.interfaces[i])->ref_remove(this);
+ context_->vao_free(static_vaos.vao_ids[i]);
+ }
+ }
+ /* Not enough place switch to dynamic. */
+ 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 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");
+ }
+ }
+
+ if (is_dynamic_vao_count) {
+ int i; /* find first unused slot */
+ for (i = 0; i < dynamic_vaos.count; i++) {
+ if (dynamic_vaos.vao_ids[i] == 0) {
+ break;
+ }
+ }
+
+ if (i == dynamic_vaos.count) {
+ /* Not enough place, realloc the array. */
+ i = dynamic_vaos.count;
+ dynamic_vaos.count += GPU_BATCH_VAO_DYN_ALLOC_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);
+ }
+ dynamic_vaos.interfaces[i] = interface;
+ dynamic_vaos.vao_ids[i] = vao;
+ }
+
+ const_cast<GLShaderInterface *>(interface)->ref_add(this);
+}
+
+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 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]);
+ vaos[i] = 0;
+ interfaces[i] = NULL;
+ break; /* cannot have duplicates */
+ }
+ }
+}
+
+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 GLShaderInterface **interfaces = (is_dynamic_vao_count) ? dynamic_vaos.interfaces :
+ static_vaos.interfaces;
+ /* Early out, nothing to free. */
+ if (context_ == NULL) {
+ return;
+ }
+
+ if (context_ == ctx) {
+ glDeleteVertexArrays(count, vaos);
+ glDeleteVertexArrays(1, &vao_base_instance_);
+ }
+ else {
+ /* TODO(fclem) Slow way. Could avoid multiple mutex lock here */
+ for (int i = 0; i < count; i++) {
+ context_->vao_free(vaos[i]);
+ }
+ context_->vao_free(vao_base_instance_);
+ }
+
+ for (int i = 0; i < count; i++) {
+ if (interfaces[i] != NULL) {
+ const_cast<GLShaderInterface *>(interfaces[i])->ref_remove(this);
+ }
+ }
+
+ if (is_dynamic_vao_count) {
+ MEM_freeN((void *)dynamic_vaos.interfaces);
+ MEM_freeN(dynamic_vaos.vao_ids);
+ }
+
+ if (context_) {
+ context_->vao_cache_unregister(this);
+ }
+ /* Reinit. */
+ this->init();
+}
+
+/* Return 0 on cache miss (invalid VAO) */
+GLuint GLVaoCache::lookup(const GLShaderInterface *interface)
+{
+ const int count = (is_dynamic_vao_count) ? dynamic_vaos.count : GPU_VAO_STATIC_LEN;
+ 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];
+ }
+ }
+ return 0;
+}
+
+/* The GLVaoCache object is only valid for one GLContext.
+ * Reset the cache if trying to draw in another context; */
+void GLVaoCache::context_check(void)
+{
+ GLContext *ctx = static_cast<GLContext *>(GPU_context_active_get());
+ BLI_assert(ctx);
+
+ if (context_ != ctx) {
+ if (context_ != NULL) {
+ /* IMPORTANT: Trying to draw a batch in multiple different context will trash the VAO cache.
+ * This has major performance impact and should be avoided in most cases. */
+ context_->vao_cache_unregister(this);
+ }
+ this->clear();
+ context_ = ctx;
+ context_->vao_cache_register(this);
+ }
+}
+
+GLuint GLVaoCache::base_instance_vao_get(GPUBatch *batch, int i_first)
+{
+ this->context_check();
+ /* Make sure the interface is up to date. */
+ 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;
+ }
+ /**
+ * There seems to be a nasty bug when drawing using the same VAO reconfiguring (T71147).
+ * We just use a throwaway VAO for that. Note that this is likely to degrade performance.
+ **/
+#ifdef __APPLE__
+ glDeleteVertexArrays(1, &vao_base_instance_);
+ vao_base_instance_ = 0;
+ base_instance_ = 0;
+#endif
+
+ if (vao_base_instance_ == 0) {
+ glGenVertexArrays(1, &vao_base_instance_);
+ }
+
+ if (base_instance_ != i_first) {
+ base_instance_ = i_first;
+ GLVertArray::update_bindings(vao_base_instance_, batch, interface_, i_first);
+ }
+ return vao_base_instance_;
+}
+
+GLuint GLVaoCache::vao_get(GPUBatch *batch)
+{
+ this->context_check();
+
+ 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) {
+ /* Cache miss, create a new VAO. */
+ glGenVertexArrays(1, &vao_id_);
+ this->insert(interface_, vao_id_);
+ GLVertArray::update_bindings(vao_id_, batch, interface_, 0);
+ }
+ }
+
+ return vao_id_;
+}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Creation & Deletion
+ * \{ */
+
+GLBatch::GLBatch(void)
+{
+}
+
+GLBatch::~GLBatch()
+{
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Drawing
+ * \{ */
+
+#if GPU_TRACK_INDEX_RANGE
+# define BASE_INDEX(el) ((el)->base_index)
+# define INDEX_TYPE(el) ((el)->gl_index_type)
+#else
+# define BASE_INDEX(el) 0
+# define INDEX_TYPE(el) GL_UNSIGNED_INT
+#endif
+
+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();
+ }
+
+#if GPU_TRACK_INDEX_RANGE
+ /* Can be removed if GL 4.3 is required. */
+ if (!GLEW_ARB_ES3_compatibility && (elem != NULL)) {
+ glPrimitiveRestartIndex((elem->index_type == GPU_INDEX_U16) ? 0xFFFFu : 0xFFFFFFFFu);
+ }
+#endif
+
+ /* Can be removed if GL 4.2 is required. */
+ if (!GPU_arb_base_instance_is_supported() && (i_first > 0)) {
+ glBindVertexArray(vao_cache_.base_instance_vao_get(this, i_first));
+ }
+ else {
+ glBindVertexArray(vao_cache_.vao_get(this));
+ }
+}
+
+void GLBatch::draw(int v_first, int v_count, int i_first, int i_count)
+{
+ GL_CHECK_RESOURCES("Batch");
+ GL_CHECK_ERROR("Batch Pre drawing");
+
+ this->bind(i_first);
+
+ BLI_assert(v_count > 0 && i_count > 0);
+
+ GLenum gl_type = to_gl(prim_type);
+
+ if (elem) {
+ const GPUIndexBuf *el = elem;
+ GLenum index_type = INDEX_TYPE(el);
+ GLint base_index = BASE_INDEX(el);
+ void *v_first_ofs = (GLuint *)0 + v_first + el->index_start;
+
+#if GPU_TRACK_INDEX_RANGE
+ if (el->index_type == GPU_INDEX_U16) {
+ v_first_ofs = (GLushort *)0 + v_first + el->index_start;
+ }
+#endif
+
+ if (GPU_arb_base_instance_is_supported()) {
+ glDrawElementsInstancedBaseVertexBaseInstance(
+ gl_type, v_count, index_type, v_first_ofs, i_count, base_index, i_first);
+ }
+ else {
+ glDrawElementsInstancedBaseVertex(
+ gl_type, v_count, index_type, v_first_ofs, i_count, base_index);
+ }
+ GL_CHECK_ERROR("Batch Post-drawing Indexed");
+ }
+ else {
+#ifdef __APPLE__
+ glDisable(GL_PRIMITIVE_RESTART);
+#endif
+ if (GPU_arb_base_instance_is_supported()) {
+ glDrawArraysInstancedBaseInstance(gl_type, v_first, v_count, i_count, i_first);
+ }
+ else {
+ glDrawArraysInstanced(gl_type, v_first, v_count, 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
new file mode 100644
index 00000000000..9399148c68d
--- /dev/null
+++ b/source/blender/gpu/opengl/gl_batch.hh
@@ -0,0 +1,106 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2020, Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup gpu
+ *
+ * GPU geometry batch
+ * Contains VAOs + VBOs + Shader representing a drawable entity.
+ */
+
+#pragma once
+
+#include "MEM_guardedalloc.h"
+
+#include "gpu_batch_private.hh"
+
+#include "glew-mx.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)
+ * for each shader interface. Start with a static number of vaos and fallback to dynamic count
+ * if necessary. Once a batch goes dynamic it does not go back. */
+class GLVaoCache {
+ private:
+ /** Context for which the vao_cache_ was generated. */
+ GLContext *context_ = NULL;
+ /** Last interface this batch was drawn with. */
+ GLShaderInterface *interface_ = NULL;
+ /** Cached vao for the last interface. */
+ GLuint vao_id_ = 0;
+ /** Used whend arb_base_instance is not supported. */
+ GLuint vao_base_instance_ = 0;
+ int base_instance_ = 0;
+
+ bool is_dynamic_vao_count = false;
+ union {
+ /** Static handle count */
+ struct {
+ const GLShaderInterface *interfaces[GPU_VAO_STATIC_LEN];
+ GLuint vao_ids[GPU_VAO_STATIC_LEN];
+ } static_vaos;
+ /** Dynamic handle count */
+ struct {
+ uint count;
+ const GLShaderInterface **interfaces;
+ GLuint *vao_ids;
+ } dynamic_vaos;
+ };
+
+ public:
+ GLVaoCache();
+ ~GLVaoCache();
+
+ GLuint vao_get(GPUBatch *batch);
+ GLuint base_instance_vao_get(GPUBatch *batch, int i_first);
+
+ GLuint lookup(const GLShaderInterface *interface);
+ void insert(const GLShaderInterface *interface, GLuint vao_id);
+ void remove(const GLShaderInterface *interface);
+ void clear(void);
+
+ private:
+ void init(void);
+ void context_check(void);
+};
+
+class GLBatch : public Batch {
+ public:
+ /** All vaos corresponding to all the GPUShaderInterface this batch was drawn with. */
+ GLVaoCache vao_cache_;
+
+ public:
+ GLBatch();
+ ~GLBatch();
+
+ void draw(int v_first, int v_count, int i_first, int i_count) override;
+ void bind(int i_first);
+
+ MEM_CXX_CLASS_ALLOC_FUNCS("GLBatch");
+};
+
+} // namespace gpu
+} // namespace blender
diff --git a/source/blender/gpu/opengl/gl_context.cc b/source/blender/gpu/opengl/gl_context.cc
index 37c84abaa7f..1495e665aa8 100644
--- a/source/blender/gpu/opengl/gl_context.cc
+++ b/source/blender/gpu/opengl/gl_context.cc
@@ -22,14 +22,22 @@
*/
#include "BLI_assert.h"
+#include "BLI_system.h"
#include "BLI_utildefines.h"
+#include "BKE_global.h"
+
#include "GPU_framebuffer.h"
#include "GHOST_C-api.h"
#include "gpu_context_private.hh"
+#include "gl_debug.hh"
+#include "gl_immediate.hh"
+#include "gl_state.hh"
+#include "gl_uniform_buffer.hh"
+
#include "gl_backend.hh" /* TODO remove */
#include "gl_context.hh"
@@ -43,17 +51,50 @@ using namespace blender::gpu;
GLContext::GLContext(void *ghost_window, GLSharedOrphanLists &shared_orphan_list)
: shared_orphan_list_(shared_orphan_list)
{
- default_framebuffer_ = ghost_window ?
- GHOST_GetDefaultOpenGLFramebuffer((GHOST_WindowHandle)ghost_window) :
- 0;
-
- glGenVertexArrays(1, &default_vao_);
+ if (G.debug & G_DEBUG_GPU) {
+ debug::init_gl_callbacks();
+ }
float data[4] = {0.0f, 0.0f, 0.0f, 1.0f};
glGenBuffers(1, &default_attr_vbo_);
glBindBuffer(GL_ARRAY_BUFFER, default_attr_vbo_);
glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ state_manager = new GLStateManager();
+ imm = new GLImmediate();
+ ghost_window_ = ghost_window;
+
+ if (ghost_window) {
+ GLuint default_fbo = GHOST_GetDefaultOpenGLFramebuffer((GHOST_WindowHandle)ghost_window);
+ GHOST_RectangleHandle bounds = GHOST_GetClientBounds((GHOST_WindowHandle)ghost_window);
+ int w = GHOST_GetWidthRectangle(bounds);
+ int h = GHOST_GetHeightRectangle(bounds);
+ GHOST_DisposeRectangle(bounds);
+
+ if (default_fbo != 0) {
+ front_left = new GLFrameBuffer("front_left", this, GL_COLOR_ATTACHMENT0, default_fbo, w, h);
+ back_left = new GLFrameBuffer("back_left", this, GL_COLOR_ATTACHMENT0, default_fbo, w, h);
+ }
+ else {
+ front_left = new GLFrameBuffer("front_left", this, GL_FRONT_LEFT, 0, w, h);
+ back_left = new GLFrameBuffer("back_left", this, GL_BACK_LEFT, 0, w, h);
+ }
+ /* TODO(fclem) enable is supported. */
+ const bool supports_stereo_quad_buffer = false;
+ if (supports_stereo_quad_buffer) {
+ front_right = new GLFrameBuffer("front_right", this, GL_FRONT_RIGHT, 0, w, h);
+ back_right = new GLFrameBuffer("back_right", this, GL_BACK_RIGHT, 0, w, h);
+ }
+ }
+ else {
+ /* For offscreen contexts. Default framebuffer is NULL. */
+ back_left = new GLFrameBuffer("back_left", this, GL_NONE, 0, 0, 0);
+ }
+
+ active_fb = back_left;
+ static_cast<GLStateManager *>(state_manager)->active_fb = static_cast<GLFrameBuffer *>(
+ back_left);
}
GLContext::~GLContext()
@@ -63,10 +104,9 @@ GLContext::~GLContext()
/* For now don't allow GPUFrameBuffers to be reuse in another context. */
BLI_assert(framebuffers_.is_empty());
/* Delete vaos so the batch can be reused in another context. */
- for (GPUBatch *batch : batches_) {
- GPU_batch_vao_cache_clear(batch);
+ for (GLVaoCache *cache : vao_caches_) {
+ cache->clear();
}
- glDeleteVertexArrays(1, &default_vao_);
glDeleteBuffers(1, &default_attr_vbo_);
}
@@ -86,6 +126,31 @@ void GLContext::activate(void)
/* Clear accumulated orphans. */
orphans_clear();
+
+ if (ghost_window_) {
+ /* Get the correct framebuffer size for the internal framebuffers. */
+ GHOST_RectangleHandle bounds = GHOST_GetClientBounds((GHOST_WindowHandle)ghost_window_);
+ int w = GHOST_GetWidthRectangle(bounds);
+ int h = GHOST_GetHeightRectangle(bounds);
+ GHOST_DisposeRectangle(bounds);
+
+ if (front_left) {
+ front_left->size_set(w, h);
+ }
+ if (back_left) {
+ back_left->size_set(w, h);
+ }
+ if (front_right) {
+ front_right->size_set(w, h);
+ }
+ if (back_right) {
+ back_right->size_set(w, h);
+ }
+ }
+
+ /* Not really following the state but we should consider
+ * no ubo bound when activating a context. */
+ bound_ubo_slots = 0;
}
void GLContext::deactivate(void)
@@ -193,47 +258,22 @@ void GLBackend::tex_free(GLuint tex_id)
/** \name Linked object deletion
*
* These objects contain data that are stored per context. We
- * need to do some cleanup if they are used accross context or if context
+ * need to do some cleanup if they are used across context or if context
* is discarded.
* \{ */
-void GLContext::batch_register(struct GPUBatch *batch)
-{
- lists_mutex_.lock();
- batches_.add(batch);
- lists_mutex_.unlock();
-}
-
-void GLContext::batch_unregister(struct GPUBatch *batch)
-{
- /* vao_cache_clear() can acquire lists_mutex_ so avoid deadlock. */
- // reinterpret_cast<GLBatch *>(batch)->vao_cache_clear();
-
- lists_mutex_.lock();
- batches_.remove(batch);
- lists_mutex_.unlock();
-}
-
-void GLContext::framebuffer_register(struct GPUFrameBuffer *fb)
+void GLContext::vao_cache_register(GLVaoCache *cache)
{
-#ifdef DEBUG
lists_mutex_.lock();
- framebuffers_.add(fb);
+ vao_caches_.add(cache);
lists_mutex_.unlock();
-#else
- UNUSED_VARS(fb);
-#endif
}
-void GLContext::framebuffer_unregister(struct GPUFrameBuffer *fb)
+void GLContext::vao_cache_unregister(GLVaoCache *cache)
{
-#ifdef DEBUG
lists_mutex_.lock();
- framebuffers_.remove(fb);
+ vao_caches_.remove(cache);
lists_mutex_.unlock();
-#else
- UNUSED_VARS(fb);
-#endif
}
/** \} */
diff --git a/source/blender/gpu/opengl/gl_context.hh b/source/blender/gpu/opengl/gl_context.hh
index 3b55965b9d1..bc7e2060804 100644
--- a/source/blender/gpu/opengl/gl_context.hh
+++ b/source/blender/gpu/opengl/gl_context.hh
@@ -25,19 +25,20 @@
#include "gpu_context_private.hh"
+#include "GPU_framebuffer.h"
+
#include "BLI_set.hh"
#include "BLI_vector.hh"
#include "glew-mx.h"
-#include <iostream>
#include <mutex>
-#include <unordered_set>
-#include <vector>
namespace blender {
namespace gpu {
+class GLVaoCache;
+
class GLSharedOrphanLists {
public:
/** Mutex for the bellow structures. */
@@ -51,19 +52,19 @@ class GLSharedOrphanLists {
};
class GLContext : public GPUContext {
+ public:
+ /** Used for debugging purpose. Bitflags of all bound slots. */
+ uint16_t bound_ubo_slots;
+
/* TODO(fclem) these needs to become private. */
public:
- /** Default VAO for procedural draw calls. */
- GLuint default_vao_;
- /** Default framebuffer object for some GL implementation. */
- GLuint default_framebuffer_;
/** VBO for missing vertex attrib binding. Avoid undefined behavior on some implementation. */
GLuint default_attr_vbo_;
/**
* GPUBatch & GPUFramebuffer have references to the context they are from, in the case the
* context is destroyed, we need to remove any reference to it.
*/
- Set<GPUBatch *> batches_;
+ Set<GLVaoCache *> vao_caches_;
Set<GPUFrameBuffer *> framebuffers_;
/** Mutex for the bellow structures. */
std::mutex lists_mutex_;
@@ -77,6 +78,8 @@ class 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;
@@ -87,10 +90,8 @@ class GLContext : public GPUContext {
void vao_free(GLuint vao_id);
void fbo_free(GLuint fbo_id);
- void batch_register(struct GPUBatch *batch);
- void batch_unregister(struct GPUBatch *batch);
- void framebuffer_register(struct GPUFrameBuffer *fb);
- void framebuffer_unregister(struct GPUFrameBuffer *fb);
+ void vao_cache_register(GLVaoCache *cache);
+ void vao_cache_unregister(GLVaoCache *cache);
};
} // namespace gpu
diff --git a/source/blender/gpu/opengl/gl_debug.cc b/source/blender/gpu/opengl/gl_debug.cc
new file mode 100644
index 00000000000..c1a3780bb51
--- /dev/null
+++ b/source/blender/gpu/opengl/gl_debug.cc
@@ -0,0 +1,207 @@
+/*
+ * 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
+ *
+ * Debug features of OpenGL.
+ */
+
+#include "BLI_compiler_attrs.h"
+#include "BLI_string.h"
+#include "BLI_system.h"
+#include "BLI_utildefines.h"
+
+#include "glew-mx.h"
+
+#include "gl_context.hh"
+#include "gl_uniform_buffer.hh"
+
+#include "gl_debug.hh"
+
+#include <stdio.h>
+
+namespace blender::gpu::debug {
+
+/* -------------------------------------------------------------------- */
+/** \name Debug Callbacks
+ *
+ * Hooks up debug callbacks to a debug OpenGL context using extensions or 4.3 core debug
+ * capabiliities.
+ * \{ */
+
+/* Debug callbacks need the same calling convention as OpenGL functions. */
+#if defined(_WIN32)
+# define APIENTRY __stdcall
+#else
+# define APIENTRY
+#endif
+
+#define VERBOSE 1
+
+static void APIENTRY debug_callback(GLenum UNUSED(source),
+ GLenum type,
+ GLuint UNUSED(id),
+ GLenum severity,
+ GLsizei UNUSED(length),
+ const GLchar *message,
+ const GLvoid *UNUSED(userParm))
+{
+ const char format[] = "GPUDebug: %s%s\033[0m\n";
+
+ if (ELEM(severity, GL_DEBUG_SEVERITY_LOW, GL_DEBUG_SEVERITY_NOTIFICATION)) {
+ if (VERBOSE) {
+ fprintf(stderr, format, "\033[2m", message);
+ }
+ }
+ else {
+ switch (type) {
+ case GL_DEBUG_TYPE_ERROR:
+ case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
+ case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
+ fprintf(stderr, format, "\033[31;1mError\033[39m: ", message);
+ break;
+ case GL_DEBUG_TYPE_PORTABILITY:
+ case GL_DEBUG_TYPE_PERFORMANCE:
+ case GL_DEBUG_TYPE_OTHER:
+ case GL_DEBUG_TYPE_MARKER: /* KHR has this, ARB does not */
+ default:
+ fprintf(stderr, format, "\033[33;1mWarning\033[39m: ", message);
+ break;
+ }
+
+ if (VERBOSE && severity == GL_DEBUG_SEVERITY_HIGH) {
+ /* Focus on error message. */
+ fprintf(stderr, "\033[2m");
+ BLI_system_backtrace(stderr);
+ fprintf(stderr, "\033[0m\n");
+ fflush(stderr);
+ }
+ }
+}
+
+#undef APIENTRY
+
+void init_gl_callbacks(void)
+{
+#ifdef __APPLE__
+ fprintf(stderr, "GPUDebug: OpenGL debug callback is not available on Apple\n");
+ return;
+#endif /* not Apple */
+
+ char msg[256] = "";
+ const char format[] = "Successfully hooked OpenGL debug callback using %s";
+
+ if (GLEW_VERSION_4_3 || GLEW_KHR_debug) {
+ SNPRINTF(msg, format, GLEW_VERSION_4_3 ? "OpenGL 4.3" : "KHR_debug extension");
+ glEnable(GL_DEBUG_OUTPUT);
+ glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
+ glDebugMessageCallback((GLDEBUGPROC)debug_callback, NULL);
+ glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE);
+ glDebugMessageInsert(GL_DEBUG_SOURCE_APPLICATION,
+ GL_DEBUG_TYPE_MARKER,
+ 0,
+ GL_DEBUG_SEVERITY_NOTIFICATION,
+ -1,
+ msg);
+ }
+ else if (GLEW_ARB_debug_output) {
+ SNPRINTF(msg, format, "ARB_debug_output");
+ glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
+ glDebugMessageCallbackARB((GLDEBUGPROCARB)debug_callback, NULL);
+ glDebugMessageControlARB(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE);
+ glDebugMessageInsertARB(GL_DEBUG_SOURCE_APPLICATION_ARB,
+ GL_DEBUG_TYPE_OTHER_ARB,
+ 0,
+ GL_DEBUG_SEVERITY_LOW_ARB,
+ -1,
+ msg);
+ }
+ else {
+ fprintf(stderr, "GPUDebug: Failed to hook OpenGL debug callback\n");
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Error Checking
+ *
+ * This is only useful for implementation that does not support the KHR_debug extension OR when the
+ * implementations do not report any errors even when clearly doing shady things.
+ * \{ */
+
+void check_gl_error(const char *info)
+{
+ GLenum error = glGetError();
+
+#define ERROR_CASE(err) \
+ case err: { \
+ char msg[256]; \
+ SNPRINTF(msg, "%s : %s", #err, info); \
+ debug_callback(0, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, 0, msg, NULL); \
+ 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:
+ char msg[256];
+ SNPRINTF(msg, "Unknown GL error: %x : %s", error, info);
+ debug_callback(0, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, 0, msg, NULL);
+ break;
+ }
+}
+
+void check_gl_resources(const char *info)
+{
+ GLContext *ctx = static_cast<GLContext *>(GPU_context_active_get());
+ ShaderInterface *interface = ctx->shader->interface;
+ /* NOTE: This only check binding. To be valid, the bound ubo needs to
+ * be big enough to feed the data range the shader awaits. */
+ uint16_t ubo_needed = interface->enabled_ubo_mask_;
+ ubo_needed &= ~ctx->bound_ubo_slots;
+
+ if (ubo_needed == 0) {
+ return;
+ }
+
+ for (int i = 0; ubo_needed != 0; i++, ubo_needed >>= 1) {
+ if ((ubo_needed & 1) != 0) {
+ const ShaderInput *ubo_input = interface->ubo_get(i);
+ const char *ubo_name = interface->input_name_get(ubo_input);
+ const char *sh_name = ctx->shader->name_get();
+ char msg[256];
+ SNPRINTF(msg, "Missing UBO bind at slot %d : %s > %s : %s", i, sh_name, ubo_name, info);
+ debug_callback(0, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, 0, msg, NULL);
+ }
+ }
+}
+
+/** \} */
+
+} // namespace blender::gpu::debug \ No newline at end of file
diff --git a/source/blender/gpu/opengl/gl_debug.hh b/source/blender/gpu/opengl/gl_debug.hh
new file mode 100644
index 00000000000..dd98505ebc1
--- /dev/null
+++ b/source/blender/gpu/opengl/gl_debug.hh
@@ -0,0 +1,46 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#pragma once
+
+namespace blender {
+namespace gpu {
+namespace debug {
+
+/* Enabled on MacOS by default since there is no support for debug callbacks. */
+#if defined(DEBUG) && defined(__APPLE__)
+# define GL_CHECK_ERROR(info) debug::check_gl_error(info)
+#else
+# define GL_CHECK_ERROR(info)
+#endif
+
+#ifdef DEBUG
+# define GL_CHECK_RESOURCES(info) debug::check_gl_resources(info)
+#else
+# define GL_CHECK_RESOURCES(info)
+#endif
+
+void check_gl_error(const char *info);
+void check_gl_resources(const char *info);
+void init_gl_callbacks(void);
+
+} // namespace debug
+} // namespace gpu
+} // namespace blender
diff --git a/source/blender/gpu/opengl/gl_drawlist.cc b/source/blender/gpu/opengl/gl_drawlist.cc
new file mode 100644
index 00000000000..35fecc859b8
--- /dev/null
+++ b/source/blender/gpu/opengl/gl_drawlist.cc
@@ -0,0 +1,247 @@
+/*
+ * 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
+ *
+ * Implementation of Multi Draw Indirect using OpenGL.
+ * Fallback if the needed extensions are not supported.
+ */
+
+#include "BLI_assert.h"
+
+#include "GPU_batch.h"
+#include "GPU_extensions.h"
+
+#include "glew-mx.h"
+
+#include "gpu_context_private.hh"
+#include "gpu_drawlist_private.hh"
+
+#include "gl_backend.hh"
+#include "gl_drawlist.hh"
+#include "gl_primitive.hh"
+
+#include <limits.h>
+
+#define USE_MULTI_DRAW_INDIRECT 1
+
+/* TODO remove. */
+#if GPU_TRACK_INDEX_RANGE
+# define BASE_INDEX(el) ((el)->base_index)
+# define INDEX_TYPE(el) ((el)->gl_index_type)
+#else
+# define BASE_INDEX(el) 0
+# define INDEX_TYPE(el) GL_UNSIGNED_INT
+#endif
+
+using namespace blender::gpu;
+
+typedef struct GLDrawCommand {
+ GLuint v_count;
+ GLuint i_count;
+ GLuint v_first;
+ GLuint i_first;
+} GLDrawCommand;
+
+typedef struct GLDrawCommandIndexed {
+ GLuint v_count;
+ GLuint i_count;
+ GLuint v_first;
+ GLuint base_index;
+ GLuint i_first;
+} GLDrawCommandIndexed;
+
+#define MDI_ENABLED (buffer_size_ != 0)
+#define MDI_DISABLED (buffer_size_ == 0)
+#define MDI_INDEXED (base_index_ != UINT_MAX)
+
+GLDrawList::GLDrawList(int length)
+{
+ BLI_assert(length > 0);
+ batch_ = NULL;
+ buffer_id_ = 0;
+ command_len_ = 0;
+ command_offset_ = 0;
+ data_offset_ = 0;
+ data_size_ = 0;
+ data_ = NULL;
+
+ if (USE_MULTI_DRAW_INDIRECT && GLEW_ARB_multi_draw_indirect &&
+ GPU_arb_base_instance_is_supported()) {
+ /* Alloc the biggest possible command list, which is indexed. */
+ buffer_size_ = sizeof(GLDrawCommandIndexed) * length;
+ }
+ else {
+ /* Indicates MDI is not supported. */
+ buffer_size_ = 0;
+ }
+}
+
+GLDrawList::~GLDrawList()
+{
+ /* TODO This ... */
+ static_cast<GLBackend *>(GPUBackend::get())->buf_free(buffer_id_);
+ /* ... should be this. */
+ // context_->buf_free(buffer_id_)
+}
+
+void GLDrawList::init(void)
+{
+ BLI_assert(GPU_context_active_get());
+ BLI_assert(MDI_ENABLED);
+ BLI_assert(data_ == NULL);
+ batch_ = NULL;
+ command_len_ = 0;
+
+ if (buffer_id_ == 0) {
+ /* Allocate on first use. */
+ glGenBuffers(1, &buffer_id_);
+ context_ = static_cast<GLContext *>(GPU_context_active_get());
+ }
+
+ glBindBuffer(GL_DRAW_INDIRECT_BUFFER, buffer_id_);
+ /* If buffer is full, orphan buffer data and start fresh. */
+ // if (command_offset_ >= data_size_) {
+ glBufferData(GL_DRAW_INDIRECT_BUFFER, buffer_size_, NULL, GL_DYNAMIC_DRAW);
+ data_offset_ = 0;
+ // }
+ /* Map the remaining range. */
+ GLbitfield flag = GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT;
+ data_size_ = buffer_size_ - data_offset_;
+ data_ = (GLbyte *)glMapBufferRange(GL_DRAW_INDIRECT_BUFFER, data_offset_, data_size_, flag);
+ command_offset_ = 0;
+}
+
+void GLDrawList::append(GPUBatch *batch, int i_first, int i_count)
+{
+ /* Fallback when MultiDrawIndirect is not supported/enabled. */
+ if (MDI_DISABLED) {
+ GPU_batch_draw_advanced(batch, 0, 0, i_first, i_count);
+ return;
+ }
+
+ if (data_ == NULL) {
+ this->init();
+ }
+
+ if (batch != batch_) {
+ // BLI_assert(batch->flag | GPU_BATCH_INIT);
+ this->submit();
+ batch_ = batch;
+ /* Cached for faster access. */
+ base_index_ = batch->elem ? BASE_INDEX(batch->elem) : UINT_MAX;
+ v_first_ = batch->elem ? batch->elem->index_start : 0;
+ v_count_ = batch->elem ? batch->elem->index_len : batch->verts[0]->vertex_len;
+ }
+
+ if (v_count_ == 0) {
+ /* Nothing to draw. */
+ return;
+ }
+
+ if (MDI_INDEXED) {
+ GLDrawCommandIndexed *cmd = reinterpret_cast<GLDrawCommandIndexed *>(data_ + command_offset_);
+ cmd->v_first = v_first_;
+ cmd->v_count = v_count_;
+ cmd->i_count = i_count;
+ cmd->base_index = base_index_;
+ cmd->i_first = i_first;
+ command_offset_ += sizeof(GLDrawCommandIndexed);
+ }
+ else {
+ GLDrawCommand *cmd = reinterpret_cast<GLDrawCommand *>(data_ + command_offset_);
+ cmd->v_first = v_first_;
+ cmd->v_count = v_count_;
+ cmd->i_count = i_count;
+ cmd->i_first = i_first;
+ command_offset_ += sizeof(GLDrawCommand);
+ }
+
+ command_len_++;
+
+ if (command_offset_ >= data_size_) {
+ this->submit();
+ }
+}
+
+void GLDrawList::submit(void)
+{
+ if (command_len_ == 0) {
+ return;
+ }
+ /* Something's wrong if we get here without MDI support. */
+ BLI_assert(MDI_ENABLED);
+ BLI_assert(data_);
+ BLI_assert(GPU_context_active_get()->shader != NULL);
+
+ GLBatch *batch = static_cast<GLBatch *>(batch_);
+
+ /* Only do multi-draw indirect if doing more than 2 drawcall. This avoids the overhead of
+ * buffer mapping if scene is not very instance friendly. BUT we also need to take into
+ * account the
+ * case where only a few instances are needed to finish filling a call buffer. */
+ const bool is_finishing_a_buffer = (command_offset_ >= data_size_);
+ if (command_len_ > 2 || is_finishing_a_buffer) {
+ GLenum prim = to_gl(batch_->prim_type);
+ void *offset = (void *)data_offset_;
+
+ glBindBuffer(GL_DRAW_INDIRECT_BUFFER, buffer_id_);
+ glFlushMappedBufferRange(GL_DRAW_INDIRECT_BUFFER, 0, command_offset_);
+ glUnmapBuffer(GL_DRAW_INDIRECT_BUFFER);
+ data_ = NULL; /* Unmapped */
+ data_offset_ += command_offset_;
+
+ batch->bind(0);
+
+ if (MDI_INDEXED) {
+ glMultiDrawElementsIndirect(prim, INDEX_TYPE(batch_->elem), offset, command_len_, 0);
+ }
+ else {
+ glMultiDrawArraysIndirect(prim, offset, command_len_, 0);
+ }
+ }
+ else {
+ /* Fallback do simple drawcalls, and don't unmap the buffer. */
+ if (MDI_INDEXED) {
+ GLDrawCommandIndexed *cmd = (GLDrawCommandIndexed *)data_;
+ for (int i = 0; i < command_len_; i++, cmd++) {
+ /* Index start was already added. Avoid counting it twice. */
+ cmd->v_first -= batch->elem->index_start;
+ batch->draw(cmd->v_first, cmd->v_count, cmd->i_first, cmd->i_count);
+ }
+ /* Reuse the same data. */
+ command_offset_ -= command_len_ * sizeof(GLDrawCommandIndexed);
+ }
+ else {
+ GLDrawCommand *cmd = (GLDrawCommand *)data_;
+ for (int i = 0; i < command_len_; i++, cmd++) {
+ batch->draw(cmd->v_first, cmd->v_count, cmd->i_first, cmd->i_count);
+ }
+ /* Reuse the same data. */
+ command_offset_ -= command_len_ * sizeof(GLDrawCommand);
+ }
+ }
+ /* Do not submit this buffer again. */
+ command_len_ = 0;
+ /* Avoid keeping reference to the batch. */
+ batch_ = NULL;
+}
+
+/** \} */
diff --git a/source/blender/gpu/opengl/gl_drawlist.hh b/source/blender/gpu/opengl/gl_drawlist.hh
new file mode 100644
index 00000000000..b690b8f8a98
--- /dev/null
+++ b/source/blender/gpu/opengl/gl_drawlist.hh
@@ -0,0 +1,86 @@
+/*
+ * 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
+ *
+ * Implementation of Multi Draw Indirect using OpenGL.
+ * Fallback if the needed extensions are not supported.
+ */
+
+#pragma once
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_sys_types.h"
+
+#include "GPU_batch.h"
+#include "GPU_glew.h"
+
+#include "gpu_drawlist_private.hh"
+
+#include "gl_context.hh"
+
+namespace blender {
+namespace gpu {
+
+/**
+ * Implementation of Multi Draw Indirect using OpenGL.
+ **/
+class GLDrawList : public DrawList {
+ public:
+ GLDrawList(int length);
+ ~GLDrawList();
+
+ void append(GPUBatch *batch, int i_first, int i_count) override;
+ void submit(void) override;
+
+ private:
+ void init(void);
+
+ /** Batch for which we are recording commands for. */
+ GPUBatch *batch_;
+ /** Mapped memory bounds. */
+ GLbyte *data_;
+ /** Length of the mapped buffer (in byte). */
+ GLsizeiptr data_size_;
+ /** Current offset inside the mapped buffer (in byte). */
+ GLintptr command_offset_;
+ /** Current number of command recorded inside the mapped buffer. */
+ uint command_len_;
+ /** Is UINT_MAX if not drawing indexed geom. Also Avoid dereferencing batch. */
+ GLuint base_index_;
+ /** Also Avoid dereferencing batch. */
+ GLuint v_first_, v_count_;
+
+ /** GL Indirect Buffer id. 0 means MultiDrawIndirect is not supported/enabled. */
+ GLuint buffer_id_;
+ /** Length of whole the buffer (in byte). */
+ GLsizeiptr buffer_size_;
+ /** Offset of data_ inside the whole buffer (in byte). */
+ GLintptr data_offset_;
+
+ /** To free the buffer_id_. */
+ GLContext *context_;
+
+ MEM_CXX_CLASS_ALLOC_FUNCS("GLDrawList");
+};
+
+} // namespace gpu
+} // namespace blender
diff --git a/source/blender/gpu/opengl/gl_framebuffer.cc b/source/blender/gpu/opengl/gl_framebuffer.cc
new file mode 100644
index 00000000000..8d48c9f8de3
--- /dev/null
+++ b/source/blender/gpu/opengl/gl_framebuffer.cc
@@ -0,0 +1,460 @@
+/*
+ * 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 "GPU_extensions.h"
+
+#include "gl_backend.hh"
+#include "gl_framebuffer.hh"
+#include "gl_state.hh"
+#include "gl_texture.hh"
+
+namespace blender::gpu {
+
+/* -------------------------------------------------------------------- */
+/** \name Creation & Deletion
+ * \{ */
+
+GLFrameBuffer::GLFrameBuffer(const char *name) : FrameBuffer(name)
+{
+ /* Just-In-Time init. See GLFrameBuffer::init(). */
+ immutable_ = false;
+ fbo_id_ = 0;
+}
+
+GLFrameBuffer::GLFrameBuffer(
+ const char *name, GLContext *ctx, GLenum target, GLuint fbo, int w, int h)
+ : FrameBuffer(name)
+{
+ context_ = ctx;
+ state_manager_ = static_cast<GLStateManager *>(ctx->state_manager);
+ immutable_ = true;
+ fbo_id_ = fbo;
+ gl_attachments_[0] = target;
+ /* Never update an internal framebuffer. */
+ dirty_attachments_ = false;
+ width_ = w;
+ height_ = h;
+ srgb_ = false;
+
+ viewport_[0] = scissor_[0] = 0;
+ viewport_[1] = scissor_[1] = 0;
+ viewport_[2] = scissor_[2] = w;
+ viewport_[3] = scissor_[3] = h;
+
+#ifndef __APPLE__
+ if (fbo_id_ && (G.debug & G_DEBUG_GPU) && (GLEW_VERSION_4_3 || GLEW_KHR_debug)) {
+ char sh_name[32];
+ SNPRINTF(sh_name, "FrameBuffer-%s", name);
+ glObjectLabel(GL_FRAMEBUFFER, fbo_id_, -1, sh_name);
+ }
+#endif
+}
+
+GLFrameBuffer::~GLFrameBuffer()
+{
+ if (context_ == NULL) {
+ return;
+ }
+
+ if (context_ == GPU_context_active_get()) {
+ /* Context might be partially freed. This happens when destroying the window framebuffers. */
+ glDeleteFramebuffers(1, &fbo_id_);
+ }
+ else {
+ context_->fbo_free(fbo_id_);
+ }
+ /* Restore default framebuffer if this framebuffer was bound. */
+ if (context_->active_fb == this && context_->back_left != this) {
+ /* If this assert triggers it means the framebuffer is being freed while in use by another
+ * context which, by the way, is TOTALLY UNSAFE!!! */
+ BLI_assert(context_ == GPU_context_active_get());
+ GPU_framebuffer_restore();
+ }
+}
+
+void GLFrameBuffer::init(void)
+{
+ context_ = static_cast<GLContext *>(GPU_context_active_get());
+ state_manager_ = static_cast<GLStateManager *>(context_->state_manager);
+ glGenFramebuffers(1, &fbo_id_);
+
+#ifndef __APPLE__
+ if ((G.debug & G_DEBUG_GPU) && (GLEW_VERSION_4_3 || GLEW_KHR_debug)) {
+ char sh_name[64];
+ SNPRINTF(sh_name, "FrameBuffer-%s", name_);
+ /* Binding before setting the label is needed on some drivers. */
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo_id_);
+ glObjectLabel(GL_FRAMEBUFFER, fbo_id_, -1, sh_name);
+ }
+#endif
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Config
+ * \{ */
+
+/* This is a rather slow operation. Don't check in normal cases. */
+bool GLFrameBuffer::check(char err_out[256])
+{
+ this->bind(true);
+
+ GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+
+#define FORMAT_STATUS(X) \
+ case X: { \
+ err = #X; \
+ break; \
+ }
+
+ const char *err;
+ switch (status) {
+ FORMAT_STATUS(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT);
+ FORMAT_STATUS(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT);
+ FORMAT_STATUS(GL_FRAMEBUFFER_UNSUPPORTED);
+ FORMAT_STATUS(GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER);
+ FORMAT_STATUS(GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER);
+ FORMAT_STATUS(GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE);
+ FORMAT_STATUS(GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS);
+ FORMAT_STATUS(GL_FRAMEBUFFER_UNDEFINED);
+ case GL_FRAMEBUFFER_COMPLETE:
+ return true;
+ default:
+ err = "unknown";
+ break;
+ }
+
+#undef FORMAT_STATUS
+
+ const char *format = "GPUFrameBuffer: framebuffer status %s\n";
+
+ if (err_out) {
+ BLI_snprintf(err_out, 256, format, err);
+ }
+ else {
+ fprintf(stderr, format, err);
+ }
+
+ return false;
+}
+
+void GLFrameBuffer::update_attachments(void)
+{
+ /* Default framebuffers cannot have attachements. */
+ BLI_assert(immutable_ == false);
+
+ /* First color texture OR the depth texture if no color is attached.
+ * Used to determine framebuffer colorspace and dimensions. */
+ GPUAttachmentType first_attachment = GPU_FB_MAX_ATTACHEMENT;
+ /* NOTE: Inverse iteration to get the first color texture. */
+ for (GPUAttachmentType type = GPU_FB_MAX_ATTACHEMENT - 1; type >= 0; --type) {
+ GPUAttachment &attach = attachments_[type];
+ GLenum gl_attachment = to_gl(type);
+
+ if (type >= GPU_FB_COLOR_ATTACHMENT0) {
+ gl_attachments_[type - GPU_FB_COLOR_ATTACHMENT0] = (attach.tex) ? gl_attachment : GL_NONE;
+ first_attachment = (attach.tex) ? type : first_attachment;
+ }
+ else if (first_attachment == GPU_FB_MAX_ATTACHEMENT) {
+ /* Only use depth texture to get infos if there is no color attachment. */
+ first_attachment = (attach.tex) ? type : first_attachment;
+ }
+
+ if (attach.tex == NULL) {
+ glFramebufferTexture(GL_FRAMEBUFFER, gl_attachment, 0, 0);
+ continue;
+ }
+ GLuint gl_tex = GPU_texture_opengl_bindcode(attach.tex);
+ if (attach.layer > -1 && GPU_texture_cube(attach.tex) && !GPU_texture_array(attach.tex)) {
+ /* Could be avoided if ARB_direct_state_access is required. In this case
+ * glFramebufferTextureLayer would bind the correct face. */
+ GLenum gl_target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + attach.layer;
+ glFramebufferTexture2D(GL_FRAMEBUFFER, gl_attachment, gl_target, gl_tex, attach.mip);
+ }
+ else if (attach.layer > -1) {
+ glFramebufferTextureLayer(GL_FRAMEBUFFER, gl_attachment, gl_tex, attach.mip, attach.layer);
+ }
+ else {
+ /* The whole texture level is attached. The framebuffer is potentially layered. */
+ glFramebufferTexture(GL_FRAMEBUFFER, gl_attachment, gl_tex, attach.mip);
+ }
+ /* We found one depth buffer type. Stop here, otherwise we would
+ * override it by setting GPU_FB_DEPTH_ATTACHMENT */
+ if (type == GPU_FB_DEPTH_STENCIL_ATTACHMENT) {
+ break;
+ }
+ }
+
+ if (GPU_unused_fb_slot_workaround()) {
+ /* Fill normally un-occupied slots to avoid rendering artifacts on some hardware. */
+ GLuint gl_tex = 0;
+ /* NOTE: Inverse iteration to get the first color texture. */
+ for (int i = ARRAY_SIZE(gl_attachments_) - 1; i >= 0; --i) {
+ GPUAttachmentType type = GPU_FB_COLOR_ATTACHMENT0 + i;
+ GPUAttachment &attach = attachments_[type];
+ if (attach.tex != NULL) {
+ gl_tex = GPU_texture_opengl_bindcode(attach.tex);
+ }
+ else if (gl_tex != 0) {
+ GLenum gl_attachment = to_gl(type);
+ gl_attachments_[i] = gl_attachment;
+ glFramebufferTexture(GL_FRAMEBUFFER, gl_attachment, gl_tex, 0);
+ }
+ }
+ }
+
+ if (first_attachment != GPU_FB_MAX_ATTACHEMENT) {
+ GPUAttachment &attach = attachments_[first_attachment];
+ int size[3];
+ GPU_texture_get_mipmap_size(attach.tex, attach.mip, size);
+ this->size_set(size[0], size[1]);
+ srgb_ = (GPU_texture_format(attach.tex) == GPU_SRGB8_A8);
+ }
+
+ dirty_attachments_ = false;
+
+ glDrawBuffers(ARRAY_SIZE(gl_attachments_), gl_attachments_);
+
+ if (G.debug & G_DEBUG_GPU) {
+ BLI_assert(this->check(NULL));
+ }
+}
+
+void GLFrameBuffer::apply_state(void)
+{
+ if (dirty_state_ == false) {
+ return;
+ }
+
+ glViewport(UNPACK4(viewport_));
+ glScissor(UNPACK4(scissor_));
+
+ if (scissor_test_) {
+ glEnable(GL_SCISSOR_TEST);
+ }
+ else {
+ glDisable(GL_SCISSOR_TEST);
+ }
+
+ dirty_state_ = false;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Binding
+ * \{ */
+
+void GLFrameBuffer::bind(bool enabled_srgb)
+{
+ if (!immutable_ && fbo_id_ == 0) {
+ this->init();
+ }
+
+ if (context_ != GPU_context_active_get()) {
+ BLI_assert(!"Trying to use the same framebuffer in multiple context");
+ return;
+ }
+
+ if (context_->active_fb != this) {
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo_id_);
+ /* Internal framebuffers have only one color output and needs to be set everytime. */
+ if (immutable_ && fbo_id_ == 0) {
+ glDrawBuffer(gl_attachments_[0]);
+ }
+ }
+
+ if (dirty_attachments_) {
+ this->update_attachments();
+ this->viewport_reset();
+ this->scissor_reset();
+ }
+
+ if (context_->active_fb != this) {
+ context_->active_fb = this;
+ state_manager_->active_fb = this;
+ dirty_state_ = true;
+
+ if (enabled_srgb) {
+ glEnable(GL_FRAMEBUFFER_SRGB);
+ }
+ else {
+ glDisable(GL_FRAMEBUFFER_SRGB);
+ }
+
+ GPU_shader_set_framebuffer_srgb_target(enabled_srgb && srgb_);
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Operations.
+ * \{ */
+
+void GLFrameBuffer::clear(eGPUFrameBufferBits buffers,
+ const float clear_col[4],
+ float clear_depth,
+ uint clear_stencil)
+{
+ BLI_assert(GPU_context_active_get() == context_);
+ BLI_assert(context_->active_fb == this);
+
+ /* 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) {
+ 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) {
+ GPU_depth_mask(true);
+ glClearDepth(clear_depth);
+ }
+ if (buffers & GPU_STENCIL_BIT) {
+ GPU_stencil_write_mask_set(0xFFu);
+ GPU_stencil_test(GPU_STENCIL_ALWAYS);
+ glClearStencil(clear_stencil);
+ }
+
+ context_->state_manager->apply_state();
+
+ GLbitfield mask = 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);
+ }
+}
+
+void GLFrameBuffer::clear_multi(const float (*clear_cols)[4])
+{
+ BLI_assert(GPU_context_active_get() == context_);
+ BLI_assert(context_->active_fb == this);
+
+ /* Save and restore the state. */
+ eGPUWriteMask write_mask = GPU_write_mask_get();
+ GPU_color_mask(true, true, true, true);
+
+ context_->state_manager->apply_state();
+
+ /* WATCH: This can easilly access clear_cols out of bounds it clear_cols is not big enough for
+ * all attachments.
+ * TODO(fclem) fix this insecurity? */
+ int type = GPU_FB_COLOR_ATTACHMENT0;
+ for (int i = 0; type < GPU_FB_MAX_ATTACHEMENT; i++, type++) {
+ if (attachments_[type].tex != NULL) {
+ glClearBufferfv(GL_COLOR, i, clear_cols[i]);
+ }
+ }
+
+ GPU_write_mask(write_mask);
+}
+
+void GLFrameBuffer::read(eGPUFrameBufferBits plane,
+ eGPUDataFormat data_format,
+ const int area[4],
+ int channel_len,
+ int slot,
+ void *r_data)
+{
+ GLenum format, type, mode;
+ mode = gl_attachments_[slot];
+ type = to_gl(data_format);
+
+ switch (plane) {
+ case GPU_DEPTH_BIT:
+ format = GL_DEPTH_COMPONENT;
+ break;
+ case GPU_COLOR_BIT:
+ format = channel_len_to_gl(channel_len);
+ /* TODO: needed for selection buffers to work properly, this should be handled better. */
+ if (format == GL_RED && type == GL_UNSIGNED_INT) {
+ format = GL_RED_INTEGER;
+ }
+ break;
+ case GPU_STENCIL_BIT:
+ fprintf(stderr, "GPUFramebuffer: Error: Trying to read stencil bit. Unsupported.");
+ return;
+ default:
+ fprintf(stderr, "GPUFramebuffer: Error: Trying to read more than one framebuffer plane.");
+ return;
+ }
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_id_);
+ glReadBuffer(mode);
+ glReadPixels(UNPACK4(area), format, type, r_data);
+}
+
+/* Copy src at the give offset inside dst. */
+void GLFrameBuffer::blit_to(
+ eGPUFrameBufferBits planes, int src_slot, FrameBuffer *dst_, int dst_slot, int x, int y)
+{
+ GLFrameBuffer *src = this;
+ GLFrameBuffer *dst = static_cast<GLFrameBuffer *>(dst_);
+
+ /* Framebuffers must be up to date. This simplify this function. */
+ if (src->dirty_attachments_) {
+ src->bind(true);
+ }
+ if (dst->dirty_attachments_) {
+ dst->bind(true);
+ }
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, src->fbo_id_);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dst->fbo_id_);
+
+ if (planes & GPU_COLOR_BIT) {
+ BLI_assert(src->immutable_ == false || src_slot == 0);
+ BLI_assert(dst->immutable_ == false || dst_slot == 0);
+ BLI_assert(src->gl_attachments_[src_slot] != GL_NONE);
+ BLI_assert(dst->gl_attachments_[dst_slot] != GL_NONE);
+ glReadBuffer(src->gl_attachments_[src_slot]);
+ glDrawBuffer(dst->gl_attachments_[dst_slot]);
+ }
+
+ context_->state_manager->apply_state();
+
+ int w = src->width_;
+ int h = src->height_;
+ GLbitfield mask = to_gl(planes);
+ glBlitFramebuffer(0, 0, w, h, x, y, x + w, y + h, mask, GL_NEAREST);
+
+ if (!dst->immutable_) {
+ /* Restore the draw buffers. */
+ glDrawBuffers(ARRAY_SIZE(dst->gl_attachments_), dst->gl_attachments_);
+ }
+}
+
+/** \} */
+
+} // namespace blender::gpu \ No newline at end of file
diff --git a/source/blender/gpu/opengl/gl_framebuffer.hh b/source/blender/gpu/opengl/gl_framebuffer.hh
new file mode 100644
index 00000000000..8d386116159
--- /dev/null
+++ b/source/blender/gpu/opengl/gl_framebuffer.hh
@@ -0,0 +1,148 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup gpu
+ *
+ * Encapsulation of Framebuffer states (attached textures, viewport, scissors).
+ */
+
+#pragma once
+
+#include "MEM_guardedalloc.h"
+
+#include "glew-mx.h"
+
+#include "gpu_framebuffer_private.hh"
+
+namespace blender::gpu {
+
+class GLStateManager;
+
+/**
+ * Implementation of FrameBuffer object using OpenGL.
+ **/
+class GLFrameBuffer : public FrameBuffer {
+ private:
+ /** OpenGL handle. */
+ GLuint fbo_id_ = 0;
+ /** Context the handle is from. Framebuffers are not shared accros contexts. */
+ GLContext *context_ = NULL;
+ /** State Manager of the same contexts. */
+ GLStateManager *state_manager_ = NULL;
+ /** Copy of the GL state. Contains ONLY color attachments enums for slot binding. */
+ GLenum gl_attachments_[GPU_FB_MAX_COLOR_ATTACHMENT];
+ /** Internal framebuffers are immutable. */
+ bool immutable_;
+ /** True is the framebuffer has it's first color target using the GPU_SRGB8_A8 format. */
+ bool srgb_;
+
+ public:
+ /**
+ * Create a conventional framebuffer to attach texture to.
+ **/
+ GLFrameBuffer(const char *name);
+
+ /**
+ * Special Framebuffer encapsulating internal window framebuffer.
+ * (i.e.: GL_FRONT_LEFT, GL_BACK_RIGHT, ...)
+ * @param ctx context the handle is from.
+ * @param target the internal GL name (i.e: GL_BACK_LEFT).
+ * @param fbo the (optional) already created object for some implementation. Default is 0.
+ * @param w buffer width.
+ * @param h buffer height.
+ **/
+ GLFrameBuffer(const char *name, GLContext *ctx, GLenum target, GLuint fbo, int w, int h);
+
+ ~GLFrameBuffer();
+
+ void bind(bool enabled_srgb) override;
+
+ bool check(char err_out[256]) override;
+
+ void clear(eGPUFrameBufferBits buffers,
+ const float clear_col[4],
+ float clear_depth,
+ uint clear_stencil) override;
+ void clear_multi(const float (*clear_cols)[4]) override;
+
+ void read(eGPUFrameBufferBits planes,
+ eGPUDataFormat format,
+ const int area[4],
+ int channel_len,
+ int slot,
+ void *r_data) override;
+
+ void blit_to(eGPUFrameBufferBits planes,
+ int src_slot,
+ FrameBuffer *dst,
+ int dst_slot,
+ int dst_offset_x,
+ int dst_offset_y) override;
+
+ void apply_state(void);
+
+ private:
+ void init(void);
+ void update_attachments(void);
+ void update_drawbuffers(void);
+
+ MEM_CXX_CLASS_ALLOC_FUNCS("GLFrameBuffer");
+};
+
+/* -------------------------------------------------------------------- */
+/** \name Enums Conversion
+ * \{ */
+
+static inline GLenum to_gl(const GPUAttachmentType type)
+{
+#define ATTACHMENT(X) \
+ case GPU_FB_##X: { \
+ return GL_##X; \
+ } \
+ ((void)0)
+
+ switch (type) {
+ ATTACHMENT(DEPTH_ATTACHMENT);
+ ATTACHMENT(DEPTH_STENCIL_ATTACHMENT);
+ ATTACHMENT(COLOR_ATTACHMENT0);
+ ATTACHMENT(COLOR_ATTACHMENT1);
+ ATTACHMENT(COLOR_ATTACHMENT2);
+ ATTACHMENT(COLOR_ATTACHMENT3);
+ ATTACHMENT(COLOR_ATTACHMENT4);
+ ATTACHMENT(COLOR_ATTACHMENT5);
+ default:
+ BLI_assert(0);
+ return GL_COLOR_ATTACHMENT0;
+ }
+#undef ATTACHMENT
+}
+
+static inline GLbitfield to_gl(const eGPUFrameBufferBits bits)
+{
+ GLbitfield mask = 0;
+ mask |= (bits & GPU_DEPTH_BIT) ? GL_DEPTH_BUFFER_BIT : 0;
+ mask |= (bits & GPU_STENCIL_BIT) ? GL_STENCIL_BUFFER_BIT : 0;
+ mask |= (bits & GPU_COLOR_BIT) ? GL_COLOR_BUFFER_BIT : 0;
+ return mask;
+}
+
+/** \} */
+
+} // namespace blender::gpu
diff --git a/source/blender/gpu/opengl/gl_immediate.cc b/source/blender/gpu/opengl/gl_immediate.cc
new file mode 100644
index 00000000000..7f12f41a598
--- /dev/null
+++ b/source/blender/gpu/opengl/gl_immediate.cc
@@ -0,0 +1,194 @@
+/*
+ * 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
+ *
+ * Mimics old style opengl immediate mode drawing.
+ */
+
+#include "BKE_global.h"
+
+#include "gpu_context_private.hh"
+#include "gpu_shader_private.hh"
+#include "gpu_vertex_format_private.h"
+
+#include "gl_context.hh"
+#include "gl_debug.hh"
+#include "gl_primitive.hh"
+#include "gl_vertex_array.hh"
+
+#include "gl_immediate.hh"
+
+namespace blender::gpu {
+
+/* -------------------------------------------------------------------- */
+/** \name Creation & Deletion
+ * \{ */
+
+GLImmediate::GLImmediate()
+{
+ glGenVertexArrays(1, &vao_id_);
+ glBindVertexArray(vao_id_); /* Necessary for glObjectLabel. */
+
+ buffer.buffer_size = DEFAULT_INTERNAL_BUFFER_SIZE;
+ glGenBuffers(1, &buffer.vbo_id);
+ glBindBuffer(GL_ARRAY_BUFFER, buffer.vbo_id);
+ glBufferData(GL_ARRAY_BUFFER, buffer.buffer_size, NULL, GL_DYNAMIC_DRAW);
+
+ buffer_strict.buffer_size = DEFAULT_INTERNAL_BUFFER_SIZE;
+ glGenBuffers(1, &buffer_strict.vbo_id);
+ glBindBuffer(GL_ARRAY_BUFFER, buffer_strict.vbo_id);
+ glBufferData(GL_ARRAY_BUFFER, buffer_strict.buffer_size, NULL, GL_DYNAMIC_DRAW);
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindVertexArray(0);
+
+#ifndef __APPLE__
+ if ((G.debug & G_DEBUG_GPU) && (GLEW_VERSION_4_3 || GLEW_KHR_debug)) {
+ glObjectLabel(GL_VERTEX_ARRAY, vao_id_, -1, "VAO-Immediate");
+ glObjectLabel(GL_BUFFER, buffer.vbo_id, -1, "VBO-ImmediateBuffer");
+ glObjectLabel(GL_BUFFER, buffer_strict.vbo_id, -1, "VBO-ImmediateBufferStrict");
+ }
+#endif
+}
+
+GLImmediate::~GLImmediate()
+{
+ glDeleteVertexArrays(1, &vao_id_);
+
+ glDeleteBuffers(1, &buffer.vbo_id);
+ glDeleteBuffers(1, &buffer_strict.vbo_id);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Buffer management
+ * \{ */
+
+uchar *GLImmediate::begin()
+{
+ /* How many bytes do we need for this draw call? */
+ const size_t bytes_needed = vertex_buffer_size(&vertex_format, vertex_len);
+ /* Does the current buffer have enough room? */
+ const size_t available_bytes = buffer_size() - buffer_offset();
+
+ GL_CHECK_RESOURCES("Immediate");
+ GL_CHECK_ERROR("Immediate Pre-Begin");
+
+ glBindBuffer(GL_ARRAY_BUFFER, vbo_id());
+
+ bool recreate_buffer = false;
+ if (bytes_needed > buffer_size()) {
+ /* expand the internal buffer */
+ buffer_size() = bytes_needed;
+ recreate_buffer = true;
+ }
+ else if (bytes_needed < DEFAULT_INTERNAL_BUFFER_SIZE &&
+ buffer_size() > DEFAULT_INTERNAL_BUFFER_SIZE) {
+ /* shrink the internal buffer */
+ buffer_size() = DEFAULT_INTERNAL_BUFFER_SIZE;
+ recreate_buffer = true;
+ }
+
+ /* ensure vertex data is aligned */
+ /* Might waste a little space, but it's safe. */
+ const uint pre_padding = padding(buffer_offset(), vertex_format.stride);
+
+ if (!recreate_buffer && ((bytes_needed + pre_padding) <= available_bytes)) {
+ buffer_offset() += pre_padding;
+ }
+ else {
+ /* orphan this buffer & start with a fresh one */
+ glBufferData(GL_ARRAY_BUFFER, buffer_size(), NULL, GL_DYNAMIC_DRAW);
+ buffer_offset() = 0;
+ }
+
+#ifndef NDEBUG
+ {
+ GLint bufsize;
+ glGetBufferParameteriv(GL_ARRAY_BUFFER, GL_BUFFER_SIZE, &bufsize);
+ BLI_assert(buffer_offset() + bytes_needed <= bufsize);
+ }
+#endif
+
+ GLbitfield access = GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT;
+ if (!strict_vertex_len) {
+ access |= GL_MAP_FLUSH_EXPLICIT_BIT;
+ }
+ void *data = glMapBufferRange(GL_ARRAY_BUFFER, buffer_offset(), bytes_needed, access);
+ BLI_assert(data != NULL);
+ GL_CHECK_ERROR("Immediate Post-Begin");
+
+ bytes_mapped_ = bytes_needed;
+ return (uchar *)data;
+}
+
+void GLImmediate::end(void)
+{
+ BLI_assert(prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
+
+ uint buffer_bytes_used = bytes_mapped_;
+ if (!strict_vertex_len) {
+ if (vertex_idx != vertex_len) {
+ vertex_len = vertex_idx;
+ buffer_bytes_used = vertex_buffer_size(&vertex_format, vertex_len);
+ /* unused buffer bytes are available to the next immBegin */
+ }
+ /* tell OpenGL what range was modified so it doesn't copy the whole mapped range */
+ glFlushMappedBufferRange(GL_ARRAY_BUFFER, 0, buffer_bytes_used);
+ }
+ glUnmapBuffer(GL_ARRAY_BUFFER);
+
+ GL_CHECK_ERROR("Immediate Post-Unmap");
+
+ if (vertex_len > 0) {
+ GPU_context_active_get()->state_manager->apply_state();
+
+ /* We convert the offset in vertex offset from the buffer's start.
+ * This works because we added some padding to align the first vertex vertex. */
+ uint v_first = buffer_offset() / vertex_format.stride;
+ GLVertArray::update_bindings(
+ vao_id_, v_first, &vertex_format, reinterpret_cast<Shader *>(shader)->interface);
+
+ /* Update matrices. */
+ GPU_shader_bind(shader);
+
+#ifdef __APPLE__
+ glDisable(GL_PRIMITIVE_RESTART);
+#endif
+ glDrawArrays(to_gl(prim_type), 0, vertex_len);
+#ifdef __APPLE__
+ glEnable(GL_PRIMITIVE_RESTART);
+#endif
+ /* These lines are causing crash on startup on some old GPU + drivers.
+ * They are not required so just comment them. (T55722) */
+ // glBindBuffer(GL_ARRAY_BUFFER, 0);
+ // glBindVertexArray(0);
+
+ GL_CHECK_ERROR("Immediate Post-drawing");
+ }
+
+ buffer_offset() += buffer_bytes_used;
+}
+
+/** \} */
+
+} // namespace blender::gpu \ No newline at end of file
diff --git a/source/blender/gpu/opengl/gl_immediate.hh b/source/blender/gpu/opengl/gl_immediate.hh
new file mode 100644
index 00000000000..2b9b90d692b
--- /dev/null
+++ b/source/blender/gpu/opengl/gl_immediate.hh
@@ -0,0 +1,81 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2016 by Mike Erwin.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup gpu
+ *
+ * Mimics old style opengl immediate mode drawing.
+ */
+
+#pragma once
+
+#include "MEM_guardedalloc.h"
+
+#include "glew-mx.h"
+
+#include "gpu_immediate_private.hh"
+
+namespace blender::gpu {
+
+/* size of internal buffer */
+#define DEFAULT_INTERNAL_BUFFER_SIZE (4 * 1024 * 1024)
+
+class GLImmediate : public Immediate {
+ private:
+ /* Use two buffers for strict and unstrict vertex count to
+ * avoid some huge driver slowdown (see T70922).
+ * Use accessor functions to get / modify. */
+ struct {
+ /** Opengl Handle for this buffer. */
+ GLuint vbo_id = 0;
+ /** Offset of the mapped data in data. */
+ size_t buffer_offset = 0;
+ /** Size of the whole buffer in bytes. */
+ size_t buffer_size = 0;
+ } buffer, buffer_strict;
+ /** Size in bytes of the mapped region. */
+ size_t bytes_mapped_ = 0;
+ /** Vertex array for this immediate mode instance. */
+ GLuint vao_id_ = 0;
+
+ public:
+ GLImmediate();
+ ~GLImmediate();
+
+ uchar *begin(void) override;
+ void end(void) override;
+
+ private:
+ GLuint &vbo_id(void)
+ {
+ return strict_vertex_len ? buffer_strict.vbo_id : buffer.vbo_id;
+ };
+
+ size_t &buffer_offset(void)
+ {
+ return strict_vertex_len ? buffer_strict.buffer_offset : buffer.buffer_offset;
+ };
+
+ size_t &buffer_size(void)
+ {
+ return strict_vertex_len ? buffer_strict.buffer_size : buffer.buffer_size;
+ };
+};
+
+} // namespace blender::gpu \ No newline at end of file
diff --git a/source/blender/gpu/opengl/gl_primitive.hh b/source/blender/gpu/opengl/gl_primitive.hh
new file mode 100644
index 00000000000..7cd0654bc2c
--- /dev/null
+++ b/source/blender/gpu/opengl/gl_primitive.hh
@@ -0,0 +1,65 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup gpu
+ *
+ * Encapsulation of Framebuffer states (attached textures, viewport, scissors).
+ */
+
+#pragma once
+
+#include "BLI_assert.h"
+
+#include "GPU_primitive.h"
+
+#include "glew-mx.h"
+
+namespace blender::gpu {
+
+static inline GLenum to_gl(GPUPrimType prim_type)
+{
+ BLI_assert(prim_type != GPU_PRIM_NONE);
+ switch (prim_type) {
+ default:
+ case GPU_PRIM_POINTS:
+ return GL_POINTS;
+ case GPU_PRIM_LINES:
+ return GL_LINES;
+ case GPU_PRIM_LINE_STRIP:
+ return GL_LINE_STRIP;
+ case GPU_PRIM_LINE_LOOP:
+ return GL_LINE_LOOP;
+ case GPU_PRIM_TRIS:
+ return GL_TRIANGLES;
+ case GPU_PRIM_TRI_STRIP:
+ return GL_TRIANGLE_STRIP;
+ case GPU_PRIM_TRI_FAN:
+ return GL_TRIANGLE_FAN;
+
+ case GPU_PRIM_LINES_ADJ:
+ return GL_LINES_ADJACENCY;
+ case GPU_PRIM_LINE_STRIP_ADJ:
+ return GL_LINE_STRIP_ADJACENCY;
+ case GPU_PRIM_TRIS_ADJ:
+ return GL_TRIANGLES_ADJACENCY;
+ };
+}
+
+} // namespace blender::gpu
diff --git a/source/blender/gpu/opengl/gl_shader.cc b/source/blender/gpu/opengl/gl_shader.cc
new file mode 100644
index 00000000000..17058a6a5a7
--- /dev/null
+++ b/source/blender/gpu/opengl/gl_shader.cc
@@ -0,0 +1,456 @@
+/*
+ * 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_platform.h"
+
+#include "gl_shader.hh"
+#include "gl_shader_interface.hh"
+
+using namespace blender;
+using namespace blender::gpu;
+
+/* -------------------------------------------------------------------- */
+/** \name Creation / Destruction
+ * \{ */
+
+GLShader::GLShader(const char *name) : Shader(name)
+{
+#if 0 /* Would be nice to have, but for now the Deferred compilation \
+ * does not have a GPUContext. */
+ BLI_assert(GPU_context_active_get() != NULL);
+#endif
+ shader_program_ = glCreateProgram();
+
+#ifndef __APPLE__
+ if ((G.debug & G_DEBUG_GPU) && (GLEW_VERSION_4_3 || GLEW_KHR_debug)) {
+ char sh_name[64];
+ SNPRINTF(sh_name, "ShaderProgram-%s", name);
+ glObjectLabel(GL_PROGRAM, shader_program_, -1, sh_name);
+ }
+#endif
+}
+
+GLShader::~GLShader(void)
+{
+#if 0 /* Would be nice to have, but for now the Deferred compilation \
+ * does not have a GPUContext. */
+ BLI_assert(GPU_context_active_get() != NULL);
+#endif
+ /* Invalid handles are silently ignored. */
+ glDeleteShader(vert_shader_);
+ glDeleteShader(geom_shader_);
+ glDeleteShader(frag_shader_);
+ glDeleteProgram(shader_program_);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Shader stage creation
+ * \{ */
+
+char *GLShader::glsl_patch_get(void)
+{
+ /** Used for shader patching. Init once. */
+ static char patch[512] = "\0";
+ if (patch[0] != '\0') {
+ return patch;
+ }
+
+ size_t slen = 0;
+ /* Version need to go first. */
+ STR_CONCAT(patch, slen, "#version 330\n");
+
+ /* Enable extensions for features that are not part of our base GLSL version
+ * don't use an extension for something already available! */
+ if (GLEW_ARB_texture_gather) {
+ /* There is a bug on older Nvidia GPU where GL_ARB_texture_gather
+ * is reported to be supported but yield a compile error (see T55802). */
+ if (!GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY) || GLEW_VERSION_4_0) {
+ STR_CONCAT(patch, slen, "#extension GL_ARB_texture_gather: enable\n");
+
+ /* Some drivers don't agree on GLEW_ARB_texture_gather and the actual support in the
+ * shader so double check the preprocessor define (see T56544). */
+ if (!GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY) && !GLEW_VERSION_4_0) {
+ STR_CONCAT(patch, slen, "#ifdef GL_ARB_texture_gather\n");
+ STR_CONCAT(patch, slen, "# define GPU_ARB_texture_gather\n");
+ STR_CONCAT(patch, slen, "#endif\n");
+ }
+ else {
+ STR_CONCAT(patch, slen, "#define GPU_ARB_texture_gather\n");
+ }
+ }
+ }
+ if (GLEW_ARB_shader_draw_parameters) {
+ STR_CONCAT(patch, slen, "#extension GL_ARB_shader_draw_parameters : enable\n");
+ STR_CONCAT(patch, slen, "#define GPU_ARB_shader_draw_parameters\n");
+ }
+ if (GPU_arb_texture_cube_map_array_is_supported()) {
+ STR_CONCAT(patch, slen, "#extension GL_ARB_texture_cube_map_array : enable\n");
+ STR_CONCAT(patch, slen, "#define GPU_ARB_texture_cube_map_array\n");
+ }
+
+ /* Derivative sign can change depending on implementation. */
+ float derivatives[2];
+ GPU_get_dfdy_factors(derivatives);
+ STR_CONCATF(patch, slen, "#define DFDX_SIGN %1.1f\n", derivatives[0]);
+ STR_CONCATF(patch, slen, "#define DFDY_SIGN %1.1f\n", derivatives[1]);
+
+ BLI_assert(slen < sizeof(patch));
+ return patch;
+}
+
+/* Create, compile and attach the shader stage to the shader program. */
+GLuint GLShader::create_shader_stage(GLenum gl_stage, MutableSpan<const char *> sources)
+{
+ GLuint shader = glCreateShader(gl_stage);
+ if (shader == 0) {
+ fprintf(stderr, "GLShader: Error: Could not create shader object.");
+ return 0;
+ }
+
+ /* Patch the shader code using the first source slot. */
+ sources[0] = glsl_patch_get();
+
+ glShaderSource(shader, sources.size(), sources.data(), NULL);
+ glCompileShader(shader);
+
+ GLint status;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
+ if (!status || (G.debug & G_DEBUG_GPU)) {
+ char log[5000] = "";
+ glGetShaderInfoLog(shader, sizeof(log), NULL, log);
+ if (log[0] != '\0') {
+ switch (gl_stage) {
+ case GL_VERTEX_SHADER:
+ this->print_errors(sources, log, "VertShader");
+ break;
+ case GL_GEOMETRY_SHADER:
+ this->print_errors(sources, log, "GeomShader");
+ break;
+ case GL_FRAGMENT_SHADER:
+ this->print_errors(sources, log, "FragShader");
+ break;
+ }
+ }
+ }
+ if (!status) {
+ glDeleteShader(shader);
+ compilation_failed_ = true;
+ return 0;
+ }
+
+#ifndef __APPLE__
+ if ((G.debug & G_DEBUG_GPU) && (GLEW_VERSION_4_3 || GLEW_KHR_debug)) {
+ char sh_name[64];
+ switch (gl_stage) {
+ case GL_VERTEX_SHADER:
+ BLI_snprintf(sh_name, sizeof(sh_name), "VertShader-%s", name);
+ break;
+ case GL_GEOMETRY_SHADER:
+ BLI_snprintf(sh_name, sizeof(sh_name), "GeomShader-%s", name);
+ break;
+ case GL_FRAGMENT_SHADER:
+ BLI_snprintf(sh_name, sizeof(sh_name), "FragShader-%s", name);
+ break;
+ }
+ glObjectLabel(GL_SHADER, shader, -1, sh_name);
+ }
+#endif
+
+ glAttachShader(shader_program_, shader);
+ return shader;
+}
+
+void GLShader::vertex_shader_from_glsl(MutableSpan<const char *> sources)
+{
+ vert_shader_ = this->create_shader_stage(GL_VERTEX_SHADER, sources);
+}
+
+void GLShader::geometry_shader_from_glsl(MutableSpan<const char *> sources)
+{
+ geom_shader_ = this->create_shader_stage(GL_GEOMETRY_SHADER, sources);
+}
+
+void GLShader::fragment_shader_from_glsl(MutableSpan<const char *> sources)
+{
+ frag_shader_ = this->create_shader_stage(GL_FRAGMENT_SHADER, sources);
+}
+
+bool GLShader::finalize(void)
+{
+ if (compilation_failed_) {
+ return false;
+ }
+
+ glLinkProgram(shader_program_);
+
+ GLint status;
+ glGetProgramiv(shader_program_, GL_LINK_STATUS, &status);
+ if (!status) {
+ char log[5000];
+ glGetProgramInfoLog(shader_program_, sizeof(log), NULL, log);
+ fprintf(stderr, "\nLinking Error:\n\n%s", log);
+ return false;
+ }
+
+ interface = new GLShaderInterface(shader_program_);
+
+ return true;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Binding
+ * \{ */
+
+void GLShader::bind(void)
+{
+ BLI_assert(shader_program_ != 0);
+ glUseProgram(shader_program_);
+}
+
+void GLShader::unbind(void)
+{
+#ifndef NDEBUG
+ glUseProgram(0);
+#endif
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Transform feedback
+ *
+ * TODO(fclem) Should be replaced by compute shaders.
+ * \{ */
+
+/* Should be called before linking. */
+void GLShader::transform_feedback_names_set(Span<const char *> name_list,
+ const eGPUShaderTFBType geom_type)
+{
+ glTransformFeedbackVaryings(
+ shader_program_, name_list.size(), name_list.data(), GL_INTERLEAVED_ATTRIBS);
+ transform_feedback_type_ = geom_type;
+}
+
+bool GLShader::transform_feedback_enable(GPUVertBuf *buf)
+{
+ if (transform_feedback_type_ == GPU_SHADER_TFB_NONE) {
+ return false;
+ }
+
+ BLI_assert(buf->vbo_id != 0);
+
+ glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, buf->vbo_id);
+
+ switch (transform_feedback_type_) {
+ case GPU_SHADER_TFB_POINTS:
+ glBeginTransformFeedback(GL_POINTS);
+ break;
+ case GPU_SHADER_TFB_LINES:
+ glBeginTransformFeedback(GL_LINES);
+ break;
+ case GPU_SHADER_TFB_TRIANGLES:
+ glBeginTransformFeedback(GL_TRIANGLES);
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+void GLShader::transform_feedback_disable(void)
+{
+ glEndTransformFeedback();
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Uniforms setters
+ * \{ */
+
+void GLShader::uniform_float(int location, int comp_len, int array_size, const float *data)
+{
+ switch (comp_len) {
+ case 1:
+ glUniform1fv(location, array_size, data);
+ break;
+ case 2:
+ glUniform2fv(location, array_size, data);
+ break;
+ case 3:
+ glUniform3fv(location, array_size, data);
+ break;
+ case 4:
+ glUniform4fv(location, array_size, data);
+ break;
+ case 9:
+ glUniformMatrix3fv(location, array_size, 0, data);
+ break;
+ case 16:
+ glUniformMatrix4fv(location, array_size, 0, data);
+ break;
+ default:
+ BLI_assert(0);
+ break;
+ }
+}
+
+void GLShader::uniform_int(int location, int comp_len, int array_size, const int *data)
+{
+ switch (comp_len) {
+ case 1:
+ glUniform1iv(location, array_size, data);
+ break;
+ case 2:
+ glUniform2iv(location, array_size, data);
+ break;
+ case 3:
+ glUniform3iv(location, array_size, data);
+ break;
+ case 4:
+ glUniform4iv(location, array_size, data);
+ break;
+ default:
+ BLI_assert(0);
+ break;
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name GPUVertFormat from Shader
+ * \{ */
+
+static uint calc_component_size(const GLenum gl_type)
+{
+ switch (gl_type) {
+ case GL_FLOAT_VEC2:
+ case GL_INT_VEC2:
+ case GL_UNSIGNED_INT_VEC2:
+ return 2;
+ case GL_FLOAT_VEC3:
+ case GL_INT_VEC3:
+ case GL_UNSIGNED_INT_VEC3:
+ return 3;
+ case GL_FLOAT_VEC4:
+ case GL_FLOAT_MAT2:
+ case GL_INT_VEC4:
+ case GL_UNSIGNED_INT_VEC4:
+ return 4;
+ case GL_FLOAT_MAT3:
+ return 9;
+ case GL_FLOAT_MAT4:
+ return 16;
+ case GL_FLOAT_MAT2x3:
+ case GL_FLOAT_MAT3x2:
+ return 6;
+ case GL_FLOAT_MAT2x4:
+ case GL_FLOAT_MAT4x2:
+ return 8;
+ case GL_FLOAT_MAT3x4:
+ case GL_FLOAT_MAT4x3:
+ return 12;
+ default:
+ return 1;
+ }
+}
+
+static void get_fetch_mode_and_comp_type(int gl_type,
+ GPUVertCompType *r_comp_type,
+ GPUVertFetchMode *r_fetch_mode)
+{
+ switch (gl_type) {
+ case GL_FLOAT:
+ case GL_FLOAT_VEC2:
+ case GL_FLOAT_VEC3:
+ case GL_FLOAT_VEC4:
+ case GL_FLOAT_MAT2:
+ case GL_FLOAT_MAT3:
+ case GL_FLOAT_MAT4:
+ case GL_FLOAT_MAT2x3:
+ case GL_FLOAT_MAT2x4:
+ case GL_FLOAT_MAT3x2:
+ case GL_FLOAT_MAT3x4:
+ case GL_FLOAT_MAT4x2:
+ case GL_FLOAT_MAT4x3:
+ *r_comp_type = GPU_COMP_F32;
+ *r_fetch_mode = GPU_FETCH_FLOAT;
+ break;
+ case GL_INT:
+ case GL_INT_VEC2:
+ case GL_INT_VEC3:
+ case GL_INT_VEC4:
+ *r_comp_type = GPU_COMP_I32;
+ *r_fetch_mode = GPU_FETCH_INT;
+ break;
+ case GL_UNSIGNED_INT:
+ case GL_UNSIGNED_INT_VEC2:
+ case GL_UNSIGNED_INT_VEC3:
+ case GL_UNSIGNED_INT_VEC4:
+ *r_comp_type = GPU_COMP_U32;
+ *r_fetch_mode = GPU_FETCH_INT;
+ break;
+ default:
+ BLI_assert(0);
+ }
+}
+
+void GLShader::vertformat_from_shader(GPUVertFormat *format) const
+{
+ GPU_vertformat_clear(format);
+
+ GLint attr_len;
+ glGetProgramiv(shader_program_, GL_ACTIVE_ATTRIBUTES, &attr_len);
+
+ for (int i = 0; i < attr_len; i++) {
+ char name[256];
+ GLenum gl_type;
+ GLint size;
+ glGetActiveAttrib(shader_program_, i, sizeof(name), NULL, &size, &gl_type, name);
+
+ /* Ignore OpenGL names like `gl_BaseInstanceARB`, `gl_InstanceID` and `gl_VertexID`. */
+ if (glGetAttribLocation(shader_program_, name) == -1) {
+ continue;
+ }
+
+ GPUVertCompType comp_type;
+ GPUVertFetchMode fetch_mode;
+ get_fetch_mode_and_comp_type(gl_type, &comp_type, &fetch_mode);
+
+ int comp_len = calc_component_size(gl_type) * size;
+
+ GPU_vertformat_attr_add(format, name, comp_type, comp_len, fetch_mode);
+ }
+}
+
+/** \} */
diff --git a/source/blender/gpu/opengl/gl_shader.hh b/source/blender/gpu/opengl/gl_shader.hh
new file mode 100644
index 00000000000..a686014f4c5
--- /dev/null
+++ b/source/blender/gpu/opengl/gl_shader.hh
@@ -0,0 +1,83 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#pragma once
+
+#include "MEM_guardedalloc.h"
+
+#include "glew-mx.h"
+
+#include "gpu_shader_private.hh"
+
+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;
+
+ public:
+ GLShader(const char *name);
+ ~GLShader();
+
+ /* Return true on success. */
+ void vertex_shader_from_glsl(MutableSpan<const char *> sources) override;
+ void geometry_shader_from_glsl(MutableSpan<const char *> sources) override;
+ void fragment_shader_from_glsl(MutableSpan<const char *> sources) override;
+ bool finalize(void) override;
+
+ void transform_feedback_names_set(Span<const char *> name_list,
+ const eGPUShaderTFBType geom_type) override;
+ bool transform_feedback_enable(GPUVertBuf *buf) override;
+ void transform_feedback_disable(void) override;
+
+ void bind(void) override;
+ void unbind(void) override;
+
+ void uniform_float(int location, int comp_len, int array_size, const float *data) override;
+ void uniform_int(int location, int comp_len, int array_size, const int *data) override;
+
+ void vertformat_from_shader(GPUVertFormat *format) const override;
+
+ private:
+ char *glsl_patch_get(void);
+
+ GLuint create_shader_stage(GLenum gl_stage, MutableSpan<const char *> sources);
+
+ MEM_CXX_CLASS_ALLOC_FUNCS("GLShader");
+};
+
+} // namespace gpu
+} // namespace blender
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..d611efcd975
--- /dev/null
+++ b/source/blender/gpu/opengl/gl_shader_interface.cc
@@ -0,0 +1,299 @@
+/*
+ * 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;
+
+ BLI_assert(ubo_len <= 16 && "enabled_ubo_mask_ is uint16_t");
+
+ /* 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..8cac84a5990
--- /dev/null
+++ b/source/blender/gpu/opengl/gl_shader_interface.hh
@@ -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) 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);
+
+ 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
new file mode 100644
index 00000000000..8f01ff13486
--- /dev/null
+++ b/source/blender/gpu/opengl/gl_state.cc
@@ -0,0 +1,421 @@
+/*
+ * 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
+ */
+
+#include "BLI_math_base.h"
+
+#include "GPU_extensions.h"
+
+#include "glew-mx.h"
+
+#include "gl_context.hh"
+#include "gl_framebuffer.hh"
+#include "gl_state.hh"
+
+using namespace blender::gpu;
+
+/* -------------------------------------------------------------------- */
+/** \name GLStateManager
+ * \{ */
+
+GLStateManager::GLStateManager(void) : GPUStateManager()
+{
+ /* Set other states that never change. */
+ glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
+ glEnable(GL_MULTISAMPLE);
+ glEnable(GL_PRIMITIVE_RESTART);
+
+ glDisable(GL_DITHER);
+
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+
+ glPrimitiveRestartIndex((GLuint)0xFFFFFFFF);
+ /* TODO: Should become default. But needs at least GL 4.3 */
+ if (GLEW_ARB_ES3_compatibility) {
+ /* Takes predecence over GL_PRIMITIVE_RESTART */
+ 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;
+ set_state(state);
+ set_mutable_state(mutable_state);
+}
+
+void GLStateManager::apply_state(void)
+{
+ this->set_state(this->state);
+ this->set_mutable_state(this->mutable_state);
+ active_fb->apply_state();
+};
+
+void GLStateManager::set_state(const GPUState &state)
+{
+ GPUState changed = state ^ current_;
+
+ if (changed.blend != 0) {
+ set_blend((eGPUBlend)state.blend);
+ }
+ if (changed.write_mask != 0) {
+ set_write_mask((eGPUWriteMask)state.write_mask);
+ }
+ if (changed.depth_test != 0) {
+ set_depth_test((eGPUDepthTest)state.depth_test);
+ }
+ if (changed.stencil_test != 0 || changed.stencil_op != 0) {
+ 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((eGPUFaceCullTest)state.culling_test);
+ }
+ if (changed.logic_op_xor != 0) {
+ set_logic_op(state.logic_op_xor);
+ }
+ if (changed.invert_facing != 0) {
+ set_facing(state.invert_facing);
+ }
+ if (changed.provoking_vert != 0) {
+ set_provoking_vert((eGPUProvokingVertex)state.provoking_vert);
+ }
+ if (changed.shadow_bias != 0) {
+ set_shadow_bias(state.shadow_bias);
+ }
+
+ /* TODO remove */
+ if (changed.polygon_smooth) {
+ if (state.polygon_smooth) {
+ glEnable(GL_POLYGON_SMOOTH);
+ }
+ else {
+ glDisable(GL_POLYGON_SMOOTH);
+ }
+ }
+ if (changed.line_smooth) {
+ if (state.line_smooth) {
+ glEnable(GL_LINE_SMOOTH);
+ }
+ else {
+ glDisable(GL_LINE_SMOOTH);
+ }
+ }
+
+ current_ = state;
+}
+
+void GLStateManager::set_mutable_state(const GPUStateMutable &state)
+{
+ GPUStateMutable changed = state ^ current_mutable_;
+
+ /* TODO remove, should be uniform. */
+ if (changed.point_size != 0) {
+ if (state.point_size > 0.0f) {
+ glEnable(GL_PROGRAM_POINT_SIZE);
+ glPointSize(state.point_size);
+ }
+ else {
+ glDisable(GL_PROGRAM_POINT_SIZE);
+ }
+ }
+
+ if (changed.line_width != 0) {
+ /* TODO remove, should use wide line shader. */
+ 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) {
+ /* TODO remove, should modify the projection matrix instead. */
+ glDepthRange(UNPACK2(state.depth_range));
+ }
+
+ if (changed.stencil_compare_mask != 0 || changed.stencil_reference != 0 ||
+ changed.stencil_write_mask != 0) {
+ set_stencil_mask((eGPUStencilTest)current_.stencil_test, state);
+ }
+
+ current_mutable_ = state;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name State set functions
+ * \{ */
+
+void GLStateManager::set_write_mask(const eGPUWriteMask value)
+{
+ glDepthMask((value & GPU_WRITE_DEPTH) != 0);
+ glColorMask((value & GPU_WRITE_RED) != 0,
+ (value & GPU_WRITE_GREEN) != 0,
+ (value & GPU_WRITE_BLUE) != 0,
+ (value & GPU_WRITE_ALPHA) != 0);
+
+ if (value == GPU_WRITE_NONE) {
+ glEnable(GL_RASTERIZER_DISCARD);
+ }
+ else {
+ glDisable(GL_RASTERIZER_DISCARD);
+ }
+}
+
+void GLStateManager::set_depth_test(const eGPUDepthTest value)
+{
+ GLenum func;
+ switch (value) {
+ case GPU_DEPTH_LESS:
+ func = GL_LESS;
+ break;
+ case GPU_DEPTH_LESS_EQUAL:
+ func = GL_LEQUAL;
+ break;
+ case GPU_DEPTH_EQUAL:
+ func = GL_EQUAL;
+ break;
+ case GPU_DEPTH_GREATER:
+ func = GL_GREATER;
+ break;
+ case GPU_DEPTH_GREATER_EQUAL:
+ func = GL_GEQUAL;
+ break;
+ case GPU_DEPTH_ALWAYS:
+ default:
+ func = GL_ALWAYS;
+ break;
+ }
+
+ if (value != GPU_DEPTH_NONE) {
+ glEnable(GL_DEPTH_TEST);
+ glDepthFunc(func);
+ }
+ else {
+ glDisable(GL_DEPTH_TEST);
+ }
+}
+
+void GLStateManager::set_stencil_test(const eGPUStencilTest test, const eGPUStencilOp operation)
+{
+ switch (operation) {
+ case GPU_STENCIL_OP_REPLACE:
+ glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
+ break;
+ case GPU_STENCIL_OP_COUNT_DEPTH_PASS:
+ glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_INCR_WRAP);
+ glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_DECR_WRAP);
+ break;
+ case GPU_STENCIL_OP_COUNT_DEPTH_FAIL:
+ glStencilOpSeparate(GL_BACK, GL_KEEP, GL_DECR_WRAP, GL_KEEP);
+ glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_INCR_WRAP, GL_KEEP);
+ break;
+ case GPU_STENCIL_OP_NONE:
+ default:
+ glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+ }
+
+ if (test != GPU_STENCIL_NONE) {
+ glEnable(GL_STENCIL_TEST);
+ }
+ else {
+ glDisable(GL_STENCIL_TEST);
+ }
+}
+
+void GLStateManager::set_stencil_mask(const eGPUStencilTest test, const GPUStateMutable state)
+{
+ GLenum func;
+ switch (test) {
+ case GPU_STENCIL_NEQUAL:
+ func = GL_NOTEQUAL;
+ break;
+ case GPU_STENCIL_EQUAL:
+ func = GL_EQUAL;
+ break;
+ case GPU_STENCIL_ALWAYS:
+ func = GL_ALWAYS;
+ break;
+ case GPU_STENCIL_NONE:
+ default:
+ glStencilMask(0x00);
+ glStencilFunc(GL_ALWAYS, 0x00, 0x00);
+ return;
+ }
+
+ glStencilMask(state.stencil_write_mask);
+ glStencilFunc(func, state.stencil_reference, state.stencil_compare_mask);
+}
+
+void GLStateManager::set_clip_distances(const int new_dist_len, const int old_dist_len)
+{
+ for (int i = 0; i < new_dist_len; i++) {
+ glEnable(GL_CLIP_DISTANCE0 + i);
+ }
+ for (int i = new_dist_len; i < old_dist_len; i++) {
+ glDisable(GL_CLIP_DISTANCE0 + i);
+ }
+}
+
+void GLStateManager::set_logic_op(const bool enable)
+{
+ if (enable) {
+ glEnable(GL_COLOR_LOGIC_OP);
+ glLogicOp(GL_XOR);
+ }
+ else {
+ glDisable(GL_COLOR_LOGIC_OP);
+ }
+}
+
+void GLStateManager::set_facing(const bool invert)
+{
+ glFrontFace((invert) ? GL_CW : GL_CCW);
+}
+
+void GLStateManager::set_backface_culling(const eGPUFaceCullTest test)
+{
+ if (test != GPU_CULL_NONE) {
+ glEnable(GL_CULL_FACE);
+ glCullFace((test == GPU_CULL_FRONT) ? GL_FRONT : GL_BACK);
+ }
+ else {
+ glDisable(GL_CULL_FACE);
+ }
+}
+
+void GLStateManager::set_provoking_vert(const eGPUProvokingVertex vert)
+{
+ GLenum value = (vert == GPU_VERTEX_FIRST) ? GL_FIRST_VERTEX_CONVENTION :
+ GL_LAST_VERTEX_CONVENTION;
+ glProvokingVertex(value);
+}
+
+void GLStateManager::set_shadow_bias(const bool enable)
+{
+ if (enable) {
+ glEnable(GL_POLYGON_OFFSET_FILL);
+ glEnable(GL_POLYGON_OFFSET_LINE);
+ /* 2.0 Seems to be the lowest possible slope bias that works in every case. */
+ glPolygonOffset(2.0f, 1.0f);
+ }
+ else {
+ glDisable(GL_POLYGON_OFFSET_FILL);
+ glDisable(GL_POLYGON_OFFSET_LINE);
+ }
+}
+
+void GLStateManager::set_blend(const eGPUBlend value)
+{
+ /**
+ * Factors to the equation.
+ * SRC is fragment shader output.
+ * DST is framebuffer color.
+ * final.rgb = SRC.rgb * src_rgb + DST.rgb * dst_rgb;
+ * final.a = SRC.a * src_alpha + DST.a * dst_alpha;
+ **/
+ GLenum src_rgb, src_alpha, dst_rgb, dst_alpha;
+ switch (value) {
+ default:
+ case GPU_BLEND_ALPHA: {
+ src_rgb = GL_SRC_ALPHA;
+ dst_rgb = GL_ONE_MINUS_SRC_ALPHA;
+ src_alpha = GL_ONE;
+ dst_alpha = GL_ONE_MINUS_SRC_ALPHA;
+ break;
+ }
+ case GPU_BLEND_ALPHA_PREMULT: {
+ src_rgb = GL_ONE;
+ dst_rgb = GL_ONE_MINUS_SRC_ALPHA;
+ src_alpha = GL_ONE;
+ dst_alpha = GL_ONE_MINUS_SRC_ALPHA;
+ break;
+ }
+ case GPU_BLEND_ADDITIVE: {
+ /* Do not let alpha accumulate but premult the source RGB by it. */
+ src_rgb = GL_SRC_ALPHA;
+ dst_rgb = GL_ONE;
+ src_alpha = GL_ZERO;
+ dst_alpha = GL_ONE;
+ break;
+ }
+ case GPU_BLEND_SUBTRACT:
+ case GPU_BLEND_ADDITIVE_PREMULT: {
+ /* Let alpha accumulate. */
+ src_rgb = GL_ONE;
+ dst_rgb = GL_ONE;
+ src_alpha = GL_ONE;
+ dst_alpha = GL_ONE;
+ break;
+ }
+ case GPU_BLEND_MULTIPLY: {
+ src_rgb = GL_DST_COLOR;
+ dst_rgb = GL_ZERO;
+ src_alpha = GL_DST_ALPHA;
+ dst_alpha = GL_ZERO;
+ break;
+ }
+ case GPU_BLEND_INVERT: {
+ src_rgb = GL_ONE_MINUS_DST_COLOR;
+ dst_rgb = GL_ZERO;
+ src_alpha = GL_ZERO;
+ dst_alpha = GL_ONE;
+ break;
+ }
+ case GPU_BLEND_OIT: {
+ src_rgb = GL_ONE;
+ dst_rgb = GL_ONE;
+ src_alpha = GL_ZERO;
+ dst_alpha = GL_ONE_MINUS_SRC_ALPHA;
+ break;
+ }
+ case GPU_BLEND_BACKGROUND: {
+ src_rgb = GL_ONE_MINUS_DST_ALPHA;
+ dst_rgb = GL_SRC_ALPHA;
+ src_alpha = GL_ZERO;
+ dst_alpha = GL_SRC_ALPHA;
+ break;
+ }
+ case GPU_BLEND_CUSTOM: {
+ src_rgb = GL_ONE;
+ dst_rgb = GL_SRC1_COLOR;
+ src_alpha = GL_ONE;
+ dst_alpha = GL_SRC1_ALPHA;
+ break;
+ }
+ }
+
+ /* Always set the blend function. This avoid a rendering error when blending is disabled but
+ * GPU_BLEND_CUSTOM was used just before and the framebuffer is using more than 1 color targe */
+ glBlendFuncSeparate(src_rgb, dst_rgb, src_alpha, dst_alpha);
+ if (value != GPU_BLEND_NONE) {
+ glEnable(GL_BLEND);
+ }
+ else {
+ glDisable(GL_BLEND);
+ }
+}
+
+/** \} */
diff --git a/source/blender/gpu/opengl/gl_state.hh b/source/blender/gpu/opengl/gl_state.hh
new file mode 100644
index 00000000000..c25e384fcd7
--- /dev/null
+++ b/source/blender/gpu/opengl/gl_state.hh
@@ -0,0 +1,77 @@
+/*
+ * 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
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_utildefines.h"
+
+#include "gpu_state_private.hh"
+
+#include "glew-mx.h"
+
+namespace blender {
+namespace gpu {
+
+class GLFrameBuffer;
+
+/**
+ * State manager keeping track of the draw state and applying it before drawing.
+ * Opengl Implementation.
+ **/
+class GLStateManager : public GPUStateManager {
+ public:
+ /** Anothter reference to tje active framebuffer. */
+ GLFrameBuffer *active_fb;
+
+ 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();
+
+ void apply_state(void) override;
+
+ private:
+ static void set_write_mask(const eGPUWriteMask value);
+ static void set_depth_test(const eGPUDepthTest value);
+ static void set_stencil_test(const eGPUStencilTest test, const eGPUStencilOp operation);
+ static void set_stencil_mask(const eGPUStencilTest test, const GPUStateMutable state);
+ static void set_clip_distances(const int new_dist_len, const int old_dist_len);
+ static void set_logic_op(const bool enable);
+ static void set_facing(const bool invert);
+ static void set_backface_culling(const eGPUFaceCullTest test);
+ static void set_provoking_vert(const eGPUProvokingVertex vert);
+ static void set_shadow_bias(const bool enable);
+ static void set_blend(const eGPUBlend value);
+
+ void set_state(const GPUState &state);
+ void set_mutable_state(const GPUStateMutable &state);
+
+ MEM_CXX_CLASS_ALLOC_FUNCS("GLStateManager")
+};
+
+} // namespace gpu
+} // namespace blender
diff --git a/source/blender/gpu/opengl/gl_texture.hh b/source/blender/gpu/opengl/gl_texture.hh
new file mode 100644
index 00000000000..c1194941038
--- /dev/null
+++ b/source/blender/gpu/opengl/gl_texture.hh
@@ -0,0 +1,81 @@
+
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup gpu
+ *
+ * GPU Framebuffer
+ * - this is a wrapper for an OpenGL framebuffer object (FBO). in practice
+ * multiple FBO's may be created.
+ * - actual FBO creation & config is deferred until GPU_framebuffer_bind or
+ * GPU_framebuffer_check_valid to allow creation & config while another
+ * opengl context is bound (since FBOs are not shared between ogl contexts).
+ */
+
+#pragma once
+
+#include "BLI_assert.h"
+
+#include "glew-mx.h"
+
+namespace blender {
+namespace gpu {
+
+static GLenum to_gl(eGPUDataFormat format)
+{
+ switch (format) {
+ case GPU_DATA_FLOAT:
+ return GL_FLOAT;
+ case GPU_DATA_INT:
+ return GL_INT;
+ case GPU_DATA_UNSIGNED_INT:
+ return GL_UNSIGNED_INT;
+ case GPU_DATA_UNSIGNED_BYTE:
+ return GL_UNSIGNED_BYTE;
+ case GPU_DATA_UNSIGNED_INT_24_8:
+ return GL_UNSIGNED_INT_24_8;
+ case GPU_DATA_10_11_11_REV:
+ return GL_UNSIGNED_INT_10F_11F_11F_REV;
+ default:
+ BLI_assert(!"Unhandled data format");
+ return GL_FLOAT;
+ }
+}
+
+/* Assume Unorm / Float target. Used with glReadPixels. */
+static GLenum channel_len_to_gl(int channel_len)
+{
+ switch (channel_len) {
+ case 1:
+ return GL_RED;
+ case 2:
+ return GL_RG;
+ case 3:
+ return GL_RGB;
+ case 4:
+ return GL_RGBA;
+ default:
+ BLI_assert(!"Wrong number of texture channels");
+ return GL_RED;
+ }
+}
+
+} // namespace gpu
+} // namespace blender
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..0e0c64e5c60
--- /dev/null
+++ b/source/blender/gpu/opengl/gl_uniform_buffer.cc
@@ -0,0 +1,133 @@
+/*
+ * 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_);
+
+#ifdef DEBUG
+ BLI_assert(slot < 16);
+ static_cast<GLContext *>(GPU_context_active_get())->bound_ubo_slots |= 1 << slot;
+#endif
+}
+
+void GLUniformBuf::unbind(void)
+{
+#ifdef DEBUG
+ /* NOTE: This only unbinds the last bound slot. */
+ glBindBufferBase(GL_UNIFORM_BUFFER, slot_, 0);
+ /* Hope that the context did not change. */
+ static_cast<GLContext *>(GPU_context_active_get())->bound_ubo_slots &= ~(1 << slot_);
+#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..8cd2ab91be9
--- /dev/null
+++ b/source/blender/gpu/opengl/gl_uniform_buffer.hh
@@ -0,0 +1,60 @@
+/*
+ * 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:
+ /** Slot to which this UBO is currently bound. -1 if not bound. */
+ int slot_ = -1;
+ /** OpenGL Object handle. */
+ 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
new file mode 100644
index 00000000000..64d44c39587
--- /dev/null
+++ b/source/blender/gpu/opengl/gl_vertex_array.cc
@@ -0,0 +1,171 @@
+/*
+ * 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
+ */
+
+#include "GPU_glew.h"
+
+#include "GPU_vertex_buffer.h"
+
+#include "gpu_shader_interface.hh"
+#include "gpu_vertex_format_private.h"
+
+#include "gl_batch.hh"
+#include "gl_context.hh"
+
+#include "gl_vertex_array.hh"
+
+namespace blender::gpu {
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex Array Bindings
+ * \{ */
+
+/* Returns enabled vertex pointers as a bitflag (one bit per attrib). */
+static uint16_t vbo_bind(const ShaderInterface *interface,
+ const GPUVertFormat *format,
+ uint v_first,
+ uint v_len,
+ const bool use_instancing)
+{
+ uint16_t enabled_attrib = 0;
+ const uint attr_len = format->attr_len;
+ uint stride = format->stride;
+ uint offset = 0;
+ GLuint divisor = (use_instancing) ? 1 : 0;
+
+ for (uint a_idx = 0; a_idx < attr_len; a_idx++) {
+ const GPUVertAttr *a = &format->attrs[a_idx];
+
+ if (format->deinterleaved) {
+ offset += ((a_idx == 0) ? 0 : format->attrs[a_idx - 1].sz) * v_len;
+ stride = a->sz;
+ }
+ else {
+ offset = a->offset;
+ }
+
+ const GLvoid *pointer = (const GLubyte *)0 + offset + v_first * stride;
+ const GLenum type = convert_comp_type_to_gl(static_cast<GPUVertCompType>(a->comp_type));
+
+ 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 ShaderInput *input = interface->attr_get(name);
+
+ if (input == NULL) {
+ continue;
+ }
+
+ enabled_attrib |= (1 << input->location);
+
+ if (a->comp_len == 16 || a->comp_len == 12 || a->comp_len == 8) {
+ BLI_assert(a->fetch_mode == GPU_FETCH_FLOAT);
+ BLI_assert(a->comp_type == GPU_COMP_F32);
+ for (int i = 0; i < a->comp_len / 4; i++) {
+ glEnableVertexAttribArray(input->location + i);
+ glVertexAttribDivisor(input->location + i, divisor);
+ glVertexAttribPointer(
+ input->location + i, 4, type, GL_FALSE, stride, (const GLubyte *)pointer + i * 16);
+ }
+ }
+ else {
+ glEnableVertexAttribArray(input->location);
+ glVertexAttribDivisor(input->location, divisor);
+
+ switch (a->fetch_mode) {
+ case GPU_FETCH_FLOAT:
+ case GPU_FETCH_INT_TO_FLOAT:
+ glVertexAttribPointer(input->location, a->comp_len, type, GL_FALSE, stride, pointer);
+ break;
+ case GPU_FETCH_INT_TO_FLOAT_UNIT:
+ glVertexAttribPointer(input->location, a->comp_len, type, GL_TRUE, stride, pointer);
+ break;
+ case GPU_FETCH_INT:
+ glVertexAttribIPointer(input->location, a->comp_len, type, stride, pointer);
+ break;
+ }
+ }
+ }
+ }
+ return enabled_attrib;
+}
+
+/* Update the Attrib Binding of the currently bound VAO. */
+void GLVertArray::update_bindings(const GLuint vao,
+ const GPUBatch *batch,
+ const ShaderInterface *interface,
+ const int base_instance)
+{
+ uint16_t attr_mask = interface->enabled_attr_mask_;
+
+ glBindVertexArray(vao);
+
+ /* Reverse order so first VBO'S have more prevalence (in term of attribute override). */
+ for (int v = GPU_BATCH_VBO_MAX_LEN - 1; v > -1; v--) {
+ GPUVertBuf *vbo = batch->verts[v];
+ if (vbo) {
+ GPU_vertbuf_use(vbo);
+ attr_mask &= ~vbo_bind(interface, &vbo->format, 0, vbo->vertex_len, false);
+ }
+ }
+
+ for (int v = GPU_BATCH_INST_VBO_MAX_LEN - 1; v > -1; v--) {
+ GPUVertBuf *vbo = batch->inst[v];
+ if (vbo) {
+ GPU_vertbuf_use(vbo);
+ attr_mask &= ~vbo_bind(interface, &vbo->format, base_instance, vbo->vertex_len, true);
+ }
+ }
+
+ if (attr_mask != 0 && GLEW_ARB_vertex_attrib_binding) {
+ for (uint16_t mask = 1, a = 0; a < 16; a++, mask <<= 1) {
+ if (attr_mask & mask) {
+ GLContext *ctx = static_cast<GLContext *>(GPU_context_active_get());
+ /* This replaces glVertexAttrib4f(a, 0.0f, 0.0f, 0.0f, 1.0f); with a more modern style.
+ * Fix issues for some drivers (see T75069). */
+ glBindVertexBuffer(a, ctx->default_attr_vbo_, (intptr_t)0, (intptr_t)0);
+ glEnableVertexAttribArray(a);
+ glVertexAttribFormat(a, 4, GL_FLOAT, GL_FALSE, 0);
+ glVertexAttribBinding(a, a);
+ }
+ }
+ }
+
+ if (batch->elem) {
+ /* Binds the index buffer. This state is also saved in the VAO. */
+ GPU_indexbuf_use(batch->elem);
+ }
+}
+
+/* Another version of update_bindings for Immediate mode. */
+void GLVertArray::update_bindings(const GLuint vao,
+ const uint v_first,
+ const GPUVertFormat *format,
+ const ShaderInterface *interface)
+{
+ glBindVertexArray(vao);
+
+ vbo_bind(interface, format, v_first, 0, false);
+}
+
+/** \} */
+
+} // 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
new file mode 100644
index 00000000000..7037986e31e
--- /dev/null
+++ b/source/blender/gpu/opengl/gl_vertex_array.hh
@@ -0,0 +1,49 @@
+/*
+ * 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 "glew-mx.h"
+
+#include "GPU_batch.h"
+#include "gl_shader_interface.hh"
+
+namespace blender {
+namespace gpu {
+
+namespace GLVertArray {
+
+void update_bindings(const GLuint vao,
+ const GPUBatch *batch,
+ const ShaderInterface *interface,
+ const int base_instance);
+
+void update_bindings(const GLuint vao,
+ const uint v_first,
+ const GPUVertFormat *format,
+ const ShaderInterface *interface);
+
+} // namespace GLVertArray
+
+} // namespace gpu
+} // namespace blender
diff --git a/source/blender/gpu/shaders/gpu_shader_2D_image_multi_rect_vert.glsl b/source/blender/gpu/shaders/gpu_shader_2D_image_multi_rect_vert.glsl
index d25cd586e65..640ceb97e5b 100644
--- a/source/blender/gpu/shaders/gpu_shader_2D_image_multi_rect_vert.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_2D_image_multi_rect_vert.glsl
@@ -13,34 +13,19 @@ flat out vec4 finalColor;
void main()
{
- /* Rendering 2 triangle per icon. */
- int i = gl_VertexID / 6;
- int v = gl_VertexID % 6;
+ vec4 pos = calls_data[gl_InstanceID * 3];
+ vec4 tex = calls_data[gl_InstanceID * 3 + 1];
+ finalColor = calls_data[gl_InstanceID * 3 + 2];
- vec4 pos = calls_data[i * 3];
- vec4 tex = calls_data[i * 3 + 1];
- finalColor = calls_data[i * 3 + 2];
-
- /* TODO Remove this */
- if (v == 2) {
- v = 4;
- }
- else if (v == 3) {
- v = 0;
- }
- else if (v == 5) {
- v = 2;
+ if (gl_VertexID == 0) {
+ pos.xy = pos.xz;
+ tex.xy = tex.xz;
}
-
- if (v == 0) {
+ else if (gl_VertexID == 1) {
pos.xy = pos.xw;
tex.xy = tex.xw;
}
- else if (v == 1) {
- pos.xy = pos.xz;
- tex.xy = tex.xz;
- }
- else if (v == 2) {
+ else if (gl_VertexID == 2) {
pos.xy = pos.yw;
tex.xy = tex.yw;
}
diff --git a/source/blender/gpu/shaders/gpu_shader_2D_image_rect_vert.glsl b/source/blender/gpu/shaders/gpu_shader_2D_image_rect_vert.glsl
index fcd877a37eb..ab9c30505c2 100644
--- a/source/blender/gpu/shaders/gpu_shader_2D_image_rect_vert.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_2D_image_rect_vert.glsl
@@ -14,13 +14,13 @@ void main()
vec2 uv;
vec2 co;
if (gl_VertexID == 0) {
- co = rect_geom.xw;
- uv = rect_icon.xw;
- }
- else if (gl_VertexID == 1) {
co = rect_geom.xy;
uv = rect_icon.xy;
}
+ else if (gl_VertexID == 1) {
+ co = rect_geom.xw;
+ uv = rect_icon.xw;
+ }
else if (gl_VertexID == 2) {
co = rect_geom.zw;
uv = rect_icon.zw;
diff --git a/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl b/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl
index d15f48c8f8a..fb512a1f00e 100644
--- a/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl
@@ -57,9 +57,14 @@ in float dummy;
vec2 do_widget(void)
{
+ /* Offset to avoid loosing pixels (mimics conservative rasterization). */
+ const vec2 ofs = vec2(0.5, -0.5);
lineWidth = abs(rect.x - recti.x);
vec2 emboss_ofs = vec2(0.0, -lineWidth);
- vec2 v_pos[4] = vec2[4](rect.xz + emboss_ofs, rect.xw, rect.yz + emboss_ofs, rect.yw);
+ vec2 v_pos[4] = vec2[4](rect.xz + emboss_ofs + ofs.yy,
+ rect.xw + ofs.yx,
+ rect.yz + emboss_ofs + ofs.xy,
+ rect.yw + ofs.xx);
vec2 pos = v_pos[gl_VertexID];
uvInterp = pos - rect.xz;
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_output_world.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_output_world.glsl
index 27ca96501ae..5eb853a4c1a 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_output_world.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_output_world.glsl
@@ -6,7 +6,8 @@ void node_output_world(Closure surface, Closure volume, out Closure result)
float alpha = renderPassEnvironment ? 1.0 : backgroundAlpha;
result = CLOSURE_DEFAULT;
result.radiance = surface.radiance * alpha;
- result.transmittance = vec3(1.0 - alpha);
+ result.transmittance = vec3(0.0);
+ result.holdout = (1.0 - alpha);
#else
result = volume;
#endif /* VOLUMETRICS */
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/exporter/abc_export_capi.cc b/source/blender/io/alembic/exporter/abc_export_capi.cc
index 8c5f3d89870..6412379c126 100644
--- a/source/blender/io/alembic/exporter/abc_export_capi.cc
+++ b/source/blender/io/alembic/exporter/abc_export_capi.cc
@@ -67,11 +67,14 @@ namespace io {
namespace alembic {
// Construct the depsgraph for exporting.
-static void build_depsgraph(Depsgraph *depsgraph, Main *bmain)
+static void build_depsgraph(Depsgraph *depsgraph, const bool visible_objects_only)
{
- Scene *scene = DEG_get_input_scene(depsgraph);
- ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph);
- DEG_graph_build_from_view_layer(depsgraph, bmain, scene, view_layer);
+ if (visible_objects_only) {
+ DEG_graph_build_from_view_layer(depsgraph);
+ }
+ else {
+ DEG_graph_build_for_all_objects(depsgraph);
+ }
}
static void export_startjob(void *customdata,
@@ -91,7 +94,7 @@ static void export_startjob(void *customdata,
*progress = 0.0f;
*do_update = true;
- build_depsgraph(data->depsgraph, data->bmain);
+ build_depsgraph(data->depsgraph, data->params.visible_objects_only);
SubdivModifierDisabler subdiv_disabler(data->depsgraph);
if (!data->params.apply_subdiv) {
subdiv_disabler.disable_modifiers();
@@ -150,7 +153,7 @@ static void export_startjob(void *customdata,
// Update the scene for the next frame to render.
scene->r.cfra = static_cast<int>(frame);
scene->r.subframe = frame - scene->r.cfra;
- BKE_scene_graph_update_for_newframe(data->depsgraph, data->bmain);
+ BKE_scene_graph_update_for_newframe(data->depsgraph);
CLOG_INFO(&LOG, 2, "Exporting frame %.2f", frame);
ExportSubset export_subset = abc_archive->export_subset_for_frame(frame);
@@ -171,7 +174,7 @@ static void export_startjob(void *customdata,
// Finish up by going back to the keyframe that was current before we started.
if (CFRA != orig_frame) {
CFRA = orig_frame;
- BKE_scene_graph_update_for_newframe(data->depsgraph, data->bmain);
+ BKE_scene_graph_update_for_newframe(data->depsgraph);
}
data->export_ok = !data->was_canceled;
diff --git a/source/blender/io/alembic/exporter/abc_writer_abstract.cc b/source/blender/io/alembic/exporter/abc_writer_abstract.cc
index e43b394e27f..84527a12e85 100644
--- a/source/blender/io/alembic/exporter/abc_writer_abstract.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_abstract.cc
@@ -25,6 +25,10 @@
#include "DNA_modifier_types.h"
+#include "DEG_depsgraph.h"
+
+#include <Alembic/AbcGeom/Visibility.h>
+
#include "CLG_log.h"
static CLG_LogRef LOG = {"io.alembic"};
@@ -96,6 +100,18 @@ void ABCAbstractWriter::update_bounding_box(Object *object)
bounding_box_.max.z = -bb->vec[0][1];
}
+void ABCAbstractWriter::write_visibility(const HierarchyContext &context)
+{
+ const bool is_visible = context.is_object_visible(DAG_EVAL_RENDER);
+ Alembic::Abc::OObject abc_object = get_alembic_object();
+
+ if (!abc_visibility_.valid()) {
+ abc_visibility_ = Alembic::AbcGeom::CreateVisibilityProperty(abc_object, timesample_index_);
+ }
+ abc_visibility_.set(is_visible ? Alembic::AbcGeom::kVisibilityVisible :
+ Alembic::AbcGeom::kVisibilityHidden);
+}
+
} // namespace alembic
} // namespace io
} // namespace blender
diff --git a/source/blender/io/alembic/exporter/abc_writer_abstract.h b/source/blender/io/alembic/exporter/abc_writer_abstract.h
index a83373a567a..f46409b7902 100644
--- a/source/blender/io/alembic/exporter/abc_writer_abstract.h
+++ b/source/blender/io/alembic/exporter/abc_writer_abstract.h
@@ -43,6 +43,9 @@ class ABCAbstractWriter : public AbstractHierarchyWriter {
uint32_t timesample_index_;
Imath::Box3d bounding_box_;
+ /* Visibility of this writer's data in Alembic. */
+ Alembic::Abc::OCharProperty abc_visibility_;
+
public:
explicit ABCAbstractWriter(const ABCWriterConstructorArgs &args);
virtual ~ABCAbstractWriter();
@@ -70,6 +73,8 @@ class ABCAbstractWriter : public AbstractHierarchyWriter {
virtual void do_write(HierarchyContext &context) = 0;
virtual void update_bounding_box(Object *object);
+
+ void write_visibility(const HierarchyContext &context);
};
} // namespace alembic
diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.cc b/source/blender/io/alembic/exporter/abc_writer_mesh.cc
index 89cb76db9a6..517f0212712 100644
--- a/source/blender/io/alembic/exporter/abc_writer_mesh.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_mesh.cc
@@ -159,27 +159,10 @@ ModifierData *ABCGenericMeshWriter::get_liquid_sim_modifier(Scene *scene, Object
bool ABCGenericMeshWriter::is_supported(const HierarchyContext *context) const
{
- Object *object = context->object;
- bool is_dupli = context->duplicator != nullptr;
- int base_flag;
-
- if (is_dupli) {
- /* Construct the object's base flags from its dupli-parent, just like is done in
- * deg_objects_dupli_iterator_next(). Without this, the visibility check below will fail. Doing
- * this here, instead of a more suitable location in AbstractHierarchyIterator, prevents
- * copying the Object for every dupli. */
- base_flag = object->base_flag;
- object->base_flag = context->duplicator->base_flag | BASE_FROM_DUPLI;
+ if (args_.export_params->visible_objects_only) {
+ return context->is_object_visible(DAG_EVAL_RENDER);
}
-
- int visibility = BKE_object_visibility(
- object, DAG_EVAL_RENDER /* TODO(Sybren): add evaluation mode to export options? */);
-
- if (is_dupli) {
- object->base_flag = base_flag;
- }
-
- return (visibility & OB_VISIBLE_SELF) != 0;
+ return true;
}
void ABCGenericMeshWriter::do_write(HierarchyContext &context)
diff --git a/source/blender/io/alembic/exporter/abc_writer_transform.cc b/source/blender/io/alembic/exporter/abc_writer_transform.cc
index 39af99c142c..7694066a13d 100644
--- a/source/blender/io/alembic/exporter/abc_writer_transform.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_transform.cc
@@ -92,6 +92,8 @@ void ABCTransformWriter::do_write(HierarchyContext &context)
xform_sample.setMatrix(convert_matrix_datatype(parent_relative_matrix));
xform_sample.setInheritsXforms(true);
abc_xform_schema_.set(xform_sample);
+
+ write_visibility(context);
}
const OObject ABCTransformWriter::get_alembic_object() const
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/io/common/IO_abstract_hierarchy_iterator.h b/source/blender/io/common/IO_abstract_hierarchy_iterator.h
index d0d9d72b880..1d78cc38746 100644
--- a/source/blender/io/common/IO_abstract_hierarchy_iterator.h
+++ b/source/blender/io/common/IO_abstract_hierarchy_iterator.h
@@ -37,6 +37,8 @@
#include "IO_dupli_persistent_id.hh"
+#include "DEG_depsgraph.h"
+
#include <map>
#include <set>
#include <string>
@@ -111,6 +113,8 @@ struct HierarchyContext {
bool is_instance() const;
void mark_as_instance_of(const std::string &reference_export_path);
void mark_as_not_instanced();
+
+ bool is_object_visible(const enum eEvaluationMode evaluation_mode) const;
};
/* Abstract writer for objects. Create concrete subclasses to write to USD, Alembic, etc.
diff --git a/source/blender/io/common/intern/abstract_hierarchy_iterator.cc b/source/blender/io/common/intern/abstract_hierarchy_iterator.cc
index fbefc8c8e7e..d825625cafc 100644
--- a/source/blender/io/common/intern/abstract_hierarchy_iterator.cc
+++ b/source/blender/io/common/intern/abstract_hierarchy_iterator.cc
@@ -28,6 +28,7 @@
#include "BKE_anim_data.h"
#include "BKE_duplilist.h"
#include "BKE_key.h"
+#include "BKE_object.h"
#include "BKE_particle.h"
#include "BLI_assert.h"
@@ -77,6 +78,29 @@ void HierarchyContext::mark_as_not_instanced()
original_export_path.clear();
}
+bool HierarchyContext::is_object_visible(const enum eEvaluationMode evaluation_mode) const
+{
+ const bool is_dupli = duplicator != nullptr;
+ int base_flag;
+
+ if (is_dupli) {
+ /* Construct the object's base flags from its dupli-parent, just like is done in
+ * deg_objects_dupli_iterator_next(). Without this, the visibility check below will fail. Doing
+ * this here, instead of a more suitable location in AbstractHierarchyIterator, prevents
+ * copying the Object for every dupli. */
+ base_flag = object->base_flag;
+ object->base_flag = duplicator->base_flag | BASE_FROM_DUPLI;
+ }
+
+ const int visibility = BKE_object_visibility(object, evaluation_mode);
+
+ if (is_dupli) {
+ object->base_flag = base_flag;
+ }
+
+ return (visibility & OB_VISIBLE_SELF) != 0;
+}
+
EnsuredWriter::EnsuredWriter() : writer_(nullptr), newly_created_(false)
{
}
diff --git a/source/blender/io/common/intern/abstract_hierarchy_iterator_test.cc b/source/blender/io/common/intern/abstract_hierarchy_iterator_test.cc
index cbb2e63a99f..27196994a3c 100644
--- a/source/blender/io/common/intern/abstract_hierarchy_iterator_test.cc
+++ b/source/blender/io/common/intern/abstract_hierarchy_iterator_test.cc
@@ -20,8 +20,11 @@
#include "tests/blendfile_loading_base_test.h"
+#include "BKE_scene.h"
#include "BLI_math.h"
+#include "BLO_readfile.h"
#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_build.h"
#include "DNA_object_types.h"
#include <map>
@@ -99,7 +102,7 @@ class TestingHierarchyIterator : public AbstractHierarchyIterator {
}
};
-class USDHierarchyIteratorTest : public BlendfileLoadingBaseTest {
+class AbstractHierarchyIteratorTest : public BlendfileLoadingBaseTest {
protected:
TestingHierarchyIterator *iterator;
@@ -131,7 +134,7 @@ class USDHierarchyIteratorTest : public BlendfileLoadingBaseTest {
}
};
-TEST_F(USDHierarchyIteratorTest, ExportHierarchyTest)
+TEST_F(AbstractHierarchyIteratorTest, ExportHierarchyTest)
{
/* Load the test blend file. */
if (!blendfile_load("usd/usd_hierarchy_export_test.blend")) {
@@ -206,7 +209,7 @@ TEST_F(USDHierarchyIteratorTest, ExportHierarchyTest)
EXPECT_EQ(expected_data, iterator->data_writers);
}
-TEST_F(USDHierarchyIteratorTest, ExportSubsetTest)
+TEST_F(AbstractHierarchyIteratorTest, ExportSubsetTest)
{
// The scene has no hair or particle systems, and this is already covered by ExportHierarchyTest,
// so not included here. Update this test when hair & particle systems are included.
@@ -317,4 +320,44 @@ TEST_F(USDHierarchyIteratorTest, ExportSubsetTest)
EXPECT_EQ(expected_transforms, iterator->transform_writers);
EXPECT_EQ(expected_data, iterator->data_writers);
}
+
+/* Test class that constructs a depsgraph in such a way that it includes invisible objects. */
+class AbstractHierarchyIteratorInvisibleTest : public AbstractHierarchyIteratorTest {
+ protected:
+ void depsgraph_create(eEvaluationMode depsgraph_evaluation_mode) override
+ {
+ depsgraph = DEG_graph_new(
+ bfile->main, bfile->curscene, bfile->cur_view_layer, depsgraph_evaluation_mode);
+ DEG_graph_build_for_all_objects(depsgraph);
+ BKE_scene_graph_update_tagged(depsgraph, bfile->main);
+ }
+};
+
+TEST_F(AbstractHierarchyIteratorInvisibleTest, ExportInvisibleTest)
+{
+ if (!blendfile_load("alembic/visibility.blend")) {
+ return;
+ }
+ depsgraph_create(DAG_EVAL_RENDER);
+ iterator_create();
+
+ iterator->iterate_and_write();
+
+ // Mapping from object name to set of export paths.
+ used_writers expected_transforms = {{"OBInvisibleAnimatedCube", {"/InvisibleAnimatedCube"}},
+ {"OBInvisibleCube", {"/InvisibleCube"}},
+ {"OBVisibleCube", {"/VisibleCube"}}};
+ EXPECT_EQ(expected_transforms, iterator->transform_writers);
+
+ used_writers expected_data = {{"OBInvisibleAnimatedCube", {"/InvisibleAnimatedCube/Cube"}},
+ {"OBInvisibleCube", {"/InvisibleCube/Cube"}},
+ {"OBVisibleCube", {"/VisibleCube/Cube"}}};
+
+ EXPECT_EQ(expected_data, iterator->data_writers);
+
+ // The scene has no hair or particle systems.
+ EXPECT_EQ(0, iterator->hair_writers.size());
+ EXPECT_EQ(0, iterator->particle_writers.size());
+}
+
} // namespace blender::io
diff --git a/source/blender/io/usd/intern/usd_capi.cc b/source/blender/io/usd/intern/usd_capi.cc
index 98aef62f38e..52075728e3e 100644
--- a/source/blender/io/usd/intern/usd_capi.cc
+++ b/source/blender/io/usd/intern/usd_capi.cc
@@ -20,6 +20,7 @@
#include "usd.h"
#include "usd_hierarchy_iterator.h"
+#include <pxr/base/plug/registry.h>
#include <pxr/pxr.h>
#include <pxr/usd/usd/stage.h>
#include <pxr/usd/usdGeom/tokens.h>
@@ -32,6 +33,7 @@
#include "DNA_scene_types.h"
+#include "BKE_appdir.h"
#include "BKE_blender_version.h"
#include "BKE_context.h"
#include "BKE_global.h"
@@ -59,6 +61,21 @@ struct ExportJobData {
bool export_ok;
};
+static void ensure_usd_plugin_path_registered(void)
+{
+ static bool plugin_path_registered = false;
+ if (plugin_path_registered) {
+ return;
+ }
+ plugin_path_registered = true;
+
+ /* Tell USD which directory to search for its JSON files. If 'datafiles/usd'
+ * does not exist, the USD library will not be able to read or write any files. */
+ const std::string blender_usd_datafiles = BKE_appdir_folder_id(BLENDER_DATAFILES, "usd");
+ /* The trailing slash indicates to the USD library that the path is a directory. */
+ pxr::PlugRegistry::GetInstance().RegisterPlugins(blender_usd_datafiles + "/");
+}
+
static void export_startjob(void *customdata,
/* Cannot be const, this function implements wm_jobs_start_callback.
* NOLINTNEXTLINE: readability-non-const-parameter. */
@@ -75,8 +92,12 @@ static void export_startjob(void *customdata,
// Construct the depsgraph for exporting.
Scene *scene = DEG_get_input_scene(data->depsgraph);
- ViewLayer *view_layer = DEG_get_input_view_layer(data->depsgraph);
- DEG_graph_build_from_view_layer(data->depsgraph, data->bmain, scene, view_layer);
+ if (data->params.visible_objects_only) {
+ DEG_graph_build_from_view_layer(data->depsgraph);
+ }
+ else {
+ DEG_graph_build_for_all_objects(data->depsgraph);
+ }
BKE_scene_graph_update_tagged(data->depsgraph, data->bmain);
*progress = 0.0f;
@@ -122,7 +143,7 @@ static void export_startjob(void *customdata,
// Update the scene for the next frame to render.
scene->r.cfra = static_cast<int>(frame);
scene->r.subframe = frame - scene->r.cfra;
- BKE_scene_graph_update_for_newframe(data->depsgraph, data->bmain);
+ BKE_scene_graph_update_for_newframe(data->depsgraph);
iter.set_export_frame(frame);
iter.iterate_and_write();
@@ -142,7 +163,7 @@ static void export_startjob(void *customdata,
// Finish up by going back to the keyframe that was current before we started.
if (CFRA != orig_frame) {
CFRA = orig_frame;
- BKE_scene_graph_update_for_newframe(data->depsgraph, data->bmain);
+ BKE_scene_graph_update_for_newframe(data->depsgraph);
}
data->export_ok = true;
@@ -176,6 +197,8 @@ bool USD_export(bContext *C,
ViewLayer *view_layer = CTX_data_view_layer(C);
Scene *scene = CTX_data_scene(C);
+ blender::io::usd::ensure_usd_plugin_path_registered();
+
blender::io::usd::ExportJobData *job = static_cast<blender::io::usd::ExportJobData *>(
MEM_mallocN(sizeof(blender::io::usd::ExportJobData), "ExportJobData"));
diff --git a/source/blender/io/usd/intern/usd_writer_abstract.cc b/source/blender/io/usd/intern/usd_writer_abstract.cc
index a416941fb4d..4910b7f11dd 100644
--- a/source/blender/io/usd/intern/usd_writer_abstract.cc
+++ b/source/blender/io/usd/intern/usd_writer_abstract.cc
@@ -114,6 +114,20 @@ pxr::UsdShadeMaterial USDAbstractWriter::ensure_usd_material(Material *material)
return usd_material;
}
+void USDAbstractWriter::write_visibility(const HierarchyContext &context,
+ const pxr::UsdTimeCode timecode,
+ pxr::UsdGeomImageable &usd_geometry)
+{
+ pxr::UsdAttribute attr_visibility = usd_geometry.CreateVisibilityAttr(pxr::VtValue(), true);
+
+ const bool is_visible = context.is_object_visible(
+ usd_export_context_.export_params.evaluation_mode);
+ const pxr::TfToken visibility = is_visible ? pxr::UsdGeomTokens->inherited :
+ pxr::UsdGeomTokens->invisible;
+
+ usd_value_writer_.SetAttribute(attr_visibility, pxr::VtValue(visibility), timecode);
+}
+
} // namespace usd
} // namespace io
} // namespace blender
diff --git a/source/blender/io/usd/intern/usd_writer_abstract.h b/source/blender/io/usd/intern/usd_writer_abstract.h
index a689deaf0d8..248bdd22a3b 100644
--- a/source/blender/io/usd/intern/usd_writer_abstract.h
+++ b/source/blender/io/usd/intern/usd_writer_abstract.h
@@ -72,6 +72,10 @@ class USDAbstractWriter : public AbstractHierarchyWriter {
pxr::UsdTimeCode get_export_time_code() const;
pxr::UsdShadeMaterial ensure_usd_material(Material *material);
+
+ void write_visibility(const HierarchyContext &context,
+ const pxr::UsdTimeCode timecode,
+ pxr::UsdGeomImageable &usd_geometry);
};
} // namespace usd
diff --git a/source/blender/io/usd/intern/usd_writer_mesh.cc b/source/blender/io/usd/intern/usd_writer_mesh.cc
index bd2c549e729..75d1ca605d4 100644
--- a/source/blender/io/usd/intern/usd_writer_mesh.cc
+++ b/source/blender/io/usd/intern/usd_writer_mesh.cc
@@ -42,6 +42,8 @@
#include "DNA_object_fluidsim_types.h"
#include "DNA_particle_types.h"
+#include <iostream>
+
namespace blender {
namespace io {
namespace usd {
@@ -52,27 +54,10 @@ USDGenericMeshWriter::USDGenericMeshWriter(const USDExporterContext &ctx) : USDA
bool USDGenericMeshWriter::is_supported(const HierarchyContext *context) const
{
- Object *object = context->object;
- bool is_dupli = context->duplicator != nullptr;
- int base_flag;
-
- if (is_dupli) {
- /* Construct the object's base flags from its dupli-parent, just like is done in
- * deg_objects_dupli_iterator_next(). Without this, the visibility check below will fail. Doing
- * this here, instead of a more suitable location in AbstractHierarchyIterator, prevents
- * copying the Object for every dupli. */
- base_flag = object->base_flag;
- object->base_flag = context->duplicator->base_flag | BASE_FROM_DUPLI;
- }
-
- int visibility = BKE_object_visibility(object,
- usd_export_context_.export_params.evaluation_mode);
-
- if (is_dupli) {
- object->base_flag = base_flag;
+ if (usd_export_context_.export_params.visible_objects_only) {
+ return context->is_object_visible(usd_export_context_.export_params.evaluation_mode);
}
-
- return (visibility & OB_VISIBLE_SELF) != 0;
+ return true;
}
void USDGenericMeshWriter::do_write(HierarchyContext &context)
@@ -169,6 +154,8 @@ void USDGenericMeshWriter::write_mesh(HierarchyContext &context, Mesh *mesh)
const pxr::SdfPath &usd_path = usd_export_context_.usd_path;
pxr::UsdGeomMesh usd_mesh = pxr::UsdGeomMesh::Define(stage, usd_path);
+ write_visibility(context, timecode, usd_mesh);
+
USDMeshData usd_mesh_data;
get_geometry_data(mesh, usd_mesh_data);
diff --git a/source/blender/io/usd/tests/usd_stage_creation_test.cc b/source/blender/io/usd/tests/usd_stage_creation_test.cc
index e6bd0bab6ce..3d28d8f8f5a 100644
--- a/source/blender/io/usd/tests/usd_stage_creation_test.cc
+++ b/source/blender/io/usd/tests/usd_stage_creation_test.cc
@@ -17,6 +17,8 @@
* All rights reserved.
*/
#include "testing/testing.h"
+
+#include <pxr/base/plug/registry.h>
#include <pxr/usd/usd/stage.h>
#include <string>
@@ -26,11 +28,6 @@
#include "BKE_appdir.h"
-extern "C" {
-/* Workaround to make it possible to pass a path at runtime to USD. See creator.c. */
-void usd_initialise_plugin_path(const char *datafiles_usd_path);
-}
-
namespace blender::io::usd {
class USDStageCreationTest : public testing::Test {
@@ -44,9 +41,16 @@ TEST_F(USDStageCreationTest, JSONFileLoadingTest)
}
char usd_datafiles_dir[FILE_MAX];
- BLI_path_join(usd_datafiles_dir, FILE_MAX, release_dir.c_str(), "datafiles", "usd", nullptr);
+ const size_t path_len = BLI_path_join(
+ usd_datafiles_dir, FILE_MAX, release_dir.c_str(), "datafiles", "usd", nullptr);
+
+ /* BLI_path_join removes trailing slashes, but the USD library requires one in order to recognise
+ * the path as directory. */
+ BLI_assert(path_len + 1 < FILE_MAX);
+ usd_datafiles_dir[path_len] = '/';
+ usd_datafiles_dir[path_len + 1] = '\0';
- usd_initialise_plugin_path(usd_datafiles_dir);
+ pxr::PlugRegistry::GetInstance().RegisterPlugins(usd_datafiles_dir);
/* Simply the ability to create a USD Stage for a specific filename means that the extension
* has been recognized by the USD library, and that a USD plugin has been loaded to write such
@@ -61,9 +65,8 @@ TEST_F(USDStageCreationTest, JSONFileLoadingTest)
unlink(filename.c_str());
}
else {
- FAIL() << "unable to find suitable USD plugin to write " << filename
- << "; re-run with the environment variable PXR_PATH_DEBUG non-empty to see which paths "
- "are considered.";
+ FAIL() << "unable to find suitable USD plugin to write " << filename << "; looked in "
+ << usd_datafiles_dir;
}
}
diff --git a/source/blender/io/usd/usd.h b/source/blender/io/usd/usd.h
index f2826cd1d7c..b9ea90736ff 100644
--- a/source/blender/io/usd/usd.h
+++ b/source/blender/io/usd/usd.h
@@ -35,6 +35,7 @@ struct USDExportParams {
bool export_normals;
bool export_materials;
bool selected_objects_only;
+ bool visible_objects_only;
bool use_instancing;
enum eEvaluationMode evaluation_mode;
};
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 5d8eddaf5e1..033a69d230e 100644
--- a/source/blender/makesdna/DNA_brush_types.h
+++ b/source/blender/makesdna/DNA_brush_types.h
@@ -322,6 +322,11 @@ typedef enum eBrushCurvePreset {
BRUSH_CURVE_SMOOTHER = 9,
} eBrushCurvePreset;
+typedef enum eBrushDeformTarget {
+ BRUSH_DEFORM_TARGET_GEOMETRY = 0,
+ BRUSH_DEFORM_TARGET_CLOTH_SIM = 1,
+} eBrushDeformTarget;
+
typedef enum eBrushElasticDeformType {
BRUSH_ELASTIC_DEFORM_GRAB = 0,
BRUSH_ELASTIC_DEFORM_GRAB_BISCALE = 1,
@@ -338,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 {
@@ -387,6 +393,13 @@ typedef enum eBrushBoundaryDeformType {
BRUSH_BOUNDARY_DEFORM_TWIST = 4,
} eBrushBushBoundaryDeformType;
+typedef enum eBrushBoundaryFalloffType {
+ BRUSH_BOUNDARY_FALLOFF_CONSTANT = 0,
+ BRUSH_BOUNDARY_FALLOFF_RADIUS = 1,
+ BRUSH_BOUNDARY_FALLOFF_LOOP = 2,
+ BRUSH_BOUNDARY_FALLOFF_LOOP_INVERT = 3,
+} eBrushBoundaryFalloffType;
+
/* Gpencilsettings.Vertex_mode */
typedef enum eGp_Vertex_Mode {
/* Affect to Stroke only. */
@@ -532,7 +545,7 @@ typedef struct Brush {
/** Source for fill tool color gradient application. */
char gradient_fill_mode;
- char _pad0[1];
+ char _pad0[5];
/** Projection shape (sphere, circle). */
char falloff_shape;
@@ -580,6 +593,8 @@ typedef struct Brush {
/* Maximun distance to search fake neighbors from a vertex. */
float disconnected_distance_max;
+ int deform_target;
+
/* automasking */
int automasking_flags;
int automasking_boundary_edges_propagation_steps;
@@ -596,6 +611,8 @@ typedef struct Brush {
/* boundary */
int boundary_deform_type;
+ int boundary_falloff_type;
+ float boundary_offset;
/* cloth */
int cloth_deform_type;
diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h
index fc50261eb03..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),
@@ -1033,6 +1039,7 @@ typedef enum {
eMultiresModifierFlag_PlainUv_DEPRECATED = (1 << 1),
eMultiresModifierFlag_UseCrease = (1 << 2),
eMultiresModifierFlag_UseCustomNormals = (1 << 3),
+ eMultiresModifierFlag_UseSculptBaseMesh = (1 << 4),
} MultiresModifierFlag;
/* DEPRECATED, only used for versioning. */
diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h
index 6568281a8d4..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). */
@@ -537,8 +524,6 @@ enum {
OB_TRANSFLAG_UNUSED_12 = 1 << 12, /* cleared */
/* runtime constraints disable */
OB_NO_CONSTRAINTS = 1 << 13,
- /* hack to work around particle issue */
- OB_NO_PSYS_UPDATE = 1 << 14,
OB_DUPLI = OB_DUPLIVERTS | OB_DUPLICOLLECTION | OB_DUPLIFACES | OB_DUPLIPARTS,
};
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_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h
index ec64eea0aae..69580da8c5f 100644
--- a/source/blender/makesdna/DNA_scene_defaults.h
+++ b/source/blender/makesdna/DNA_scene_defaults.h
@@ -364,4 +364,3 @@
}
/* clang-format off */
-
diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h
index d5b828c898d..8e4063b36eb 100644
--- a/source/blender/makesdna/DNA_screen_types.h
+++ b/source/blender/makesdna/DNA_screen_types.h
@@ -68,8 +68,6 @@ typedef struct bScreen {
/** User-setting for which editors get redrawn during anim playback. */
short redraws_flag;
- char statusbar_info[256];
-
/** Temp screen in a temp window, don't save (like user prefs). */
char temp;
/** Temp screen for image render display or fileselect. */
@@ -536,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),
@@ -635,13 +632,21 @@ typedef enum eRegionType {
RGN_TYPE_EXECUTE = 10,
RGN_TYPE_FOOTER = 11,
RGN_TYPE_TOOL_HEADER = 12,
+
+#define RGN_TYPE_LEN (RGN_TYPE_TOOL_HEADER + 1)
} eRegionType;
+
/* use for function args */
#define RGN_TYPE_ANY -1
/* Region supports panel tabs (categories). */
#define RGN_TYPE_HAS_CATEGORY_MASK (1 << RGN_TYPE_UI)
+/* Check for any kind of header region. */
+#define RGN_TYPE_IS_HEADER_ANY(regiontype) \
+ (((1 << (regiontype)) & \
+ ((1 << RGN_TYPE_HEADER) | 1 << (RGN_TYPE_TOOL_HEADER) | (1 << RGN_TYPE_FOOTER))) != 0)
+
/** #ARegion.alignment */
enum {
RGN_ALIGN_NONE = 0,
@@ -661,6 +666,7 @@ enum {
/** Mask out flags so we can check the alignment. */
#define RGN_ALIGN_ENUM_FROM_MASK(align) ((align) & ((1 << 4) - 1))
+#define RGN_ALIGN_FLAG_FROM_MASK(align) ((align) & ~((1 << 4) - 1))
/** #ARegion.flag */
enum {
diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h
index 7d77e8478ae..ad1635ba0c0 100644
--- a/source/blender/makesdna/DNA_space_types.h
+++ b/source/blender/makesdna/DNA_space_types.h
@@ -612,6 +612,7 @@ typedef enum eSpaceSeq_Flag {
SEQ_SHOW_SAFE_CENTER = (1 << 9),
SEQ_SHOW_METADATA = (1 << 10),
SEQ_SHOW_MARKERS = (1 << 11), /* show markers region */
+ SEQ_ZOOM_TO_FIT = (1 << 12),
} eSpaceSeq_Flag;
/* SpaceSeq.view */
diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h
index e66a3233dd0..0acc2054472 100644
--- a/source/blender/makesdna/DNA_userdef_types.h
+++ b/source/blender/makesdna/DNA_userdef_types.h
@@ -358,7 +358,7 @@ typedef struct ThemeSpace {
unsigned char path_before[4], path_after[4];
unsigned char path_keyframe_before[4], path_keyframe_after[4];
unsigned char camera_path[4];
- unsigned char _pad1[2];
+ unsigned char _pad1[6];
unsigned char gp_vertex_size;
unsigned char gp_vertex[4], gp_vertex_select[4];
@@ -373,8 +373,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];
@@ -690,16 +688,17 @@ typedef struct UserDef {
int audioformat;
int audiochannels;
- /** Setting for UI scale. */
+ /** Setting for UI scale (fractional), before screen DPI has been applied. */
float ui_scale;
/** Setting for UI line width. */
int ui_line_width;
/** Runtime, full DPI divided by `pixelsize`. */
int dpi;
- /** Runtime, multiplier to scale UI elements based on DPI. */
+ /** Runtime, multiplier to scale UI elements based on DPI (fractional). */
float dpi_fac;
+ /** Runtime, `1.0 / dpi_fac` */
float inv_dpi_fac;
- /** Runtime, line width and point size based on DPI. */
+ /** Runtime, calculated from line-width and point-size based on DPI (rounded to int). */
float pixelsize;
/** Deprecated, for forward compatibility. */
int virtual_pixel;
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/RNA_enum_types.h b/source/blender/makesrna/RNA_enum_types.h
index fb9b62e729a..08442a36c87 100644
--- a/source/blender/makesrna/RNA_enum_types.h
+++ b/source/blender/makesrna/RNA_enum_types.h
@@ -117,6 +117,8 @@ extern const EnumPropertyItem rna_enum_motionpath_bake_location_items[];
extern const EnumPropertyItem rna_enum_event_value_items[];
extern const EnumPropertyItem rna_enum_event_type_items[];
extern const EnumPropertyItem rna_enum_event_type_mask_items[];
+
+extern const EnumPropertyItem rna_enum_operator_type_flag_items[];
extern const EnumPropertyItem rna_enum_operator_return_items[];
extern const EnumPropertyItem rna_enum_operator_property_tags[];
diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt
index 0b43a5a6653..303005a0f9e 100644
--- a/source/blender/makesrna/intern/CMakeLists.txt
+++ b/source/blender/makesrna/intern/CMakeLists.txt
@@ -47,7 +47,6 @@ set(DEFSRC
rna_fluid.c
rna_gpencil.c
rna_gpencil_modifier.c
- rna_hair.c
rna_image.c
rna_key.c
rna_lattice.c
@@ -69,7 +68,6 @@ set(DEFSRC
rna_packedfile.c
rna_palette.c
rna_particle.c
- rna_pointcloud.c
rna_pose.c
rna_render.c
rna_rigidbody.c
@@ -79,7 +77,6 @@ set(DEFSRC
rna_sculpt_paint.c
rna_sequencer.c
rna_shader_fx.c
- rna_simulation.c
rna_sound.c
rna_space.c
rna_speaker.c
@@ -99,6 +96,16 @@ set(DEFSRC
rna_xr.c
)
+if(WITH_EXPERIMENTAL_FEATURES)
+ add_definitions(-DWITH_PARTICLE_NODES)
+ add_definitions(-DWITH_HAIR_NODES)
+ list(APPEND DEFSRC
+ rna_pointcloud.c
+ rna_simulation.c
+ rna_hair.c
+ )
+endif()
+
set(APISRC
rna_action_api.c
rna_animation_api.c
@@ -345,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/makesrna.c b/source/blender/makesrna/intern/makesrna.c
index 779e4363be0..2b1e5b3c702 100644
--- a/source/blender/makesrna/intern/makesrna.c
+++ b/source/blender/makesrna/intern/makesrna.c
@@ -599,7 +599,7 @@ static void rna_float_print(FILE *f, float num)
else if (num == FLT_MAX) {
fprintf(f, "FLT_MAX");
}
- else if ((fabsf(num) < INT64_MAX) && ((int64_t)num == num)) {
+ else if ((fabsf(num) < (float)INT64_MAX) && ((int64_t)num == num)) {
fprintf(f, "%.1ff", num);
}
else {
@@ -4285,7 +4285,9 @@ static RNAProcessItem PROCESS_ITEMS[] = {
{"rna_dynamicpaint.c", NULL, RNA_def_dynamic_paint},
{"rna_fcurve.c", "rna_fcurve_api.c", RNA_def_fcurve},
{"rna_gpencil.c", NULL, RNA_def_gpencil},
+#ifdef WITH_HAIR_NODES
{"rna_hair.c", NULL, RNA_def_hair},
+#endif
{"rna_image.c", "rna_image_api.c", RNA_def_image},
{"rna_key.c", NULL, RNA_def_key},
{"rna_light.c", NULL, RNA_def_light},
@@ -4308,7 +4310,9 @@ static RNAProcessItem PROCESS_ITEMS[] = {
{"rna_packedfile.c", NULL, RNA_def_packedfile},
{"rna_palette.c", NULL, RNA_def_palette},
{"rna_particle.c", NULL, RNA_def_particle},
+#ifdef WITH_PARTICLE_NODES
{"rna_pointcloud.c", NULL, RNA_def_pointcloud},
+#endif
{"rna_pose.c", "rna_pose_api.c", RNA_def_pose},
{"rna_curveprofile.c", NULL, RNA_def_profile},
{"rna_lightprobe.c", NULL, RNA_def_lightprobe},
@@ -4318,7 +4322,9 @@ static RNAProcessItem PROCESS_ITEMS[] = {
{"rna_screen.c", NULL, RNA_def_screen},
{"rna_sculpt_paint.c", NULL, RNA_def_sculpt_paint},
{"rna_sequencer.c", "rna_sequencer_api.c", RNA_def_sequencer},
+#ifdef WITH_PARTICLE_NODES
{"rna_simulation.c", NULL, RNA_def_simulation},
+#endif
{"rna_space.c", "rna_space_api.c", RNA_def_space},
{"rna_speaker.c", NULL, RNA_def_speaker},
{"rna_test.c", NULL, RNA_def_test},
diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c
index e9ca0d577ce..f1c125fcbb9 100644
--- a/source/blender/makesrna/intern/rna_ID.c
+++ b/source/blender/makesrna/intern/rna_ID.c
@@ -251,9 +251,11 @@ short RNA_type_to_ID_code(const StructRNA *type)
if (base_type == &RNA_FreestyleLineStyle) {
return ID_LS;
}
+# ifdef WITH_HAIR_NODES
if (base_type == &RNA_Hair) {
return ID_HA;
}
+# endif
if (base_type == &RNA_Lattice) {
return ID_LT;
}
@@ -287,9 +289,11 @@ short RNA_type_to_ID_code(const StructRNA *type)
if (base_type == &RNA_PaintCurve) {
return ID_PC;
}
+# ifdef WITH_PARTICLE_NODES
if (base_type == &RNA_PointCloud) {
return ID_PT;
}
+# endif
if (base_type == &RNA_LightProbe) {
return ID_LP;
}
@@ -299,9 +303,11 @@ short RNA_type_to_ID_code(const StructRNA *type)
if (base_type == &RNA_Screen) {
return ID_SCR;
}
+# ifdef WITH_PARTICLE_NODES
if (base_type == &RNA_Simulation) {
return ID_SIM;
}
+# endif
if (base_type == &RNA_Sound) {
return ID_SO;
}
@@ -355,7 +361,11 @@ StructRNA *ID_code_to_RNA_type(short idcode)
case ID_GR:
return &RNA_Collection;
case ID_HA:
+# ifdef WITH_HAIR_NODES
return &RNA_Hair;
+# else
+ return &RNA_ID;
+# endif
case ID_IM:
return &RNA_Image;
case ID_KE:
@@ -389,7 +399,11 @@ StructRNA *ID_code_to_RNA_type(short idcode)
case ID_PC:
return &RNA_PaintCurve;
case ID_PT:
+# ifdef WITH_PARTICLE_NODES
return &RNA_PointCloud;
+# else
+ return &RNA_ID;
+# endif
case ID_LP:
return &RNA_LightProbe;
case ID_SCE:
@@ -397,7 +411,11 @@ StructRNA *ID_code_to_RNA_type(short idcode)
case ID_SCR:
return &RNA_Screen;
case ID_SIM:
+# ifdef WITH_PARTICLE_NODES
return &RNA_Simulation;
+# else
+ return &RNA_ID;
+# endif
case ID_SO:
return &RNA_Sound;
case ID_SPK:
diff --git a/source/blender/makesrna/intern/rna_access_compare_override.c b/source/blender/makesrna/intern/rna_access_compare_override.c
index 0a739dcfc5a..ac4553349cc 100644
--- a/source/blender/makesrna/intern/rna_access_compare_override.c
+++ b/source/blender/makesrna/intern/rna_access_compare_override.c
@@ -484,13 +484,13 @@ static bool rna_property_override_operation_apply(Main *bmain,
/* Special case for IDProps, we use default callback then. */
if (prop_dst->magic != RNA_MAGIC) {
override_apply = rna_property_override_apply_default;
- if (prop_src->magic == RNA_MAGIC && prop_src->override_apply != override_apply) {
+ if (prop_src->magic == RNA_MAGIC && !ELEM(prop_src->override_apply, NULL, override_apply)) {
override_apply = NULL;
}
}
else if (prop_src->magic != RNA_MAGIC) {
override_apply = rna_property_override_apply_default;
- if (prop_dst->override_apply != override_apply) {
+ if (!ELEM(prop_dst->override_apply, NULL, override_apply)) {
override_apply = NULL;
}
}
diff --git a/source/blender/makesrna/intern/rna_armature.c b/source/blender/makesrna/intern/rna_armature.c
index 8454d5c125f..155943b3b8d 100644
--- a/source/blender/makesrna/intern/rna_armature.c
+++ b/source/blender/makesrna/intern/rna_armature.c
@@ -1264,6 +1264,7 @@ static void rna_def_edit_bone(BlenderRNA *brna)
prop = RNA_def_property(srna, "head", PROP_FLOAT, PROP_TRANSLATION);
RNA_def_property_float_sdna(prop, NULL, "head");
+ RNA_def_property_ui_range(prop, 0, FLT_MAX, 10, RNA_TRANSLATION_PREC_DEFAULT);
RNA_def_property_array(prop, 3);
RNA_def_property_ui_text(prop, "Head", "Location of head end of the bone");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
@@ -1271,6 +1272,7 @@ static void rna_def_edit_bone(BlenderRNA *brna)
prop = RNA_def_property(srna, "tail", PROP_FLOAT, PROP_TRANSLATION);
RNA_def_property_float_sdna(prop, NULL, "tail");
+ RNA_def_property_ui_range(prop, 0, FLT_MAX, 10, RNA_TRANSLATION_PREC_DEFAULT);
RNA_def_property_array(prop, 3);
RNA_def_property_ui_text(prop, "Tail", "Location of tail end of the bone");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c
index 38916e2a45a..0b923eb5635 100644
--- a/source/blender/makesrna/intern/rna_brush.c
+++ b/source/blender/makesrna/intern/rna_brush.c
@@ -45,6 +45,18 @@ static const EnumPropertyItem prop_direction_items[] = {
{0, NULL, 0, NULL, NULL},
};
+#ifdef RNA_RUNTIME
+static const EnumPropertyItem prop_smooth_direction_items[] = {
+ {0, "SMOOTH", ICON_ADD, "Smooth", "Smooth the surfae"},
+ {BRUSH_DIR_IN,
+ "ENHANCE_DETAILS",
+ ICON_REMOVE,
+ "Enhance Details",
+ "Enhance the surface detail"},
+ {0, NULL, 0, NULL, NULL},
+};
+#endif
+
static const EnumPropertyItem sculpt_stroke_method_items[] = {
{0, "DOTS", 0, "Dots", "Apply paint on each mouse move step"},
{BRUSH_DRAG_DOT, "DRAG_DOT", 0, "Drag Dot", "Allows a single dot to be carefully positioned"},
@@ -527,6 +539,7 @@ static bool rna_BrushCapabilitiesSculpt_has_direction_get(PointerRNA *ptr)
SCULPT_TOOL_DRAW_SHARP,
SCULPT_TOOL_CLAY,
SCULPT_TOOL_CLAY_STRIPS,
+ SCULPT_TOOL_SMOOTH,
SCULPT_TOOL_LAYER,
SCULPT_TOOL_INFLATE,
SCULPT_TOOL_BLOB,
@@ -795,7 +808,8 @@ static const EnumPropertyItem *rna_Brush_direction_itemf(bContext *C,
case SCULPT_TOOL_CLAY:
case SCULPT_TOOL_CLAY_STRIPS:
return prop_direction_items;
-
+ case SCULPT_TOOL_SMOOTH:
+ return prop_smooth_direction_items;
case SCULPT_TOOL_MASK:
switch ((BrushMaskTool)me->mask_tool) {
case BRUSH_MASK_DRAW:
@@ -1967,6 +1981,20 @@ static void rna_def_brush(BlenderRNA *brna)
{0, NULL, 0, NULL, NULL},
};
+ static const EnumPropertyItem brush_deformation_target_items[] = {
+ {BRUSH_DEFORM_TARGET_GEOMETRY,
+ "GEOMETRY",
+ 0,
+ "Geometry",
+ "Brush deformation displaces the vertices of the mesh"},
+ {BRUSH_DEFORM_TARGET_CLOTH_SIM,
+ "CLOTH_SIM",
+ 0,
+ "Cloth Simulation",
+ "Brush deforms the mesh by deforming the constraints of a cloth simulation"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
static const EnumPropertyItem brush_elastic_deform_type_items[] = {
{BRUSH_ELASTIC_DEFORM_GRAB, "GRAB", 0, "Grab", ""},
{BRUSH_ELASTIC_DEFORM_GRAB_BISCALE, "GRAB_BISCALE", 0, "Bi-scale Grab", ""},
@@ -1988,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},
};
@@ -1997,6 +2026,31 @@ static void rna_def_brush(BlenderRNA *brna)
{0, NULL, 0, NULL, NULL},
};
+ static const EnumPropertyItem brush_boundary_falloff_type_items[] = {
+ {BRUSH_BOUNDARY_FALLOFF_CONSTANT,
+ "CONSTANT",
+ 0,
+ "Constant",
+ "Applies the same deformation in the entire boundary"},
+ {BRUSH_BOUNDARY_FALLOFF_RADIUS,
+ "RADIUS",
+ 0,
+ "Brush Radius",
+ "Applies the deformation in a localiced area limited by the brush radius"},
+ {BRUSH_BOUNDARY_FALLOFF_LOOP,
+ "LOOP",
+ 0,
+ "Loop",
+ "Applies the brush falloff in a loop pattern"},
+ {BRUSH_BOUNDARY_FALLOFF_LOOP_INVERT,
+ "LOOP_INVERT",
+ 0,
+ "Loop and Invert",
+ "Applies the fallof radius in a loop pattern, inverting the displacement direction in each "
+ "pattern repetition"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
static const EnumPropertyItem brush_cloth_simulation_area_type_items[] = {
{BRUSH_CLOTH_SIMULATION_AREA_LOCAL,
"LOCAL",
@@ -2170,6 +2224,12 @@ static void rna_def_brush(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Curve Preset", "");
RNA_def_property_update(prop, 0, "rna_Brush_update");
+ prop = RNA_def_property(srna, "deform_target", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, brush_deformation_target_items);
+ RNA_def_property_ui_text(
+ prop, "Deformation Target", "How the deformation of the brush will affect the object");
+ RNA_def_property_update(prop, 0, "rna_Brush_update");
+
prop = RNA_def_property(srna, "elastic_deform_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, brush_elastic_deform_type_items);
RNA_def_property_ui_text(prop, "Deformation", "Deformation type that is used in the brush");
@@ -2194,6 +2254,12 @@ static void rna_def_brush(BlenderRNA *brna)
"Part of the mesh that is going to be simulated when the stroke is active");
RNA_def_property_update(prop, 0, "rna_Brush_update");
+ prop = RNA_def_property(srna, "boundary_falloff_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, brush_boundary_falloff_type_items);
+ RNA_def_property_ui_text(
+ prop, "Boundary Falloff", "How the brush falloff is applied across the boundary");
+ RNA_def_property_update(prop, 0, "rna_Brush_update");
+
prop = RNA_def_property(srna, "smooth_deform_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, brush_smooth_deform_type_items);
RNA_def_property_ui_text(prop, "Deformation", "Deformation type that is used in the brush");
@@ -2545,6 +2611,14 @@ static void rna_def_brush(BlenderRNA *brna)
"Maximum distance to search for disconnected loose parts in the mesh");
RNA_def_property_update(prop, 0, "rna_Brush_update");
+ prop = RNA_def_property(srna, "boundary_offset", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_sdna(prop, NULL, "boundary_offset");
+ RNA_def_property_range(prop, 0.0f, 30.0f);
+ RNA_def_property_ui_text(prop,
+ "Boundary Origin Offset",
+ "Offset of the boundary origin in relation to the brush radius");
+ RNA_def_property_update(prop, 0, "rna_Brush_update");
+
prop = RNA_def_property(srna, "surface_smooth_shape_preservation", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "surface_smooth_shape_preservation");
RNA_def_property_range(prop, 0.0f, 1.0f);
diff --git a/source/blender/makesrna/intern/rna_color.c b/source/blender/makesrna/intern/rna_color.c
index 60b6cc40792..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);
@@ -1222,7 +1222,7 @@ static void rna_def_colormanage(BlenderRNA *brna)
"rna_ColorManagedViewSettings_look_set",
"rna_ColorManagedViewSettings_look_itemf");
RNA_def_property_ui_text(
- prop, "Look", "Additional transform applied before view transform for an artistic needs");
+ prop, "Look", "Additional transform applied before view transform for artistic needs");
RNA_def_property_update(prop, NC_WINDOW, "rna_ColorManagement_update");
prop = RNA_def_property(srna, "view_transform", PROP_ENUM, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c
index c8d16ab65cc..9bcf2b81557 100644
--- a/source/blender/makesrna/intern/rna_gpencil.c
+++ b/source/blender/makesrna/intern/rna_gpencil.c
@@ -106,7 +106,7 @@ static const EnumPropertyItem rna_enum_keyframe_type_items[] = {
};
static const EnumPropertyItem rna_enum_onion_keyframe_type_items[] = {
- {-1, "ALL", ICON_ACTION, "All Types", "Include all Keyframe types"},
+ {-1, "ALL", 0, "All", "Include all Keyframe types"},
{BEZT_KEYTYPE_KEYFRAME,
"KEYFRAME",
ICON_KEYTYPE_KEYFRAME_VEC,
diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h
index 8045279eef2..ad43202d850 100644
--- a/source/blender/makesrna/intern/rna_internal.h
+++ b/source/blender/makesrna/intern/rna_internal.h
@@ -455,10 +455,16 @@ 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_HAIR_NODES
void RNA_def_main_hairs(BlenderRNA *brna, PropertyRNA *cprop);
+#endif
+#ifdef WITH_PARTICLE_NODES
void RNA_def_main_pointclouds(BlenderRNA *brna, PropertyRNA *cprop);
+#endif
void RNA_def_main_volumes(BlenderRNA *brna, PropertyRNA *cprop);
+#ifdef WITH_PARTICLE_NODES
void RNA_def_main_simulations(BlenderRNA *brna, PropertyRNA *cprop);
+#endif
/* ID Properties */
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_main.c b/source/blender/makesrna/intern/rna_main.c
index 97702b06b6f..d83fca69278 100644
--- a/source/blender/makesrna/intern/rna_main.c
+++ b/source/blender/makesrna/intern/rna_main.c
@@ -109,7 +109,9 @@ RNA_MAIN_LISTBASE_FUNCS_DEF(collections)
RNA_MAIN_LISTBASE_FUNCS_DEF(curves)
RNA_MAIN_LISTBASE_FUNCS_DEF(fonts)
RNA_MAIN_LISTBASE_FUNCS_DEF(gpencils)
+# ifdef WITH_HAIR_NODES
RNA_MAIN_LISTBASE_FUNCS_DEF(hairs)
+# endif
RNA_MAIN_LISTBASE_FUNCS_DEF(images)
RNA_MAIN_LISTBASE_FUNCS_DEF(lattices)
RNA_MAIN_LISTBASE_FUNCS_DEF(libraries)
@@ -126,11 +128,15 @@ RNA_MAIN_LISTBASE_FUNCS_DEF(objects)
RNA_MAIN_LISTBASE_FUNCS_DEF(paintcurves)
RNA_MAIN_LISTBASE_FUNCS_DEF(palettes)
RNA_MAIN_LISTBASE_FUNCS_DEF(particles)
+# ifdef WITH_PARTICLE_NODES
RNA_MAIN_LISTBASE_FUNCS_DEF(pointclouds)
+# endif
RNA_MAIN_LISTBASE_FUNCS_DEF(scenes)
RNA_MAIN_LISTBASE_FUNCS_DEF(screens)
RNA_MAIN_LISTBASE_FUNCS_DEF(shapekeys)
+# ifdef WITH_PARTICLE_NODES
RNA_MAIN_LISTBASE_FUNCS_DEF(simulations)
+# endif
RNA_MAIN_LISTBASE_FUNCS_DEF(sounds)
RNA_MAIN_LISTBASE_FUNCS_DEF(speakers)
RNA_MAIN_LISTBASE_FUNCS_DEF(texts)
@@ -384,25 +390,31 @@ void RNA_def_main(BlenderRNA *brna)
"LightProbes",
"LightProbe data-blocks",
RNA_def_main_lightprobes},
+# ifdef WITH_HAIR_NODES
{"hairs", "Hair", "rna_Main_hairs_begin", "Hairs", "Hair data-blocks", RNA_def_main_hairs},
+# endif
+# ifdef WITH_PARTICLE_NODES
{"pointclouds",
"PointCloud",
"rna_Main_pointclouds_begin",
"Point Clouds",
"Point cloud data-blocks",
RNA_def_main_pointclouds},
+# endif
{"volumes",
"Volume",
"rna_Main_volumes_begin",
"Volumes",
"Volume data-blocks",
RNA_def_main_volumes},
+# ifdef WITH_PARTICLE_NODES
{"simulations",
"Simulation",
"rna_Main_simulations_begin",
"Simulations",
"Simulation data-blocks",
RNA_def_main_simulations},
+# endif
{NULL, NULL, NULL, NULL, NULL, NULL},
};
diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c
index 990a5412093..7c941ddb524 100644
--- a/source/blender/makesrna/intern/rna_main_api.c
+++ b/source/blender/makesrna/intern/rna_main_api.c
@@ -708,6 +708,7 @@ static bGPdata *rna_Main_gpencils_new(Main *bmain, const char *name)
return gpd;
}
+# ifdef WITH_HAIR_NODES
static Hair *rna_Main_hairs_new(Main *bmain, const char *name)
{
char safe_name[MAX_ID_NAME - 2];
@@ -717,7 +718,9 @@ static Hair *rna_Main_hairs_new(Main *bmain, const char *name)
id_us_min(&hair->id);
return hair;
}
+# endif
+# ifdef WITH_PARTICLE_NODES
static PointCloud *rna_Main_pointclouds_new(Main *bmain, const char *name)
{
char safe_name[MAX_ID_NAME - 2];
@@ -727,6 +730,7 @@ static PointCloud *rna_Main_pointclouds_new(Main *bmain, const char *name)
id_us_min(&pointcloud->id);
return pointcloud;
}
+# endif
static Volume *rna_Main_volumes_new(Main *bmain, const char *name)
{
@@ -738,6 +742,7 @@ static Volume *rna_Main_volumes_new(Main *bmain, const char *name)
return volume;
}
+# ifdef WITH_PARTICLE_NODES
static Simulation *rna_Main_simulations_new(Main *bmain, const char *name)
{
char safe_name[MAX_ID_NAME - 2];
@@ -747,6 +752,7 @@ static Simulation *rna_Main_simulations_new(Main *bmain, const char *name)
id_us_min(&simulation->id);
return simulation;
}
+# endif
/* tag functions, all the same */
# define RNA_MAIN_ID_TAG_FUNCS_DEF(_func_name, _listbase_name, _id_type) \
@@ -790,10 +796,16 @@ RNA_MAIN_ID_TAG_FUNCS_DEF(cachefiles, cachefiles, ID_CF)
RNA_MAIN_ID_TAG_FUNCS_DEF(paintcurves, paintcurves, ID_PC)
RNA_MAIN_ID_TAG_FUNCS_DEF(workspaces, workspaces, ID_WS)
RNA_MAIN_ID_TAG_FUNCS_DEF(lightprobes, lightprobes, ID_LP)
+# ifdef WITH_HAIR_NODES
RNA_MAIN_ID_TAG_FUNCS_DEF(hairs, hairs, ID_HA)
+# endif
+# ifdef WITH_PARTICLE_NODES
RNA_MAIN_ID_TAG_FUNCS_DEF(pointclouds, pointclouds, ID_PT)
+# endif
RNA_MAIN_ID_TAG_FUNCS_DEF(volumes, volumes, ID_VO)
+# ifdef WITH_PARTICLE_NODES
RNA_MAIN_ID_TAG_FUNCS_DEF(simulations, simulations, ID_SIM)
+# endif
# undef RNA_MAIN_ID_TAG_FUNCS_DEF
@@ -2194,6 +2206,7 @@ void RNA_def_main_lightprobes(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
}
+# ifdef WITH_HAIR_NODES
void RNA_def_main_hairs(BlenderRNA *brna, PropertyRNA *cprop)
{
StructRNA *srna;
@@ -2237,7 +2250,9 @@ void RNA_def_main_hairs(BlenderRNA *brna, PropertyRNA *cprop)
parm = RNA_def_boolean(func, "value", 0, "Value", "");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
}
+# endif
+# ifdef WITH_PARTICLE_NODES
void RNA_def_main_pointclouds(BlenderRNA *brna, PropertyRNA *cprop)
{
StructRNA *srna;
@@ -2284,6 +2299,7 @@ void RNA_def_main_pointclouds(BlenderRNA *brna, PropertyRNA *cprop)
parm = RNA_def_boolean(func, "value", 0, "Value", "");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
}
+# endif
void RNA_def_main_volumes(BlenderRNA *brna, PropertyRNA *cprop)
{
@@ -2329,6 +2345,7 @@ void RNA_def_main_volumes(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
}
+# ifdef WITH_PARTICLE_NODES
void RNA_def_main_simulations(BlenderRNA *brna, PropertyRNA *cprop)
{
StructRNA *srna;
@@ -2368,5 +2385,6 @@ void RNA_def_main_simulations(BlenderRNA *brna, PropertyRNA *cprop)
parm = RNA_def_boolean(func, "value", 0, "Value", "");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
}
+# endif
#endif
diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c
index f95899262d3..d9e151e5f73 100644
--- a/source/blender/makesrna/intern/rna_modifier.c
+++ b/source/blender/makesrna/intern/rna_modifier.c
@@ -723,7 +723,9 @@ static StructRNA *rna_Modifier_refine(struct PointerRNA *ptr)
case eModifierType_WeightedNormal:
return &RNA_WeightedNormalModifier;
case eModifierType_Simulation:
+# ifdef WITH_PARTICLE_NODES
return &RNA_SimulationModifier;
+# endif
/* Default */
case eModifierType_Fluidsim: /* deprecated */
case eModifierType_None:
@@ -1630,6 +1632,7 @@ static void rna_ParticleInstanceModifier_particle_system_set(PointerRNA *ptr,
CLAMP_MIN(psmd->psys, 1);
}
+# ifdef WITH_PARTICLE_NODES
static void rna_SimulationModifier_simulation_update(Main *bmain, Scene *scene, PointerRNA *ptr)
{
SimulationModifierData *smd = ptr->data;
@@ -1672,6 +1675,7 @@ static void rna_SimulationModifier_data_path_set(PointerRNA *ptr, const char *va
smd->data_path = NULL;
}
}
+# endif
/**
* Special set callback that just changes the first bit of the expansion flag.
@@ -2076,6 +2080,14 @@ static void rna_def_modifier_multires(BlenderRNA *brna)
prop, "Use Custom Normals", "Interpolates existing custom normals to resulting mesh");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
+ prop = RNA_def_property(srna, "use_sculpt_base_mesh", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", eMultiresModifierFlag_UseSculptBaseMesh);
+ RNA_def_property_ui_text(prop,
+ "Sculpt Base Mesh",
+ "Make Sculpt Mode tools deform the base mesh while previewing the "
+ "displacement of higher subdivision levels");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
RNA_define_lib_overridable(false);
}
@@ -2807,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");
@@ -2835,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 */
@@ -6999,6 +7027,7 @@ static void rna_def_modifier_weightednormal(BlenderRNA *brna)
RNA_define_lib_overridable(false);
}
+# ifdef WITH_PARTICLE_NODES
static void rna_def_modifier_simulation(BlenderRNA *brna)
{
StructRNA *srna;
@@ -7027,6 +7056,7 @@ static void rna_def_modifier_simulation(BlenderRNA *brna)
RNA_define_lib_overridable(false);
}
+# endif
void RNA_def_modifier(BlenderRNA *brna)
{
@@ -7156,7 +7186,9 @@ void RNA_def_modifier(BlenderRNA *brna)
rna_def_modifier_meshseqcache(brna);
rna_def_modifier_surfacedeform(brna);
rna_def_modifier_weightednormal(brna);
+# ifdef WITH_PARTICLE_NODES
rna_def_modifier_simulation(brna);
+# endif
}
#endif
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_object.c b/source/blender/makesrna/intern/rna_object.c
index 08ca3f16b6d..39e1f17d33d 100644
--- a/source/blender/makesrna/intern/rna_object.c
+++ b/source/blender/makesrna/intern/rna_object.c
@@ -570,9 +570,17 @@ static StructRNA *rna_Object_data_typef(PointerRNA *ptr)
case OB_GPENCIL:
return &RNA_GreasePencil;
case OB_HAIR:
+# ifdef WITH_HAIR_NODES
return &RNA_Hair;
+# else
+ return &RNA_ID;
+# endif
case OB_POINTCLOUD:
+# ifdef WITH_PARTICLE_NODES
return &RNA_PointCloud;
+# else
+ return &RNA_ID;
+# endif
case OB_VOLUME:
return &RNA_Volume;
default:
diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c
index 3b80714bcc5..ab6b60603c7 100644
--- a/source/blender/makesrna/intern/rna_object_api.c
+++ b/source/blender/makesrna/intern/rna_object_api.c
@@ -713,8 +713,9 @@ bool rna_Object_generate_gpencil_strokes(Object *ob,
bContext *C,
ReportList *reports,
Object *ob_gpencil,
- bool gpencil_lines,
- bool use_collections)
+ bool use_collections,
+ float scale_thickness,
+ float sample)
{
if (ob->type != OB_CURVE) {
BKE_reportf(reports,
@@ -726,7 +727,8 @@ bool rna_Object_generate_gpencil_strokes(Object *ob,
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
- BKE_gpencil_convert_curve(bmain, scene, ob_gpencil, ob, gpencil_lines, use_collections, false);
+ BKE_gpencil_convert_curve(
+ bmain, scene, ob_gpencil, ob, use_collections, scale_thickness, sample);
WM_main_add_notifier(NC_GPENCIL | ND_DATA, NULL);
@@ -1190,12 +1192,17 @@ void RNA_api_object(StructRNA *srna)
RNA_def_function_ui_description(func, "Convert a curve object to grease pencil strokes.");
RNA_def_function_flag(func, FUNC_USE_CONTEXT | FUNC_USE_REPORTS);
- parm = RNA_def_pointer(
- func, "ob_gpencil", "Object", "", "Grease Pencil object used to create new strokes");
+ parm = RNA_def_pointer(func,
+ "grease_pencil_object",
+ "Object",
+ "",
+ "Grease Pencil object used to create new strokes");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
- parm = RNA_def_boolean(func, "gpencil_lines", 0, "", "Create Lines");
- parm = RNA_def_boolean(func, "use_collections", 1, "", "Use Collections");
-
+ parm = RNA_def_boolean(func, "use_collections", true, "", "Use Collections");
+ parm = RNA_def_float(
+ func, "scale_thickness", 1.0f, 0.0f, FLT_MAX, "", "Thickness scaling factor", 0.0f, 100.0f);
+ parm = RNA_def_float(
+ func, "sample", 0.0f, 0.0f, FLT_MAX, "", "Sample distance, zero to disable", 0.0f, 100.0f);
parm = RNA_def_boolean(func, "result", 0, "", "Result");
RNA_def_function_return(func, parm);
}
diff --git a/source/blender/makesrna/intern/rna_object_force.c b/source/blender/makesrna/intern/rna_object_force.c
index fa837df682a..4c22890887a 100644
--- a/source/blender/makesrna/intern/rna_object_force.c
+++ b/source/blender/makesrna/intern/rna_object_force.c
@@ -935,7 +935,7 @@ static void rna_def_pointcache_common(StructRNA *srna)
prop = RNA_def_property(srna, "frame_start", PROP_INT, PROP_TIME);
RNA_def_property_int_sdna(prop, NULL, "startframe");
RNA_def_property_range(prop, -MAXFRAME, MAXFRAME);
- RNA_def_property_ui_range(prop, 1, MAXFRAME, 1, 1);
+ RNA_def_property_ui_range(prop, 0, MAXFRAME, 1, 1);
RNA_def_property_ui_text(prop, "Start", "Frame on which the simulation starts");
prop = RNA_def_property(srna, "frame_end", PROP_INT, PROP_TIME);
@@ -1918,7 +1918,7 @@ static void rna_def_softbody(BlenderRNA *brna)
prop = RNA_def_property(srna, "plastic", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "plastic");
RNA_def_property_range(prop, 0.0f, 100.0f);
- RNA_def_property_ui_text(prop, "Plastic", "Permanent deform");
+ RNA_def_property_ui_text(prop, "Plasticity", "Permanent deform");
RNA_def_property_update(prop, 0, "rna_softbody_update");
prop = RNA_def_property(srna, "bend", PROP_FLOAT, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index 262c9f87b66..e470a8ddb85 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -924,6 +924,23 @@ static void rna_Scene_volume_update(Main *UNUSED(bmain), Scene *UNUSED(scene), P
DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_VOLUME | ID_RECALC_SEQUENCER_STRIPS);
}
+static const char *rna_Scene_statistics_string_get(Scene *scene,
+ Main *bmain,
+ ReportList *reports,
+ ViewLayer *view_layer)
+{
+ if (!BKE_scene_has_view_layer(scene, view_layer)) {
+ BKE_reportf(reports,
+ RPT_ERROR,
+ "View Layer '%s' not found in scene '%s'",
+ view_layer->name,
+ scene->id.name + 2);
+ return "";
+ }
+
+ return ED_info_statistics_string(bmain, scene, view_layer);
+}
+
static void rna_Scene_framelen_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr))
{
scene->r.framelen = (float)scene->r.framapto / (float)scene->r.images;
@@ -1866,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;
}
}
@@ -5919,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);
@@ -6465,7 +6481,7 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
prop = RNA_def_property(srna, "simplify_gpencil_shader_fx", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_negative_sdna(prop, NULL, "simplify_gpencil", SIMPLIFY_GPENCIL_FX);
- RNA_def_property_ui_text(prop, "Shaders Effects", "Display Shader Effects");
+ RNA_def_property_ui_text(prop, "Shader Effects", "Display Shader Effects");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
prop = RNA_def_property(srna, "simplify_gpencil_tint", PROP_BOOLEAN, PROP_NONE);
@@ -7277,6 +7293,9 @@ void RNA_def_scene(BlenderRNA *brna)
StructRNA *srna;
PropertyRNA *prop;
+ FunctionRNA *func;
+ PropertyRNA *parm;
+
static const EnumPropertyItem audio_distance_model_items[] = {
{0, "NONE", 0, "None", "No distance attenuation"},
{1, "INVERSE", 0, "Inverse", "Inverse distance model"},
@@ -7668,6 +7687,14 @@ void RNA_def_scene(BlenderRNA *brna)
RNA_def_property_update(prop, NC_SCENE, NULL);
RNA_def_property_update(prop, NC_SCENE, "rna_Scene_volume_update");
+ /* Statistics */
+ func = RNA_def_function(srna, "statistics", "rna_Scene_statistics_string_get");
+ RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_REPORTS);
+ parm = RNA_def_pointer(func, "view_layer", "ViewLayer", "View Layer", "");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ parm = RNA_def_string(func, "statistics", NULL, 0, "Statistics", "");
+ RNA_def_function_return(func, parm);
+
/* Grease Pencil */
prop = RNA_def_property(srna, "grease_pencil", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "gpd");
diff --git a/source/blender/makesrna/intern/rna_scene_api.c b/source/blender/makesrna/intern/rna_scene_api.c
index 06c73fbb19c..ff887e53965 100644
--- a/source/blender/makesrna/intern/rna_scene_api.c
+++ b/source/blender/makesrna/intern/rna_scene_api.c
@@ -72,8 +72,8 @@ 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);
- BKE_scene_graph_update_for_newframe(depsgraph, bmain);
+ Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer);
+ BKE_scene_graph_update_for_newframe(depsgraph);
}
# ifdef WITH_PYTHON
diff --git a/source/blender/makesrna/intern/rna_screen.c b/source/blender/makesrna/intern/rna_screen.c
index fb2a60db0fd..ab84dcb0aba 100644
--- a/source/blender/makesrna/intern/rna_screen.c
+++ b/source/blender/makesrna/intern/rna_screen.c
@@ -288,9 +288,11 @@ static void rna_View2D_view_to_region(
}
}
-static const char *rna_Screen_statusbar_info_get(struct bScreen *screen, Main *bmain, bContext *C)
+static const char *rna_Screen_statusbar_info_get(struct bScreen *UNUSED(screen),
+ Main *bmain,
+ bContext *C)
{
- return ED_info_statusbar_string(bmain, screen, C);
+ return ED_info_statusbar_string(bmain, CTX_data_scene(C), CTX_data_view_layer(C));
}
#else
diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c
index 0cfc6fd569c..629dc104ab5 100644
--- a/source/blender/makesrna/intern/rna_sequencer.c
+++ b/source/blender/makesrna/intern/rna_sequencer.c
@@ -637,6 +637,8 @@ static void rna_Sequence_name_set(PointerRNA *ptr, const char *value)
char oldname[sizeof(seq->name)];
AnimData *adt;
+ BKE_sequencer_prefetch_stop(scene);
+
/* make a copy of the old name first */
BLI_strncpy(oldname, seq->name + 2, sizeof(seq->name) - 2);
diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c
index 155f5ab3043..21ba130f925 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -242,9 +242,9 @@ const EnumPropertyItem rna_enum_space_action_mode_items[] = {
#undef SACT_ITEM_MASK
#undef SACT_ITEM_CACHEFILE
-#define SI_ITEM_VIEW(name, icon) \
+#define SI_ITEM_VIEW(identifier, name, icon) \
{ \
- SI_MODE_VIEW, "VIEW", icon, name, "View the image" \
+ SI_MODE_VIEW, identifier, icon, name, "View the image" \
}
#define SI_ITEM_UV \
{ \
@@ -260,7 +260,7 @@ const EnumPropertyItem rna_enum_space_action_mode_items[] = {
}
const EnumPropertyItem rna_enum_space_image_mode_all_items[] = {
- SI_ITEM_VIEW("View", ICON_FILE_IMAGE),
+ SI_ITEM_VIEW("VIEW", "View", ICON_FILE_IMAGE),
SI_ITEM_UV,
SI_ITEM_PAINT,
SI_ITEM_MASK,
@@ -268,14 +268,14 @@ const EnumPropertyItem rna_enum_space_image_mode_all_items[] = {
};
static const EnumPropertyItem rna_enum_space_image_mode_ui_items[] = {
- SI_ITEM_VIEW("View", ICON_FILE_IMAGE),
+ SI_ITEM_VIEW("VIEW", "View", ICON_FILE_IMAGE),
SI_ITEM_PAINT,
SI_ITEM_MASK,
{0, NULL, 0, NULL, NULL},
};
const EnumPropertyItem rna_enum_space_image_mode_items[] = {
- SI_ITEM_VIEW("Image Editor", ICON_IMAGE),
+ SI_ITEM_VIEW("IMAGE_EDITOR", "Image Editor", ICON_IMAGE),
SI_ITEM_UV,
{0, NULL, 0, NULL, NULL},
};
@@ -1297,15 +1297,13 @@ static const EnumPropertyItem *rna_3DViewShading_render_pass_itemf(bContext *C,
{
Scene *scene = CTX_data_scene(C);
- const bool ao_enabled = scene->eevee.flag & SCE_EEVEE_GTAO_ENABLED;
const bool bloom_enabled = scene->eevee.flag & SCE_EEVEE_BLOOM_ENABLED;
int totitem = 0;
EnumPropertyItem *result = NULL;
for (int i = 0; rna_enum_view3dshading_render_pass_type_items[i].identifier != NULL; i++) {
const EnumPropertyItem *item = &rna_enum_view3dshading_render_pass_type_items[i];
- if (!((!ao_enabled && item->value == EEVEE_RENDER_PASS_AO) ||
- (!bloom_enabled &&
+ if (!((!bloom_enabled &&
(item->value == EEVEE_RENDER_PASS_BLOOM || STREQ(item->name, "Effects"))))) {
RNA_enum_item_add(&result, &totitem, item);
}
@@ -1321,9 +1319,6 @@ static int rna_3DViewShading_render_pass_get(PointerRNA *ptr)
eViewLayerEEVEEPassType result = shading->render_pass;
Scene *scene = rna_3DViewShading_scene(ptr);
- if (result == EEVEE_RENDER_PASS_AO && ((scene->eevee.flag & SCE_EEVEE_GTAO_ENABLED) == 0)) {
- result = EEVEE_RENDER_PASS_COMBINED;
- }
if (result == EEVEE_RENDER_PASS_BLOOM && ((scene->eevee.flag & SCE_EEVEE_BLOOM_ENABLED) == 0)) {
result = EEVEE_RENDER_PASS_COMBINED;
}
@@ -2122,6 +2117,7 @@ static void rna_SpaceNodeEditor_node_tree_update(const bContext *C, PointerRNA *
ED_node_tree_update(C);
}
+# ifdef WITH_PARTICLE_NODES
static PointerRNA rna_SpaceNodeEditor_simulation_get(PointerRNA *ptr)
{
SpaceNode *snode = (SpaceNode *)ptr->data;
@@ -2153,6 +2149,7 @@ static void rna_SpaceNodeEditor_simulation_set(PointerRNA *ptr,
}
snode->id = &sim->id;
}
+# endif
static int rna_SpaceNodeEditor_tree_type_get(PointerRNA *ptr)
{
@@ -3792,14 +3789,14 @@ static void rna_def_space_view3d_overlay(BlenderRNA *brna)
prop = RNA_def_property(srna, "texture_paint_mode_opacity", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "overlay.texture_paint_mode_opacity");
RNA_def_property_ui_text(
- prop, "Stencil Opacity", "Opacity of the texture paint mode stencil mask overlay");
+ prop, "Stencil Mask Opacity", "Opacity of the texture paint mode stencil mask overlay");
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL);
prop = RNA_def_property(srna, "vertex_paint_mode_opacity", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "overlay.vertex_paint_mode_opacity");
RNA_def_property_ui_text(
- prop, "Stencil Opacity", "Opacity of the texture paint mode stencil mask overlay");
+ prop, "Stencil Mask Opacity", "Opacity of the texture paint mode stencil mask overlay");
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL);
@@ -4796,6 +4793,12 @@ static void rna_def_space_sequencer(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Waveform Displaying", "How Waveforms are drawn");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL);
+ prop = RNA_def_property(srna, "use_zoom_to_fit", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_ZOOM_TO_FIT);
+ RNA_def_property_ui_text(
+ prop, "Zoom to Fit", "Automatically zoom preview image to make it fully fit the region");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL);
+
prop = RNA_def_property(srna, "show_overexposed", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "zebra");
RNA_def_property_ui_text(prop, "Show Overexposed", "Show overexposed areas with zebra stripes");
@@ -6207,6 +6210,7 @@ static void rna_def_space_node(BlenderRNA *brna)
RNA_def_property_ui_text(
prop, "ID From", "Data-block from which the edited data-block is linked");
+# ifdef WITH_PARTICLE_NODES
prop = RNA_def_property(srna, "simulation", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_struct_type(prop, "Simulation");
@@ -6217,6 +6221,7 @@ static void rna_def_space_node(BlenderRNA *brna)
NULL,
NULL);
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE, NULL);
+# endif
prop = RNA_def_property(srna, "path", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "treepath", NULL);
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 31d417abeb1..e060114912e 100644
--- a/source/blender/makesrna/intern/rna_userdef.c
+++ b/source/blender/makesrna/intern/rna_userdef.c
@@ -2483,6 +2483,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)
@@ -3044,12 +3049,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/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c
index bfd99c01551..f248764eab9 100644
--- a/source/blender/makesrna/intern/rna_wm.c
+++ b/source/blender/makesrna/intern/rna_wm.c
@@ -438,8 +438,7 @@ static const EnumPropertyItem keymap_modifiers_items[] = {
};
#endif
-#ifndef RNA_RUNTIME
-static const EnumPropertyItem operator_flag_items[] = {
+const EnumPropertyItem rna_enum_operator_type_flag_items[] = {
{OPTYPE_REGISTER,
"REGISTER",
0,
@@ -465,7 +464,6 @@ static const EnumPropertyItem operator_flag_items[] = {
{OPTYPE_INTERNAL, "INTERNAL", 0, "Internal", "Removes the operator from search results"},
{0, NULL, 0, NULL, NULL},
};
-#endif
const EnumPropertyItem rna_enum_operator_return_items[] = {
{OPERATOR_RUNNING_MODAL,
@@ -1928,7 +1926,7 @@ static void rna_def_operator(BlenderRNA *brna)
prop = RNA_def_property(srna, "bl_options", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "type->flag");
- RNA_def_property_enum_items(prop, operator_flag_items);
+ RNA_def_property_enum_items(prop, rna_enum_operator_type_flag_items);
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL | PROP_ENUM_FLAG);
RNA_def_property_ui_text(prop, "Options", "Options for this operator type");
@@ -2020,7 +2018,7 @@ static void rna_def_macro_operator(BlenderRNA *brna)
prop = RNA_def_property(srna, "bl_options", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "type->flag");
- RNA_def_property_enum_items(prop, operator_flag_items);
+ RNA_def_property_enum_items(prop, rna_enum_operator_type_flag_items);
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL | PROP_ENUM_FLAG);
RNA_def_property_ui_text(prop, "Options", "Options for this operator type");
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..37c3f32f529 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,33 @@ 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
+ if (bmd->solver == eBooleanModifierSolver_Exact) {
+ BKE_modifier_set_error(md, "Compiled without GMP, using fast solver");
+ }
+ 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);
}
@@ -373,8 +393,14 @@ static void panel_draw(const bContext *C, Panel *panel)
uiLayoutSetPropSep(layout, true);
+ const bool use_exact = RNA_enum_get(&ptr, "solver") == eBooleanModifierSolver_Exact;
+
uiItemR(layout, &ptr, "object", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "double_threshold", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "solver", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+
+ if (!use_exact) {
+ uiItemR(layout, &ptr, "double_threshold", 0, NULL, ICON_NONE);
+ }
if (G.debug) {
uiLayout *col = uiLayoutColumn(layout, true);
@@ -394,7 +420,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/modifiers/intern/MOD_multires.c b/source/blender/modifiers/intern/MOD_multires.c
index 98a348bfbfc..9ced297bb48 100644
--- a/source/blender/modifiers/intern/MOD_multires.c
+++ b/source/blender/modifiers/intern/MOD_multires.c
@@ -238,7 +238,9 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
/* Needed when rendering or baking will in sculpt mode. */
const bool for_render = (ctx->flag & MOD_APPLY_RENDER) != 0;
- if ((ctx->object->mode & OB_MODE_SCULPT) && !for_orco && !for_render) {
+ const bool sculpt_base_mesh = mmd->flags & eMultiresModifierFlag_UseSculptBaseMesh;
+
+ if ((ctx->object->mode & OB_MODE_SCULPT) && !for_orco && !for_render && !sculpt_base_mesh) {
/* NOTE: CCG takes ownership over Subdiv. */
result = multires_as_ccg(mmd, ctx, mesh, subdiv);
result->runtime.subdiv_ccg_tot_level = mmd->totlvl;
@@ -341,6 +343,12 @@ static void panel_draw(const bContext *C, Panel *panel)
uiItemR(col, &ptr, "sculpt_levels", 0, IFACE_("Sculpt"), ICON_NONE);
uiItemR(col, &ptr, "render_levels", 0, IFACE_("Render"), ICON_NONE);
+ const bool is_sculpt_mode = CTX_data_active_object(C)->mode & OB_MODE_SCULPT;
+ uiBlock *block = uiLayoutGetBlock(panel->layout);
+ UI_block_lock_set(block, !is_sculpt_mode, IFACE_("Sculpt Base Mesh"));
+ uiItemR(col, &ptr, "use_sculpt_base_mesh", 0, IFACE_("Sculpt Base Mesh"), ICON_NONE);
+ UI_block_lock_clear(block);
+
uiItemR(layout, &ptr, "show_only_control_edges", 0, NULL, ICON_NONE);
modifier_panel_end(layout, &ptr);
@@ -407,10 +415,7 @@ static void subdivisions_panel_draw(const bContext *C, Panel *panel)
uiItemS(layout);
uiItemO(layout, IFACE_("Unsubdivide"), ICON_NONE, "OBJECT_OT_multires_unsubdivide");
-
- row = uiLayoutRow(layout, false);
- uiItemL(row, "", ICON_NONE);
- uiItemO(row, IFACE_("Delete Higher"), ICON_NONE, "OBJECT_OT_multires_higher_levels_delete");
+ uiItemO(layout, IFACE_("Delete Higher"), ICON_NONE, "OBJECT_OT_multires_higher_levels_delete");
}
static void shape_panel_draw(const bContext *C, Panel *panel)
diff --git a/source/blender/modifiers/intern/MOD_particlesystem.c b/source/blender/modifiers/intern/MOD_particlesystem.c
index ea0c63da1b0..4ef1b19dc64 100644
--- a/source/blender/modifiers/intern/MOD_particlesystem.c
+++ b/source/blender/modifiers/intern/MOD_particlesystem.c
@@ -219,7 +219,7 @@ static void deformVerts(ModifierData *md,
psmd->totdmedge = psmd->mesh_final->totedge;
psmd->totdmface = psmd->mesh_final->totface;
- if (!(ctx->object->transflag & OB_NO_PSYS_UPDATE)) {
+ {
struct Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph);
psmd->flag &= ~eParticleSystemFlag_psys_updated;
particle_system_update(
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/BPY_extern.h b/source/blender/python/BPY_extern.h
index 7bcf96116b9..e1e0d01055a 100644
--- a/source/blender/python/BPY_extern.h
+++ b/source/blender/python/BPY_extern.h
@@ -50,12 +50,6 @@ void BPY_pyconstraint_update(struct Object *owner, struct bConstraint *con);
int BPY_is_pyconstraint(struct Text *text);
// void BPY_free_pyconstraint_links(struct Text *text);
-void BPY_python_start(int argc, const char **argv);
-void BPY_python_end(void);
-void BPY_python_reset(struct bContext *C);
-void BPY_python_use_system_env(void);
-void BPY_python_backtrace(/* FILE */ void *file);
-
/* global interpreter lock */
typedef void *BPy_ThreadStatePtr;
@@ -73,40 +67,6 @@ void BPY_thread_restore(BPy_ThreadStatePtr tstate);
} \
(void)0
-bool BPY_execute_filepath(struct bContext *C, const char *filepath, struct ReportList *reports);
-bool BPY_execute_text(struct bContext *C,
- struct Text *text,
- struct ReportList *reports,
- const bool do_jump);
-
-bool BPY_execute_string_as_number(struct bContext *C,
- const char *imports[],
- const char *expr,
- const char *report_prefix,
- double *r_value);
-bool BPY_execute_string_as_intptr(struct bContext *C,
- const char *imports[],
- const char *expr,
- const char *report_prefix,
- intptr_t *r_value);
-bool BPY_execute_string_as_string_and_size(struct bContext *C,
- const char *imports[],
- const char *expr,
- const char *report_prefix,
- char **r_value,
- size_t *r_value_size);
-bool BPY_execute_string_as_string(struct bContext *C,
- const char *imports[],
- const char *expr,
- const char *report_prefix,
- char **r_value);
-
-bool BPY_execute_string_ex(struct bContext *C,
- const char *imports[],
- const char *expr,
- bool use_eval);
-bool BPY_execute_string(struct bContext *C, const char *imports[], const char *expr);
-
void BPY_text_free_code(struct Text *text);
void BPY_modules_update(
struct bContext *C); // XXX - annoying, need this for pointers that get out of date
diff --git a/source/blender/gpu/GPU_attr_binding.h b/source/blender/python/BPY_extern_python.h
index e7c3dcbce05..348f6986863 100644
--- a/source/blender/gpu/GPU_attr_binding.h
+++ b/source/blender/python/BPY_extern_python.h
@@ -12,32 +12,32 @@
* 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
+ * \ingroup python
*
- * GPU vertex attribute binding
+ * Functionality relating to Python setup & tear down.
*/
#pragma once
-#include "GPU_common.h"
+struct bContext;
#ifdef __cplusplus
extern "C" {
#endif
-typedef struct GPUAttrBinding {
- /** Store 4 bits for each of the 16 attributes. */
- uint64_t loc_bits;
- /** 1 bit for each attribute. */
- uint16_t enabled_bits;
-} GPUAttrBinding;
+/* For 'FILE'. */
+#include <stdio.h>
+
+/* bpy_interface.c */
+void BPY_python_start(int argc, const char **argv);
+void BPY_python_end(void);
+void BPY_python_reset(struct bContext *C);
+void BPY_python_use_system_env(void);
+void BPY_python_backtrace(FILE *file);
#ifdef __cplusplus
-}
+} /* extern "C" */
#endif
diff --git a/source/blender/python/BPY_extern_run.h b/source/blender/python/BPY_extern_run.h
new file mode 100644
index 00000000000..5f12ada4ff2
--- /dev/null
+++ b/source/blender/python/BPY_extern_run.h
@@ -0,0 +1,70 @@
+/*
+ * 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 python
+ */
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "BLI_sys_types.h"
+
+struct ReportList;
+struct Text;
+struct bContext;
+
+/* bpy_interface_run.c */
+bool BPY_run_filepath(struct bContext *C, const char *filepath, struct ReportList *reports);
+bool BPY_run_text(struct bContext *C,
+ struct Text *text,
+ struct ReportList *reports,
+ const bool do_jump);
+
+/* Use the 'eval' for simple single-line expressions,
+ * otherwise 'exec' for full multi-line scripts. */
+bool BPY_run_string_exec(struct bContext *C, const char *imports[], const char *expr);
+bool BPY_run_string_eval(struct bContext *C, const char *imports[], const char *expr);
+
+/* Run, evaluating to fixed type result. */
+bool BPY_run_string_as_number(struct bContext *C,
+ const char *imports[],
+ const char *expr,
+ const char *report_prefix,
+ double *r_value);
+bool BPY_run_string_as_intptr(struct bContext *C,
+ const char *imports[],
+ const char *expr,
+ const char *report_prefix,
+ intptr_t *r_value);
+bool BPY_run_string_as_string_and_size(struct bContext *C,
+ const char *imports[],
+ const char *expr,
+ const char *report_prefix,
+ char **r_value,
+ size_t *r_value_size);
+bool BPY_run_string_as_string(struct bContext *C,
+ const char *imports[],
+ const char *expr,
+ const char *report_prefix,
+ char **r_value);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
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 e39b5faf3c4..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);
@@ -1134,7 +1134,7 @@ static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args, PyObject
return NULL;
}
- me_eval = mesh_create_eval_final_render(depsgraph, scene_eval, ob_eval, &data_masks);
+ me_eval = mesh_create_eval_final(depsgraph, scene_eval, ob_eval, &data_masks);
}
else {
if (use_cage) {
@@ -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/CMakeLists.txt b/source/blender/python/generic/CMakeLists.txt
index 785c1d66407..dce6a7e1c91 100644
--- a/source/blender/python/generic/CMakeLists.txt
+++ b/source/blender/python/generic/CMakeLists.txt
@@ -32,18 +32,18 @@ set(INC_SYS
set(SRC
bgl.c
+ bl_math_py_api.c
blf_py_api.c
bpy_threads.c
idprop_py_api.c
imbuf_py_api.c
- bl_math_py_api.c
py_capi_utils.c
bgl.h
+ bl_math_py_api.h
blf_py_api.h
idprop_py_api.h
imbuf_py_api.h
- bl_math_py_api.h
py_capi_utils.h
# header-only
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 caae5c4e122..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,25 +377,11 @@ 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);
}
-void PyC_StackPrint(/* FILE */ void *fp)
-{
- PyThreadState *tstate = PyGILState_GetThisThreadState();
- if (tstate != NULL && tstate->frame != NULL) {
- PyFrameObject *frame = tstate->frame;
- do {
- const int line = PyCode_Addr2Line(frame->f_code, frame->f_lasti);
- const char *filename = _PyUnicode_AsString(frame->f_code->co_filename);
- const char *funcname = _PyUnicode_AsString(frame->f_code->co_name);
- fprintf(fp, " File \"%s\", line %d in %s\n", filename, line, funcname);
- } while ((frame = frame->f_back));
- }
-}
-
/** \} */
/* -------------------------------------------------------------------- */
@@ -962,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;
@@ -1437,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;
@@ -1447,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;
@@ -1457,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;
@@ -1472,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;
@@ -1482,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;
@@ -1492,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/generic/py_capi_utils.h b/source/blender/python/generic/py_capi_utils.h
index dde450012d0..e8b2e8ff502 100644
--- a/source/blender/python/generic/py_capi_utils.h
+++ b/source/blender/python/generic/py_capi_utils.h
@@ -28,7 +28,6 @@ void PyC_ObSpit(const char *name, PyObject *var);
void PyC_ObSpitStr(char *result, size_t result_len, PyObject *var);
void PyC_LineSpit(void);
void PyC_StackSpit(void);
-void PyC_StackPrint(/* FILE */ void *fp);
PyObject *PyC_ExceptionBuffer(void);
PyObject *PyC_ExceptionBuffer_Simple(void);
PyObject *PyC_Object_GetAttrStringArgs(PyObject *o, Py_ssize_t n, ...);
diff --git a/source/blender/python/gpu/gpu_py_batch.c b/source/blender/python/gpu/gpu_py_batch.c
index 01bccc57c7a..bb7028c11ab 100644
--- a/source/blender/python/gpu/gpu_py_batch.c
+++ b/source/blender/python/gpu/gpu_py_batch.c
@@ -50,7 +50,7 @@
static bool bpygpu_batch_is_program_or_error(BPyGPUBatch *self)
{
- if (!glIsProgram(self->batch->program)) {
+ if (!self->batch->shader) {
PyErr_SetString(PyExc_RuntimeError, "batch does not have any program assigned to it");
return false;
}
@@ -227,7 +227,7 @@ static PyObject *bpygpu_Batch_draw(BPyGPUBatch *self, PyObject *args)
return NULL;
}
}
- else if (self->batch->program != GPU_shader_get_program(py_program->shader)) {
+ else if (self->batch->shader != py_program->shader) {
GPU_batch_set_shader(self->batch, py_program->shader);
}
@@ -240,7 +240,7 @@ static PyObject *bpygpu_Batch_program_use_begin(BPyGPUBatch *self)
if (!bpygpu_batch_is_program_or_error(self)) {
return NULL;
}
- GPU_batch_program_use_begin(self->batch);
+ GPU_shader_bind(self->batch->shader);
Py_RETURN_NONE;
}
@@ -249,7 +249,7 @@ static PyObject *bpygpu_Batch_program_use_end(BPyGPUBatch *self)
if (!bpygpu_batch_is_program_or_error(self)) {
return NULL;
}
- GPU_batch_program_use_end(self->batch);
+ GPU_shader_unbind();
Py_RETURN_NONE;
}
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 165286c3661..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);
@@ -604,7 +604,8 @@ PyDoc_STRVAR(
" ``GPU_ATI``, ``GPU_NVIDIA`` and ``GPU_INTEL``.\n"
"\n"
" The following extensions are enabled by default if supported by the GPU:\n"
- " ``GL_ARB_texture_gather`` and ``GL_ARB_texture_query_lod``.\n"
+ " ``GL_ARB_texture_gather``, ``GL_ARB_texture_cube_map_array`` and "
+ "``GL_ARB_shader_draw_parameters``.\n"
"\n"
" To debug shaders, use the --debug-gpu-shaders command line option"
" to see full GLSL shader compilation and linking errors.\n"
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/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt
index 769618005af..44949c478cc 100644
--- a/source/blender/python/intern/CMakeLists.txt
+++ b/source/blender/python/intern/CMakeLists.txt
@@ -62,6 +62,7 @@ set(SRC
bpy_gizmo_wrap.c
bpy_interface.c
bpy_interface_atexit.c
+ bpy_interface_run.c
bpy_intern_string.c
bpy_library_load.c
bpy_library_write.c
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_capi_utils.c b/source/blender/python/intern/bpy_capi_utils.c
index 27eea80f1f6..8eb44e918d7 100644
--- a/source/blender/python/intern/bpy_capi_utils.c
+++ b/source/blender/python/intern/bpy_capi_utils.c
@@ -51,16 +51,17 @@ void BPy_SetContext(bContext *C)
char *BPy_enum_as_string(const EnumPropertyItem *item)
{
DynStr *dynstr = BLI_dynstr_new();
- const EnumPropertyItem *e;
- char *cstring;
- for (e = item; item->identifier; item++) {
+ /* We can't compare with the first element in the array
+ * since it may be a category (without an identifier). */
+ for (bool is_first = true; item->identifier; item++) {
if (item->identifier[0]) {
- BLI_dynstr_appendf(dynstr, (e == item) ? "'%s'" : ", '%s'", item->identifier);
+ BLI_dynstr_appendf(dynstr, is_first ? "'%s'" : ", '%s'", item->identifier);
+ is_first = false;
}
}
- cstring = BLI_dynstr_get_cstring(dynstr);
+ char *cstring = BLI_dynstr_get_cstring(dynstr);
BLI_dynstr_free(dynstr);
return cstring;
}
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 c311041e4cb..bc7318e1a15 100644
--- a/source/blender/python/intern/bpy_interface.c
+++ b/source/blender/python/intern/bpy_interface.c
@@ -25,6 +25,7 @@
*/
#include <Python.h>
+#include <frameobject.h>
#include "MEM_guardedalloc.h"
@@ -62,6 +63,8 @@
#endif
#include "BPY_extern.h"
+#include "BPY_extern_python.h"
+#include "BPY_extern_run.h"
#include "../generic/py_capi_utils.h"
@@ -166,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();
@@ -426,192 +429,31 @@ void BPY_python_use_system_env(void)
py_use_system_env = true;
}
-static void python_script_error_jump_text(struct Text *text)
-{
- int lineno;
- int offset;
- python_script_error_jump(text->id.name + 2, &lineno, &offset);
- if (lineno != -1) {
- /* select the line with the error */
- txt_move_to(text, lineno - 1, INT_MAX, false);
- txt_move_to(text, lineno - 1, offset, true);
- }
-}
-
-void BPY_python_backtrace(/* FILE */ void *fp)
+void BPY_python_backtrace(FILE *fp)
{
fputs("\n# Python backtrace\n", fp);
- PyC_StackPrint(fp);
-}
-
-/* super annoying, undo _PyModule_Clear(), bug [#23871] */
-#define PYMODULE_CLEAR_WORKAROUND
-
-#ifdef PYMODULE_CLEAR_WORKAROUND
-/* bad!, we should never do this, but currently only safe way I could find to keep namespace.
- * from being cleared. - campbell */
-typedef struct {
- PyObject_HEAD PyObject *md_dict;
- /* omit other values, we only want the dict. */
-} PyModuleObject;
-#endif
-
-/* returns a dummy filename for a textblock so we can tell what file a text block comes from */
-static void bpy_text_filename_get(char *fn, const Main *bmain, size_t fn_len, const Text *text)
-{
- BLI_snprintf(fn, fn_len, "%s%c%s", ID_BLEND_PATH(bmain, &text->id), SEP, text->id.name + 2);
-}
-
-static bool python_script_exec(
- bContext *C, const char *fn, struct Text *text, struct ReportList *reports, const bool do_jump)
-{
- Main *bmain_old = CTX_data_main(C);
- PyObject *main_mod = NULL;
- PyObject *py_dict = NULL, *py_result = NULL;
- PyGILState_STATE gilstate;
-
- BLI_assert(fn || text);
-
- if (fn == NULL && text == NULL) {
- return 0;
- }
-
- bpy_context_set(C, &gilstate);
-
- PyC_MainModule_Backup(&main_mod);
-
- if (text) {
- char fn_dummy[FILE_MAXDIR];
- bpy_text_filename_get(fn_dummy, bmain_old, sizeof(fn_dummy), text);
-
- if (text->compiled == NULL) { /* if it wasn't already compiled, do it now */
- char *buf;
- PyObject *fn_dummy_py;
-
- fn_dummy_py = PyC_UnicodeFromByte(fn_dummy);
-
- buf = txt_to_buf(text, NULL);
- text->compiled = Py_CompileStringObject(buf, fn_dummy_py, Py_file_input, NULL, -1);
- MEM_freeN(buf);
-
- Py_DECREF(fn_dummy_py);
-
- if (PyErr_Occurred()) {
- if (do_jump) {
- python_script_error_jump_text(text);
- }
- BPY_text_free_code(text);
- }
- }
-
- if (text->compiled) {
- py_dict = PyC_DefaultNameSpace(fn_dummy);
- py_result = PyEval_EvalCode(text->compiled, py_dict, py_dict);
- }
- }
- else {
- FILE *fp = BLI_fopen(fn, "r");
-
- if (fp) {
- py_dict = PyC_DefaultNameSpace(fn);
-
-#ifdef _WIN32
- /* Previously we used PyRun_File to run directly the code on a FILE
- * object, but as written in the Python/C API Ref Manual, chapter 2,
- * 'FILE structs for different C libraries can be different and
- * incompatible'.
- * So now we load the script file data to a buffer.
- *
- * Note on use of 'globals()', it's important not copy the dictionary because
- * tools may inspect 'sys.modules["__main__"]' for variables defined in the code
- * where using a copy of 'globals()' causes code execution
- * to leave the main namespace untouched. see: T51444
- *
- * This leaves us with the problem of variables being included,
- * currently this is worked around using 'dict.__del__' it's ugly but works.
- */
- {
- const char *pystring =
- "with open(__file__, 'rb') as f:"
- "exec(compile(f.read(), __file__, 'exec'), globals().__delitem__('f') or globals())";
-
- fclose(fp);
-
- py_result = PyRun_String(pystring, Py_file_input, py_dict, py_dict);
- }
-#else
- py_result = PyRun_File(fp, fn, Py_file_input, py_dict, py_dict);
- fclose(fp);
-#endif
- }
- else {
- PyErr_Format(
- PyExc_IOError, "Python file \"%s\" could not be opened: %s", fn, strerror(errno));
- py_result = NULL;
- }
- }
-
- if (!py_result) {
- if (text) {
- if (do_jump) {
- /* ensure text is valid before use, the script may have freed its self */
- Main *bmain_new = CTX_data_main(C);
- if ((bmain_old == bmain_new) && (BLI_findindex(&bmain_new->texts, text) != -1)) {
- python_script_error_jump_text(text);
- }
- }
- }
- BPy_errors_to_report(reports);
+ PyThreadState *tstate = PyGILState_GetThisThreadState();
+ if (tstate != NULL && tstate->frame != NULL) {
+ PyFrameObject *frame = tstate->frame;
+ do {
+ const int line = PyCode_Addr2Line(frame->f_code, frame->f_lasti);
+ const char *filename = _PyUnicode_AsString(frame->f_code->co_filename);
+ const char *funcname = _PyUnicode_AsString(frame->f_code->co_name);
+ fprintf(fp, " File \"%s\", line %d in %s\n", filename, line, funcname);
+ } while ((frame = frame->f_back));
}
- else {
- Py_DECREF(py_result);
- }
-
- if (py_dict) {
-#ifdef PYMODULE_CLEAR_WORKAROUND
- PyModuleObject *mmod = (PyModuleObject *)PyDict_GetItem(PyImport_GetModuleDict(),
- bpy_intern_str___main__);
- PyObject *dict_back = mmod->md_dict;
- /* freeing the module will clear the namespace,
- * gives problems running classes defined in this namespace being used later. */
- mmod->md_dict = NULL;
- Py_DECREF(dict_back);
-#endif
-
-#undef PYMODULE_CLEAR_WORKAROUND
- }
-
- PyC_MainModule_Restore(main_mod);
-
- bpy_context_clear(C, &gilstate);
-
- return (py_result != NULL);
-}
-
-/* Can run a file or text block */
-bool BPY_execute_filepath(bContext *C, const char *filepath, struct ReportList *reports)
-{
- return python_script_exec(C, filepath, NULL, reports, false);
-}
-
-bool BPY_execute_text(bContext *C,
- struct Text *text,
- struct ReportList *reports,
- const bool do_jump)
-{
- return python_script_exec(C, NULL, text, reports, do_jump);
}
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) {
@@ -620,177 +462,6 @@ void BPY_DECREF_RNA_INVALIDATE(void *pyob_ptr)
PyGILState_Release(gilstate);
}
-/**
- * \return success
- */
-bool BPY_execute_string_as_number(bContext *C,
- const char *imports[],
- const char *expr,
- const char *report_prefix,
- double *r_value)
-{
- PyGILState_STATE gilstate;
- bool ok = true;
-
- if (!r_value || !expr) {
- return -1;
- }
-
- if (expr[0] == '\0') {
- *r_value = 0.0;
- return ok;
- }
-
- bpy_context_set(C, &gilstate);
-
- ok = PyC_RunString_AsNumber(imports, expr, "<expr as number>", r_value);
-
- if (ok == false) {
- if (report_prefix != NULL) {
- BPy_errors_to_report_ex(CTX_wm_reports(C), report_prefix, false, false);
- }
- else {
- PyErr_Clear();
- }
- }
-
- bpy_context_clear(C, &gilstate);
-
- return ok;
-}
-
-/**
- * \return success
- */
-bool BPY_execute_string_as_string_and_size(bContext *C,
- const char *imports[],
- const char *expr,
- const char *report_prefix,
- char **r_value,
- size_t *r_value_size)
-{
- BLI_assert(r_value && expr);
- PyGILState_STATE gilstate;
- bool ok = true;
-
- if (expr[0] == '\0') {
- *r_value = NULL;
- return ok;
- }
-
- bpy_context_set(C, &gilstate);
-
- ok = PyC_RunString_AsStringAndSize(imports, expr, "<expr as str>", r_value, r_value_size);
-
- if (ok == false) {
- if (report_prefix != NULL) {
- BPy_errors_to_report_ex(CTX_wm_reports(C), false, false, report_prefix);
- }
- else {
- PyErr_Clear();
- }
- }
-
- bpy_context_clear(C, &gilstate);
-
- return ok;
-}
-
-bool BPY_execute_string_as_string(bContext *C,
- const char *imports[],
- const char *expr,
- const char *report_prefix,
- char **r_value)
-{
- size_t value_dummy_size;
- return BPY_execute_string_as_string_and_size(
- C, imports, expr, report_prefix, r_value, &value_dummy_size);
-}
-
-/**
- * Support both int and pointers.
- *
- * \return success
- */
-bool BPY_execute_string_as_intptr(bContext *C,
- const char *imports[],
- const char *expr,
- const char *report_prefix,
- intptr_t *r_value)
-{
- BLI_assert(r_value && expr);
- PyGILState_STATE gilstate;
- bool ok = true;
-
- if (expr[0] == '\0') {
- *r_value = 0;
- return ok;
- }
-
- bpy_context_set(C, &gilstate);
-
- ok = PyC_RunString_AsIntPtr(imports, expr, "<expr as intptr>", r_value);
-
- if (ok == false) {
- if (report_prefix != NULL) {
- BPy_errors_to_report_ex(CTX_wm_reports(C), report_prefix, false, false);
- }
- else {
- PyErr_Clear();
- }
- }
-
- bpy_context_clear(C, &gilstate);
-
- return ok;
-}
-
-bool BPY_execute_string_ex(bContext *C, const char *imports[], const char *expr, bool use_eval)
-{
- BLI_assert(expr);
- PyGILState_STATE gilstate;
- PyObject *main_mod = NULL;
- PyObject *py_dict, *retval;
- bool ok = true;
-
- if (expr[0] == '\0') {
- return ok;
- }
-
- bpy_context_set(C, &gilstate);
-
- PyC_MainModule_Backup(&main_mod);
-
- py_dict = PyC_DefaultNameSpace("<blender string>");
-
- if (imports && (!PyC_NameSpace_ImportArray(py_dict, imports))) {
- Py_DECREF(py_dict);
- retval = NULL;
- }
- else {
- retval = PyRun_String(expr, use_eval ? Py_eval_input : Py_file_input, py_dict, py_dict);
- }
-
- if (retval == NULL) {
- ok = false;
- BPy_errors_to_report(CTX_wm_reports(C));
- }
- else {
- Py_DECREF(retval);
- }
-
- PyC_MainModule_Restore(main_mod);
-
- bpy_context_clear(C, &gilstate);
-
- return ok;
-}
-
-bool BPY_execute_string(bContext *C, const char *imports[], const char *expr)
-{
- return BPY_execute_string_ex(C, imports, expr, true);
-}
-
void BPY_modules_load_user(bContext *C)
{
PyGILState_STATE gilstate;
@@ -823,7 +494,7 @@ void BPY_modules_load_user(bContext *C)
}
}
else {
- BPY_execute_text(C, text, NULL, false);
+ BPY_run_text(C, text, NULL, false);
/* Check if the script loaded a new file. */
if (bmain != CTX_data_main(C)) {
@@ -838,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;
@@ -873,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_interface_run.c b/source/blender/python/intern/bpy_interface_run.c
new file mode 100644
index 00000000000..a7593ae7d79
--- /dev/null
+++ b/source/blender/python/intern/bpy_interface_run.c
@@ -0,0 +1,422 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup pythonintern
+ */
+
+#include <stdio.h>
+
+#include <Python.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_fileops.h"
+#include "BLI_listbase.h"
+#include "BLI_path_util.h"
+#include "BLI_string.h"
+
+#include "BKE_context.h"
+#include "BKE_main.h"
+#include "BKE_text.h"
+
+#include "DNA_text_types.h"
+
+#include "BPY_extern.h"
+#include "BPY_extern_run.h"
+
+#include "bpy_capi_utils.h"
+#include "bpy_intern_string.h"
+#include "bpy_traceback.h"
+
+#include "../generic/py_capi_utils.h"
+
+/* -------------------------------------------------------------------- */
+/** \name Private Utilities
+ * \{ */
+
+static void python_script_error_jump_text(Text *text)
+{
+ int lineno;
+ int offset;
+ python_script_error_jump(text->id.name + 2, &lineno, &offset);
+ if (lineno != -1) {
+ /* select the line with the error */
+ txt_move_to(text, lineno - 1, INT_MAX, false);
+ txt_move_to(text, lineno - 1, offset, true);
+ }
+}
+
+/* returns a dummy filename for a textblock so we can tell what file a text block comes from */
+static void bpy_text_filename_get(char *fn, const Main *bmain, size_t fn_len, const Text *text)
+{
+ BLI_snprintf(fn, fn_len, "%s%c%s", ID_BLEND_PATH(bmain, &text->id), SEP, text->id.name + 2);
+}
+
+/* Very annoying! Undo #_PyModule_Clear(), see T23871. */
+#define PYMODULE_CLEAR_WORKAROUND
+
+#ifdef PYMODULE_CLEAR_WORKAROUND
+/* bad!, we should never do this, but currently only safe way I could find to keep namespace.
+ * from being cleared. - campbell */
+typedef struct {
+ PyObject_HEAD PyObject *md_dict;
+ /* omit other values, we only want the dict. */
+} PyModuleObject;
+#endif
+
+static bool python_script_exec(
+ bContext *C, const char *fn, struct Text *text, struct ReportList *reports, const bool do_jump)
+{
+ Main *bmain_old = CTX_data_main(C);
+ PyObject *main_mod = NULL;
+ PyObject *py_dict = NULL, *py_result = NULL;
+ PyGILState_STATE gilstate;
+
+ BLI_assert(fn || text);
+
+ if (fn == NULL && text == NULL) {
+ return 0;
+ }
+
+ bpy_context_set(C, &gilstate);
+
+ PyC_MainModule_Backup(&main_mod);
+
+ if (text) {
+ char fn_dummy[FILE_MAXDIR];
+ bpy_text_filename_get(fn_dummy, bmain_old, sizeof(fn_dummy), text);
+
+ if (text->compiled == NULL) { /* if it wasn't already compiled, do it now */
+ char *buf;
+ PyObject *fn_dummy_py;
+
+ fn_dummy_py = PyC_UnicodeFromByte(fn_dummy);
+
+ buf = txt_to_buf(text, NULL);
+ text->compiled = Py_CompileStringObject(buf, fn_dummy_py, Py_file_input, NULL, -1);
+ MEM_freeN(buf);
+
+ Py_DECREF(fn_dummy_py);
+
+ if (PyErr_Occurred()) {
+ if (do_jump) {
+ python_script_error_jump_text(text);
+ }
+ BPY_text_free_code(text);
+ }
+ }
+
+ if (text->compiled) {
+ py_dict = PyC_DefaultNameSpace(fn_dummy);
+ py_result = PyEval_EvalCode(text->compiled, py_dict, py_dict);
+ }
+ }
+ else {
+ FILE *fp = BLI_fopen(fn, "r");
+
+ if (fp) {
+ py_dict = PyC_DefaultNameSpace(fn);
+
+#ifdef _WIN32
+ /* Previously we used PyRun_File to run directly the code on a FILE
+ * object, but as written in the Python/C API Ref Manual, chapter 2,
+ * 'FILE structs for different C libraries can be different and
+ * incompatible'.
+ * So now we load the script file data to a buffer.
+ *
+ * Note on use of 'globals()', it's important not copy the dictionary because
+ * tools may inspect 'sys.modules["__main__"]' for variables defined in the code
+ * where using a copy of 'globals()' causes code execution
+ * to leave the main namespace untouched. see: T51444
+ *
+ * This leaves us with the problem of variables being included,
+ * currently this is worked around using 'dict.__del__' it's ugly but works.
+ */
+ {
+ const char *pystring =
+ "with open(__file__, 'rb') as f:"
+ "exec(compile(f.read(), __file__, 'exec'), globals().__delitem__('f') or globals())";
+
+ fclose(fp);
+
+ py_result = PyRun_String(pystring, Py_file_input, py_dict, py_dict);
+ }
+#else
+ py_result = PyRun_File(fp, fn, Py_file_input, py_dict, py_dict);
+ fclose(fp);
+#endif
+ }
+ else {
+ PyErr_Format(
+ PyExc_IOError, "Python file \"%s\" could not be opened: %s", fn, strerror(errno));
+ py_result = NULL;
+ }
+ }
+
+ if (!py_result) {
+ if (text) {
+ if (do_jump) {
+ /* ensure text is valid before use, the script may have freed its self */
+ Main *bmain_new = CTX_data_main(C);
+ if ((bmain_old == bmain_new) && (BLI_findindex(&bmain_new->texts, text) != -1)) {
+ python_script_error_jump_text(text);
+ }
+ }
+ }
+ BPy_errors_to_report(reports);
+ }
+ else {
+ Py_DECREF(py_result);
+ }
+
+ if (py_dict) {
+#ifdef PYMODULE_CLEAR_WORKAROUND
+ PyModuleObject *mmod = (PyModuleObject *)PyDict_GetItem(PyImport_GetModuleDict(),
+ bpy_intern_str___main__);
+ PyObject *dict_back = mmod->md_dict;
+ /* freeing the module will clear the namespace,
+ * gives problems running classes defined in this namespace being used later. */
+ mmod->md_dict = NULL;
+ Py_DECREF(dict_back);
+#endif
+
+#undef PYMODULE_CLEAR_WORKAROUND
+ }
+
+ PyC_MainModule_Restore(main_mod);
+
+ bpy_context_clear(C, &gilstate);
+
+ return (py_result != NULL);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Run Text / Filename / String
+ * \{ */
+
+/* Can run a file or text block */
+bool BPY_run_filepath(bContext *C, const char *filepath, struct ReportList *reports)
+{
+ return python_script_exec(C, filepath, NULL, reports, false);
+}
+
+bool BPY_run_text(bContext *C, struct Text *text, struct ReportList *reports, const bool do_jump)
+{
+ return python_script_exec(C, NULL, text, reports, do_jump);
+}
+
+/**
+ * \param mode: Passed to #PyRun_String, matches Python's `compile` functions mode argument.
+ * #Py_eval_input for `eval`, #Py_file_input for `exec`.
+ */
+static bool bpy_run_string_impl(bContext *C,
+ const char *imports[],
+ const char *expr,
+ const int mode)
+{
+ BLI_assert(expr);
+ PyGILState_STATE gilstate;
+ PyObject *main_mod = NULL;
+ PyObject *py_dict, *retval;
+ bool ok = true;
+
+ if (expr[0] == '\0') {
+ return ok;
+ }
+
+ bpy_context_set(C, &gilstate);
+
+ PyC_MainModule_Backup(&main_mod);
+
+ py_dict = PyC_DefaultNameSpace("<blender string>");
+
+ if (imports && (!PyC_NameSpace_ImportArray(py_dict, imports))) {
+ Py_DECREF(py_dict);
+ retval = NULL;
+ }
+ else {
+ retval = PyRun_String(expr, mode, py_dict, py_dict);
+ }
+
+ if (retval == NULL) {
+ ok = false;
+ BPy_errors_to_report(CTX_wm_reports(C));
+ }
+ else {
+ Py_DECREF(retval);
+ }
+
+ PyC_MainModule_Restore(main_mod);
+
+ bpy_context_clear(C, &gilstate);
+
+ return ok;
+}
+
+/**
+ * Run an expression, matches: `exec(compile(..., "eval"))`
+ */
+bool BPY_run_string_eval(bContext *C, const char *imports[], const char *expr)
+{
+ return bpy_run_string_impl(C, imports, expr, Py_eval_input);
+}
+
+/**
+ * Run an entire script, matches: `exec(compile(..., "exec"))`
+ */
+bool BPY_run_string_exec(bContext *C, const char *imports[], const char *expr)
+{
+ return bpy_run_string_impl(C, imports, expr, Py_file_input);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Run Python & Evaluate Utilities
+ *
+ * Return values as plain C types, useful to run Python scripts
+ * in code that doesn't deal with Python data-types.
+ * \{ */
+
+/**
+ * \return success
+ */
+bool BPY_run_string_as_number(bContext *C,
+ const char *imports[],
+ const char *expr,
+ const char *report_prefix,
+ double *r_value)
+{
+ PyGILState_STATE gilstate;
+ bool ok = true;
+
+ if (!r_value || !expr) {
+ return -1;
+ }
+
+ if (expr[0] == '\0') {
+ *r_value = 0.0;
+ return ok;
+ }
+
+ bpy_context_set(C, &gilstate);
+
+ ok = PyC_RunString_AsNumber(imports, expr, "<expr as number>", r_value);
+
+ if (ok == false) {
+ if (report_prefix != NULL) {
+ BPy_errors_to_report_ex(CTX_wm_reports(C), report_prefix, false, false);
+ }
+ else {
+ PyErr_Clear();
+ }
+ }
+
+ bpy_context_clear(C, &gilstate);
+
+ return ok;
+}
+
+/**
+ * \return success
+ */
+bool BPY_run_string_as_string_and_size(bContext *C,
+ const char *imports[],
+ const char *expr,
+ const char *report_prefix,
+ char **r_value,
+ size_t *r_value_size)
+{
+ BLI_assert(r_value && expr);
+ PyGILState_STATE gilstate;
+ bool ok = true;
+
+ if (expr[0] == '\0') {
+ *r_value = NULL;
+ return ok;
+ }
+
+ bpy_context_set(C, &gilstate);
+
+ ok = PyC_RunString_AsStringAndSize(imports, expr, "<expr as str>", r_value, r_value_size);
+
+ if (ok == false) {
+ if (report_prefix != NULL) {
+ BPy_errors_to_report_ex(CTX_wm_reports(C), false, false, report_prefix);
+ }
+ else {
+ PyErr_Clear();
+ }
+ }
+
+ bpy_context_clear(C, &gilstate);
+
+ return ok;
+}
+
+bool BPY_run_string_as_string(bContext *C,
+ const char *imports[],
+ const char *expr,
+ const char *report_prefix,
+ char **r_value)
+{
+ size_t value_dummy_size;
+ return BPY_run_string_as_string_and_size(
+ C, imports, expr, report_prefix, r_value, &value_dummy_size);
+}
+
+/**
+ * Support both int and pointers.
+ *
+ * \return success
+ */
+bool BPY_run_string_as_intptr(bContext *C,
+ const char *imports[],
+ const char *expr,
+ const char *report_prefix,
+ intptr_t *r_value)
+{
+ BLI_assert(r_value && expr);
+ PyGILState_STATE gilstate;
+ bool ok = true;
+
+ if (expr[0] == '\0') {
+ *r_value = 0;
+ return ok;
+ }
+
+ bpy_context_set(C, &gilstate);
+
+ ok = PyC_RunString_AsIntPtr(imports, expr, "<expr as intptr>", r_value);
+
+ if (ok == false) {
+ if (report_prefix != NULL) {
+ BPy_errors_to_report_ex(CTX_wm_reports(C), report_prefix, false, false);
+ }
+ else {
+ PyErr_Clear();
+ }
+ }
+
+ bpy_context_clear(C, &gilstate);
+
+ return ok;
+}
+
+/** \} */
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_operator.c b/source/blender/python/intern/bpy_operator.c
index 274c1934e9e..6d86d788644 100644
--- a/source/blender/python/intern/bpy_operator.c
+++ b/source/blender/python/intern/bpy_operator.c
@@ -436,12 +436,22 @@ static PyObject *pyop_getrna_type(PyObject *UNUSED(self), PyObject *value)
return (PyObject *)pyrna;
}
+static PyObject *pyop_get_bl_options(PyObject *UNUSED(self), PyObject *value)
+{
+ wmOperatorType *ot;
+ if ((ot = ot_lookup_from_py_string(value, "get_bl_options")) == NULL) {
+ return NULL;
+ }
+ return pyrna_enum_bitfield_to_py(rna_enum_operator_type_flag_items, ot->flag);
+}
+
static struct PyMethodDef bpy_ops_methods[] = {
{"poll", (PyCFunction)pyop_poll, METH_VARARGS, NULL},
{"call", (PyCFunction)pyop_call, METH_VARARGS, NULL},
{"as_string", (PyCFunction)pyop_as_string, METH_VARARGS, NULL},
{"dir", (PyCFunction)pyop_dir, METH_NOARGS, NULL},
{"get_rna_type", (PyCFunction)pyop_getrna_type, METH_O, NULL},
+ {"get_bl_options", (PyCFunction)pyop_get_bl_options, METH_O, NULL},
{"macro_define", (PyCFunction)PYOP_wrap_macro_define, METH_VARARGS, NULL},
{NULL, NULL, 0, NULL},
};
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 0c53639c67d..1d477421e30 100644
--- a/source/blender/python/mathutils/mathutils_bvhtree.c
+++ b/source/blender/python/mathutils/mathutils_bvhtree.c
@@ -549,8 +549,7 @@ static bool py_bvhtree_overlap_cb(void *userdata, int index_a, int index_b, int
}
}
- return (isect_tri_tri_v3(
- UNPACK3(tri_a_co), UNPACK3(tri_b_co), ix_pair[0], ix_pair[1]) &&
+ return (isect_tri_tri_v3(UNPACK3(tri_a_co), UNPACK3(tri_b_co), ix_pair[0], ix_pair[1]) &&
((verts_shared == 0) || (len_squared_v3v3(ix_pair[0], ix_pair[1]) > data->epsilon)));
}
@@ -590,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;
@@ -1038,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;
@@ -1054,7 +1053,7 @@ static Mesh *bvh_get_mesh(const char *funcname,
}
*r_free_mesh = true;
- return mesh_create_eval_final_render(depsgraph, scene, ob, &data_masks);
+ return mesh_create_eval_final(depsgraph, scene, ob, &data_masks);
}
if (ob_eval != NULL) {
if (use_cage) {
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/external_engine.c b/source/blender/render/intern/source/external_engine.c
index 075202e8a79..440c54f5eeb 100644
--- a/source/blender/render/intern/source/external_engine.c
+++ b/source/blender/render/intern/source/external_engine.c
@@ -513,15 +513,14 @@ void RE_engine_active_view_set(RenderEngine *engine, const char *viewname)
float RE_engine_get_camera_shift_x(RenderEngine *engine, Object *camera, bool use_spherical_stereo)
{
- Render *re = engine->re;
-
/* When using spherical stereo, get camera shift without multiview,
* leaving stereo to be handled by the engine. */
- if (use_spherical_stereo) {
- re = NULL;
+ Render *re = engine->re;
+ if (use_spherical_stereo || re == NULL) {
+ return BKE_camera_multiview_shift_x(NULL, camera, NULL);
}
- return BKE_camera_multiview_shift_x(re ? &re->r : NULL, camera, re->viewname);
+ return BKE_camera_multiview_shift_x(&re->r, camera, re->viewname);
}
void RE_engine_get_camera_model_matrix(RenderEngine *engine,
@@ -529,16 +528,15 @@ void RE_engine_get_camera_model_matrix(RenderEngine *engine,
bool use_spherical_stereo,
float *r_modelmat)
{
- Render *re = engine->re;
-
/* When using spherical stereo, get model matrix without multiview,
* leaving stereo to be handled by the engine. */
- if (use_spherical_stereo) {
- re = NULL;
+ Render *re = engine->re;
+ if (use_spherical_stereo || re == NULL) {
+ BKE_camera_multiview_model_matrix(NULL, camera, NULL, (float(*)[4])r_modelmat);
+ }
+ else {
+ BKE_camera_multiview_model_matrix(&re->r, camera, re->viewname, (float(*)[4])r_modelmat);
}
-
- BKE_camera_multiview_model_matrix(
- re ? &re->r : NULL, camera, re->viewname, (float(*)[4])r_modelmat);
}
bool RE_engine_get_spherical_stereo(RenderEngine *engine, Object *camera)
@@ -611,13 +609,13 @@ static void engine_depsgraph_init(RenderEngine *engine, ViewLayer *view_layer)
if (engine->re->r.scemode & R_BUTS_PREVIEW) {
Depsgraph *depsgraph = engine->depsgraph;
- DEG_graph_relations_update(depsgraph, bmain, scene, view_layer);
- DEG_evaluate_on_framechange(bmain, depsgraph, CFRA);
+ DEG_graph_relations_update(depsgraph);
+ DEG_evaluate_on_framechange(depsgraph, CFRA);
DEG_ids_check_recalc(bmain, depsgraph, scene, view_layer, true);
DEG_ids_clear_recalc(bmain, depsgraph);
}
else {
- BKE_scene_graph_update_for_newframe(engine->depsgraph, bmain);
+ BKE_scene_graph_update_for_newframe(engine->depsgraph);
}
}
@@ -639,7 +637,7 @@ void RE_engine_frame_set(RenderEngine *engine, int frame, float subframe)
CLAMP(cfra, MINAFRAME, MAXFRAME);
BKE_scene_frame_set(re->scene, cfra);
- BKE_scene_graph_update_for_newframe(engine->depsgraph, re->main);
+ BKE_scene_graph_update_for_newframe(engine->depsgraph);
BKE_scene_camera_switch_update(re->scene);
}
diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c
index 3236026c69f..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;
@@ -1964,7 +1964,7 @@ void RE_SetReports(Render *re, ReportList *reports)
static void render_update_depsgraph(Render *re)
{
Scene *scene = re->scene;
- DEG_evaluate_on_framechange(re->main, re->pipeline_depsgraph, CFRA);
+ DEG_evaluate_on_framechange(re->pipeline_depsgraph, CFRA);
BKE_scene_update_sound(re->pipeline_depsgraph, re->main);
}
@@ -1977,7 +1977,7 @@ static void render_init_depsgraph(Render *re)
DEG_debug_name_set(re->pipeline_depsgraph, "RENDER PIPELINE");
/* Make sure there is a correct evaluated scene pointer. */
- DEG_graph_build_for_render_pipeline(re->pipeline_depsgraph, re->main, scene, view_layer);
+ DEG_graph_build_for_render_pipeline(re->pipeline_depsgraph);
/* Update immediately so we have proper evaluated scene. */
render_update_depsgraph(re);
diff --git a/source/blender/simulation/CMakeLists.txt b/source/blender/simulation/CMakeLists.txt
index cbc6ee65303..e47586d55cc 100644
--- a/source/blender/simulation/CMakeLists.txt
+++ b/source/blender/simulation/CMakeLists.txt
@@ -45,8 +45,8 @@ set(SRC
intern/particle_function.cc
intern/particle_mesh_emitter.cc
intern/simulation_collect_influences.cc
- intern/simulation_solver_influences.cc
intern/simulation_solver.cc
+ intern/simulation_solver_influences.cc
intern/simulation_update.cc
intern/ConstrainedConjugateGradient.h
@@ -56,8 +56,8 @@ set(SRC
intern/particle_function.hh
intern/particle_mesh_emitter.hh
intern/simulation_collect_influences.hh
- intern/simulation_solver_influences.hh
intern/simulation_solver.hh
+ intern/simulation_solver_influences.hh
intern/time_interval.hh
SIM_mass_spring.h
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/WM_gizmo_types.h b/source/blender/windowmanager/gizmo/WM_gizmo_types.h
index bddf54b846f..8e2b5e04e42 100644
--- a/source/blender/windowmanager/gizmo/WM_gizmo_types.h
+++ b/source/blender/windowmanager/gizmo/WM_gizmo_types.h
@@ -244,7 +244,7 @@ struct wmGizmo {
int drag_part;
/** Distance to bias this gizmo above others when picking
- * (in worldspace, scaled by the gizmo scale - when used). */
+ * (in world-space, scaled by the gizmo scale - when used). */
float select_bias;
/**
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 43c08a2b980..a14ccfa104c 100644
--- a/source/blender/windowmanager/intern/wm.c
+++ b/source/blender/windowmanager/intern/wm.c
@@ -66,6 +66,7 @@
#ifdef WITH_PYTHON
# include "BPY_extern.h"
+# include "BPY_extern_run.h"
#endif
/* ****************************************************** */
@@ -112,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
@@ -270,7 +277,7 @@ void WM_keyconfig_reload(bContext *C)
{
if (CTX_py_init_get(C) && !G.background) {
#ifdef WITH_PYTHON
- BPY_execute_string(C, (const char *[]){"bpy", NULL}, "bpy.utils.keyconfig_init()");
+ BPY_run_string_eval(C, (const char *[]){"bpy", NULL}, "bpy.utils.keyconfig_init()");
#endif
}
}
diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c
index ec18a401fa4..37ed9f89bc7 100644
--- a/source/blender/windowmanager/intern/wm_dragdrop.c
+++ b/source/blender/windowmanager/intern/wm_dragdrop.c
@@ -401,7 +401,7 @@ void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect)
}
/* XXX todo, multiline drag draws... but maybe not, more types mixed wont work well */
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
for (drag = wm->drags.first; drag; drag = drag->next) {
const uchar text_col[] = {255, 255, 255, 255};
int iconsize = UI_DPI_ICON_SIZE;
@@ -495,5 +495,5 @@ void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect)
}
}
}
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c
index b8cb5432a49..c0de86a599c 100644
--- a/source/blender/windowmanager/intern/wm_draw.c
+++ b/source/blender/windowmanager/intern/wm_draw.c
@@ -51,6 +51,7 @@
#include "ED_screen.h"
#include "ED_view3d.h"
+#include "GPU_batch_presets.h"
#include "GPU_context.h"
#include "GPU_framebuffer.h"
#include "GPU_immediate.h"
@@ -307,7 +308,9 @@ static void wm_region_test_xr_do_draw(const wmWindowManager *wm,
static bool wm_region_use_viewport_by_type(short space_type, short region_type)
{
- return (ELEM(space_type, SPACE_VIEW3D, SPACE_IMAGE) && region_type == RGN_TYPE_WINDOW);
+ return (ELEM(space_type, SPACE_VIEW3D, SPACE_IMAGE, SPACE_NODE) &&
+ region_type == RGN_TYPE_WINDOW) ||
+ ((space_type == SPACE_SEQ) && ELEM(region_type, RGN_TYPE_PREVIEW, RGN_TYPE_WINDOW));
}
bool WM_region_use_viewport(ScrArea *area, ARegion *region)
@@ -574,9 +577,8 @@ void wm_draw_region_blend(ARegion *region, int view, bool blend)
const float rectg[4] = {rect_geo.xmin, rect_geo.ymin, rect_geo.xmax, rect_geo.ymax};
if (blend) {
- /* GL_ONE because regions drawn offscreen have premultiplied alpha. */
- GPU_blend_set_func(GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
- GPU_blend(true);
+ /* Regions drawn offscreen have premultiplied alpha. */
+ GPU_blend(GPU_BLEND_ALPHA_PREMULT);
}
/* setup actual texture */
@@ -596,13 +598,14 @@ void wm_draw_region_blend(ARegion *region, int view, bool blend)
GPU_shader_uniform_vector(shader, rect_geo_loc, 4, 1, rectg);
GPU_shader_uniform_vector(shader, color_loc, 4, 1, (const float[4]){1, 1, 1, 1});
- GPU_draw_primitive(GPU_PRIM_TRI_STRIP, 4);
+ GPUBatch *quad = GPU_batch_preset_quad();
+ GPU_batch_set_shader(quad, shader);
+ GPU_batch_draw(quad);
GPU_texture_unbind(texture);
if (blend) {
- GPU_blend_set_func(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
}
}
@@ -720,8 +723,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 +746,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. */
@@ -828,13 +829,11 @@ static void wm_draw_window(bContext *C, wmWindow *win)
}
else if (win->stereo3d_format->display_mode == S3D_DISPLAY_PAGEFLIP) {
/* For pageflip we simply draw to both back buffers. */
- GPU_backbuffer_bind(GPU_BACKBUFFER_LEFT);
- wm_draw_window_onscreen(C, win, 0);
-
GPU_backbuffer_bind(GPU_BACKBUFFER_RIGHT);
wm_draw_window_onscreen(C, win, 1);
- GPU_backbuffer_bind(GPU_BACKBUFFER);
+ GPU_backbuffer_bind(GPU_BACKBUFFER_LEFT);
+ wm_draw_window_onscreen(C, win, 0);
}
else if (ELEM(win->stereo3d_format->display_mode, S3D_DISPLAY_ANAGLYPH, S3D_DISPLAY_INTERLACE)) {
/* For anaglyph and interlace, we draw individual regions with
@@ -910,7 +909,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 bea4faa779a..6f7c074c704 100644
--- a/source/blender/windowmanager/intern/wm_event_system.c
+++ b/source/blender/windowmanager/intern/wm_event_system.c
@@ -350,9 +350,9 @@ 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, bmain, scene, view_layer);
+ DEG_graph_relations_update(depsgraph);
DEG_graph_on_visible_update(bmain, depsgraph, true);
}
DEG_make_active(depsgraph);
@@ -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 */
@@ -1363,8 +1367,7 @@ static int wm_operator_invoke(bContext *C,
ScrArea *area = CTX_wm_area(C);
/* Wrap only in X for header. */
- if (region &&
- ELEM(region->regiontype, RGN_TYPE_HEADER, RGN_TYPE_TOOL_HEADER, RGN_TYPE_FOOTER)) {
+ if (region && RGN_TYPE_IS_HEADER_ANY(region->regiontype)) {
wrap = WM_CURSOR_WRAP_X;
}
@@ -3190,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 ef4f2b4a62a..f53a3d6bf35 100644
--- a/source/blender/windowmanager/intern/wm_files.c
+++ b/source/blender/windowmanager/intern/wm_files.c
@@ -121,7 +121,8 @@
#include "RE_engine.h"
#ifdef WITH_PYTHON
-# include "BPY_extern.h"
+# include "BPY_extern_python.h"
+# include "BPY_extern_run.h"
#endif
#include "DEG_depsgraph.h"
@@ -579,14 +580,14 @@ static void wm_file_read_post(bContext *C,
if (use_userdef || reset_app_template) {
/* Only run when we have a template path found. */
if (BKE_appdir_app_template_any()) {
- BPY_execute_string(
+ BPY_run_string_eval(
C, (const char *[]){"bl_app_template_utils", NULL}, "bl_app_template_utils.reset()");
reset_all = true;
}
}
if (reset_all) {
/* sync addons, these may have changed from the defaults */
- BPY_execute_string(C, (const char *[]){"addon_utils", NULL}, "addon_utils.reset_all()");
+ BPY_run_string_eval(C, (const char *[]){"addon_utils", NULL}, "addon_utils.reset_all()");
}
if (use_data) {
BPY_python_reset(C);
@@ -923,7 +924,7 @@ void wm_homefile_read(bContext *C,
*
* Note that this fits into 'wm_file_read_pre' function but gets messy
* since we need to know if 'reset_app_template' is true. */
- BPY_execute_string(C, (const char *[]){"addon_utils", NULL}, "addon_utils.disable_all()");
+ BPY_run_string_eval(C, (const char *[]){"addon_utils", NULL}, "addon_utils.disable_all()");
}
#endif /* WITH_PYTHON */
}
@@ -1575,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 55233168ab2..b245bbe054d 100644
--- a/source/blender/windowmanager/intern/wm_gesture.c
+++ b/source/blender/windowmanager/intern/wm_gesture.c
@@ -236,7 +236,7 @@ static void wm_gesture_draw_rect(wmGesture *gt)
uint shdr_pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
immUniformColor4f(1.0f, 1.0f, 1.0f, 0.05f);
@@ -245,7 +245,7 @@ static void wm_gesture_draw_rect(wmGesture *gt)
immUnbindProgram();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
shdr_pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
@@ -274,7 +274,7 @@ static void wm_gesture_draw_circle(wmGesture *gt)
{
rcti *rect = (rcti *)gt->customdata;
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
const uint shdr_pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
@@ -286,7 +286,7 @@ static void wm_gesture_draw_circle(wmGesture *gt)
immUnbindProgram();
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
@@ -353,9 +353,7 @@ static void draw_filled_lasso(wmGesture *gt)
draw_filled_lasso_px_cb,
&lasso_fill_data);
- /* Additive Blending */
- GPU_blend(true);
- GPU_blend_set_func(GPU_ONE, GPU_ONE);
+ GPU_blend(GPU_BLEND_ADDITIVE_PREMULT);
IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR);
GPU_shader_bind(state.shader);
@@ -363,14 +361,13 @@ 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();
MEM_freeN(pixel_buf);
- GPU_blend(false);
- GPU_blend_set_func(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_NONE);
}
MEM_freeN(mcoords);
diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c
index 03e84f098c0..b85bf8cb323 100644
--- a/source/blender/windowmanager/intern/wm_init_exit.c
+++ b/source/blender/windowmanager/intern/wm_init_exit.c
@@ -85,6 +85,7 @@
#ifdef WITH_PYTHON
# include "BPY_extern.h"
+# include "BPY_extern_python.h"
#endif
#include "GHOST_C-api.h"
@@ -209,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;
}
@@ -349,8 +350,6 @@ void WM_init(bContext *C, int argc, const char **argv)
BKE_material_copybuf_clear();
ED_render_clear_mtex_copybuf();
- // GPU_blend_set_func(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
-
wm_history_file_read();
/* allow a path of "", this is what happens when making a new file */
diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c
index 1964813fff9..9b25d660ff6 100644
--- a/source/blender/windowmanager/intern/wm_operators.c
+++ b/source/blender/windowmanager/intern/wm_operators.c
@@ -2393,7 +2393,7 @@ static void radial_control_paint_cursor(bContext *UNUSED(C), int x, int y, void
}
GPU_matrix_translate_2f((float)x, (float)y);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
GPU_line_smooth(true);
/* apply zoom if available */
@@ -2472,7 +2472,7 @@ static void radial_control_paint_cursor(bContext *UNUSED(C), int x, int y, void
BLF_position(fontid, -0.5f * strwidth, -0.5f * strheight, 0.0f);
BLF_draw(fontid, str, strdrawlen);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
GPU_line_smooth(false);
}
@@ -3151,7 +3151,6 @@ static const EnumPropertyItem redraw_timer_type_items[] = {
};
static void redraw_timer_step(bContext *C,
- Main *bmain,
Scene *scene,
struct Depsgraph *depsgraph,
wmWindow *win,
@@ -3202,7 +3201,7 @@ static void redraw_timer_step(bContext *C,
}
else if (type == eRTAnimationStep) {
scene->r.cfra += (cfra == scene->r.cfra) ? 1 : -1;
- BKE_scene_graph_update_for_newframe(depsgraph, bmain);
+ BKE_scene_graph_update_for_newframe(depsgraph);
}
else if (type == eRTAnimationPlay) {
/* play anim, return on same frame as started with */
@@ -3215,7 +3214,7 @@ static void redraw_timer_step(bContext *C,
scene->r.cfra = scene->r.sfra;
}
- BKE_scene_graph_update_for_newframe(depsgraph, bmain);
+ BKE_scene_graph_update_for_newframe(depsgraph);
redraw_timer_window_swap(C);
}
}
@@ -3231,7 +3230,6 @@ static void redraw_timer_step(bContext *C,
static int redraw_timer_exec(bContext *C, wmOperator *op)
{
- Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
wmWindow *win = CTX_wm_window(C);
ScrArea *area = CTX_wm_area(C);
@@ -3256,7 +3254,7 @@ static int redraw_timer_exec(bContext *C, wmOperator *op)
wm_window_make_drawable(wm, win);
for (a = 0; a < iter; a++) {
- redraw_timer_step(C, bmain, scene, depsgraph, win, area, region, type, cfra);
+ redraw_timer_step(C, scene, depsgraph, win, area, region, type, cfra);
iter_steps += 1;
if (time_limit != 0.0) {
diff --git a/source/blender/windowmanager/intern/wm_playanim.c b/source/blender/windowmanager/intern/wm_playanim.c
index a0a21fadbbb..86d3f7f35dc 100644
--- a/source/blender/windowmanager/intern/wm_playanim.c
+++ b/source/blender/windowmanager/intern/wm_playanim.c
@@ -310,14 +310,11 @@ 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) {
- GPU_blend(true);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(GPU_BLEND_ALPHA);
imm_draw_box_checker_2d_ex(offs_x,
offs_y,
@@ -342,7 +339,7 @@ static void playanim_toscreen(
((ps->draw_flip[1] ? -1.0f : 1.0f)) * (ps->zoom / (float)ps->win_y),
NULL);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
pupdate_time();
@@ -1315,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_surface.c b/source/blender/windowmanager/intern/wm_surface.c
index 12e55790259..e8cb5d9cd7d 100644
--- a/source/blender/windowmanager/intern/wm_surface.c
+++ b/source/blender/windowmanager/intern/wm_surface.c
@@ -56,8 +56,6 @@ void wm_surface_clear_drawable(void)
WM_opengl_context_release(g_drawable->ghost_ctx);
GPU_context_active_set(NULL);
- BLF_batch_reset();
- gpu_batch_presets_reset();
immDeactivate();
if (g_drawable->deactivate) {
@@ -86,7 +84,7 @@ void wm_surface_set_drawable(wmSurface *surface, bool activate)
void wm_surface_make_drawable(wmSurface *surface)
{
- BLI_assert(GPU_framebuffer_active_get() == NULL);
+ BLI_assert(GPU_framebuffer_active_get() == GPU_framebuffer_back_get());
if (surface != g_drawable) {
wm_surface_clear_drawable();
@@ -97,7 +95,7 @@ void wm_surface_make_drawable(wmSurface *surface)
void wm_surface_reset_drawable(void)
{
BLI_assert(BLI_thread_is_main());
- BLI_assert(GPU_framebuffer_active_get() == NULL);
+ BLI_assert(GPU_framebuffer_active_get() == GPU_framebuffer_back_get());
if (g_drawable) {
wm_surface_clear_drawable();
diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c
index 47afa343394..0e19f79e659 100644
--- a/source/blender/windowmanager/intern/wm_window.c
+++ b/source/blender/windowmanager/intern/wm_window.c
@@ -646,11 +646,7 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm,
}
#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);
- }
+ GPU_clear_color(0.55f, 0.55f, 0.55f, 1.0f);
/* needed here, because it's used before it reads userdef */
WM_window_set_dpi(win);
@@ -658,9 +654,6 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm,
wm_window_swap_buffers(win);
// GHOST_SetWindowState(ghostwin, GHOST_kWindowStateModified);
-
- /* standard state vars for window */
- GPU_state_init();
}
else {
wm_window_set_drawable(wm, prev_windrawable, false);
@@ -1112,8 +1105,6 @@ static void wm_window_set_drawable(wmWindowManager *wm, wmWindow *win, bool acti
void wm_window_clear_drawable(wmWindowManager *wm)
{
if (wm->windrawable) {
- BLF_batch_reset();
- gpu_batch_presets_reset();
immDeactivate();
wm->windrawable = NULL;
}
@@ -1121,7 +1112,7 @@ void wm_window_clear_drawable(wmWindowManager *wm)
void wm_window_make_drawable(wmWindowManager *wm, wmWindow *win)
{
- BLI_assert(GPU_framebuffer_active_get() == NULL);
+ BLI_assert(GPU_framebuffer_active_get() == GPU_framebuffer_back_get());
if (win != wm->windrawable && win->ghostwin) {
// win->lmbut = 0; /* keeps hanging when mousepressed while other window opened */
@@ -1142,7 +1133,7 @@ void wm_window_make_drawable(wmWindowManager *wm, wmWindow *win)
void wm_window_reset_drawable(void)
{
BLI_assert(BLI_thread_is_main());
- BLI_assert(GPU_framebuffer_active_get() == NULL);
+ BLI_assert(GPU_framebuffer_active_get() == GPU_framebuffer_back_get());
wmWindowManager *wm = G_MAIN->wm.first;
if (wm == NULL) {
@@ -2493,25 +2484,30 @@ void *WM_opengl_context_create(void)
* So we should call this function only on the main thread.
*/
BLI_assert(BLI_thread_is_main());
- BLI_assert(GPU_framebuffer_active_get() == NULL);
- return GHOST_CreateOpenGLContext(g_system);
+ BLI_assert(GPU_framebuffer_active_get() == GPU_framebuffer_back_get());
+
+ GHOST_GLSettings glSettings = {0};
+ if (G.debug & G_DEBUG_GPU) {
+ glSettings.flags |= GHOST_glDebugContext;
+ }
+ return GHOST_CreateOpenGLContext(g_system, glSettings);
}
void WM_opengl_context_dispose(void *context)
{
- BLI_assert(GPU_framebuffer_active_get() == NULL);
+ BLI_assert(GPU_framebuffer_active_get() == GPU_framebuffer_back_get());
GHOST_DisposeOpenGLContext(g_system, (GHOST_ContextHandle)context);
}
void WM_opengl_context_activate(void *context)
{
- BLI_assert(GPU_framebuffer_active_get() == NULL);
+ BLI_assert(GPU_framebuffer_active_get() == GPU_framebuffer_back_get());
GHOST_ActivateOpenGLContext((GHOST_ContextHandle)context);
}
void WM_opengl_context_release(void *context)
{
- BLI_assert(GPU_framebuffer_active_get() == NULL);
+ BLI_assert(GPU_framebuffer_active_get() == GPU_framebuffer_back_get());
GHOST_ReleaseOpenGLContext((GHOST_ContextHandle)context);
}
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 f3ce7be993f..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;
@@ -250,9 +250,14 @@ void wm_xr_session_draw_data_update(const wmXrSessionState *state,
switch (event) {
case SESSION_STATE_EVENT_START:
- /* We want to start the session exactly at landmark position.
- * Run-times may have a non-[0,0,0] starting position that we have to subtract for that. */
- copy_v3_v3(draw_data->eye_position_ofs, draw_view->local_pose.position);
+ if (use_position_tracking) {
+ /* We want to start the session exactly at landmark position.
+ * Run-times may have a non-[0,0,0] starting position that we have to subtract for that. */
+ copy_v3_v3(draw_data->eye_position_ofs, draw_view->local_pose.position);
+ }
+ else {
+ copy_v3_fl(draw_data->eye_position_ofs, 0.0f);
+ }
break;
/* This should be triggered by the VR add-on if a landmark changes. */
case SESSION_STATE_EVENT_RESET_TO_BASE_POSE:
diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt
index 8784d9f1744..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})
@@ -722,6 +732,19 @@ elseif(WIN32)
endif()
endif()
+ if(WITH_OPENVDB)
+ install(
+ FILES ${LIBDIR}/openvdb/bin/openvdb.dll
+ DESTINATION "."
+ CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel
+ )
+ install(
+ FILES ${LIBDIR}/openvdb/bin/openvdb_d.dll
+ DESTINATION "."
+ CONFIGURATIONS Debug
+ )
+ endif()
+
if(WITH_PYTHON)
string(REPLACE "." "" _PYTHON_VERSION_NO_DOTS ${PYTHON_VERSION})
diff --git a/source/creator/creator.c b/source/creator/creator.c
index 3d76d832d9f..37fbf0cf76a 100644
--- a/source/creator/creator.c
+++ b/source/creator/creator.c
@@ -442,23 +442,6 @@ int main(int argc,
BKE_materials_init();
-#ifdef WITH_USD
- /* Workaround to make it possible to pass a path at runtime to USD.
- *
- * USD requires some JSON files, and it uses a static constructor to determine the possible
- * file-system paths to find those files. This made it impossible for Blender to pass a path to
- * the USD library at runtime, as the constructor would run before Blender's main() function.
- * We have patched USD (see usd.diff) to avoid that particular static constructor, and have an
- * initialization function instead.
- *
- * This function is implemented in the USD source code, `pxr/base/lib/plug/initConfig.cpp`. */
- extern void usd_initialise_plugin_path(const char *datafiles_usd_path);
-
- /* Tell USD which directory to search for its JSON files. If 'datafiles/usd'
- * does not exist, the USD library will not be able to read or write any files. */
- usd_initialise_plugin_path(BKE_appdir_folder_id(BLENDER_DATAFILES, "usd"));
-#endif /* WITH_USD */
-
if (G.background == 0) {
#ifndef WITH_PYTHON_MODULE
BLI_argsParse(ba, 2, NULL, NULL);
diff --git a/source/creator/creator_args.c b/source/creator/creator_args.c
index b8e99899821..0d1c932d2d2 100644
--- a/source/creator/creator_args.c
+++ b/source/creator/creator_args.c
@@ -61,7 +61,8 @@
# endif
# ifdef WITH_PYTHON
-# include "BPY_extern.h"
+# include "BPY_extern_python.h"
+# include "BPY_extern_run.h"
# endif
# include "RE_engine.h"
@@ -750,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).";
@@ -1299,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
@@ -1779,7 +1799,7 @@ static int arg_handle_python_file_run(int argc, const char **argv, void *data)
BLI_path_abs_from_cwd(filename, sizeof(filename));
bool ok;
- BPY_CTX_SETUP(ok = BPY_execute_filepath(C, filename, NULL));
+ BPY_CTX_SETUP(ok = BPY_run_filepath(C, filename, NULL));
if (!ok && app_state.exit_code_on_error.python) {
printf("\nError: script failed, file: '%s', exiting.\n", argv[1]);
BPY_python_end();
@@ -1814,7 +1834,7 @@ static int arg_handle_python_text_run(int argc, const char **argv, void *data)
bool ok;
if (text) {
- BPY_CTX_SETUP(ok = BPY_execute_text(C, text, NULL, false));
+ BPY_CTX_SETUP(ok = BPY_run_text(C, text, NULL, false));
}
else {
printf("\nError: text block not found %s.\n", argv[1]);
@@ -1851,7 +1871,7 @@ static int arg_handle_python_expr_run(int argc, const char **argv, void *data)
/* workaround for scripts not getting a bpy.context.scene, causes internal errors elsewhere */
if (argc > 1) {
bool ok;
- BPY_CTX_SETUP(ok = BPY_execute_string_ex(C, NULL, argv[1], false));
+ BPY_CTX_SETUP(ok = BPY_run_string_exec(C, NULL, argv[1]));
if (!ok && app_state.exit_code_on_error.python) {
printf("\nError: script failed, expr: '%s', exiting.\n", argv[1]);
BPY_python_end();
@@ -1878,7 +1898,7 @@ static int arg_handle_python_console_run(int UNUSED(argc), const char **argv, vo
# ifdef WITH_PYTHON
bContext *C = data;
- BPY_CTX_SETUP(BPY_execute_string(C, (const char *[]){"code", NULL}, "code.interact()"));
+ BPY_CTX_SETUP(BPY_run_string_eval(C, (const char *[]){"code", NULL}, "code.interact()"));
return 0;
# else
@@ -1951,7 +1971,7 @@ static int arg_handle_addons_set(int argc, const char **argv, void *data)
BLI_snprintf(str, slen, script_str, argv[1]);
BLI_assert(strlen(str) + 1 == slen);
- BPY_CTX_SETUP(BPY_execute_string_ex(C, NULL, str, false));
+ BPY_CTX_SETUP(BPY_run_string_exec(C, NULL, str));
free(str);
# else
UNUSED_VARS(argv, data);
@@ -2213,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/creator/creator_signals.c b/source/creator/creator_signals.c
index ad0b7b2547d..29e12a96fe1 100644
--- a/source/creator/creator_signals.c
+++ b/source/creator/creator_signals.c
@@ -60,7 +60,7 @@
# include <signal.h>
# ifdef WITH_PYTHON
-# include "BPY_extern.h" /* BPY_python_backtrace */
+# include "BPY_extern_python.h" /* BPY_python_backtrace */
# endif
# include "creator_intern.h" /* own include */
diff --git a/source/tools b/source/tools
-Subproject 6a252de776d0b9dca3167c30a7621a4f1e9bc91
+Subproject 896c5f78952adb2d091d28c65086d46992dabda
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 1b78a938a04..18f61d83c3c 100644
--- a/tests/python/CMakeLists.txt
+++ b/tests/python/CMakeLists.txt
@@ -662,8 +662,8 @@ if(WITH_ALEMBIC)
get_filename_component(ALEMBIC_ROOT_DIR ${real_include_dir} DIRECTORY)
add_python_test(
- alembic_tests
- ${CMAKE_CURRENT_LIST_DIR}/alembic_tests.py
+ alembic_export_tests
+ ${CMAKE_CURRENT_LIST_DIR}/alembic_export_tests.py
--blender "${TEST_BLENDER_EXE}"
--testdir "${TEST_SRC_DIR}/alembic"
--alembic-root "${ALEMBIC_ROOT_DIR}"
diff --git a/tests/python/alembic_tests.py b/tests/python/alembic_export_tests.py
index 66545dc85c7..b5d6bf65f3f 100644
--- a/tests/python/alembic_tests.py
+++ b/tests/python/alembic_export_tests.py
@@ -19,11 +19,20 @@
# <pep8 compliant>
+"""
+Alembic Export Tests
+
+This test suite runs outside of Blender. Tests run Blender to call the exporter,
+and then use the Alembic CLI tools to inspect the exported Alembic files.
+"""
+
+
import argparse
import pathlib
import subprocess
import sys
import unittest
+from typing import Tuple
from modules.test_utils import (
with_tempdir,
@@ -51,20 +60,16 @@ class AbstractAlembicTest(AbstractBlenderRunnerTest):
# 'abcls' array notation, like "name[16]"
cls.abcls_array = re.compile(r'^(?P<name>[^\[]+)(\[(?P<arraysize>\d+)\])?$')
- def abcprop(self, filepath: pathlib.Path, proppath: str) -> dict:
- """Uses abcls to obtain compound property values from an Alembic object.
-
- A dict of subproperties is returned, where the values are Python values.
+ def abcls(self, *arguments) -> Tuple[int, str]:
+ """Uses abcls and return its output.
- The Python bindings for Alembic are old, and only compatible with Python 2.x,
- so that's why we can't use them here, and have to rely on other tooling.
+ :return: tuple (process exit status code, stdout)
"""
- import collections
- abcls = self.alembic_root / 'bin' / 'abcls'
-
- command = (str(abcls), '-vl', '%s%s' % (filepath, proppath))
- proc = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
+ command = (self.alembic_root / 'bin' / 'abcls', *arguments)
+ # Convert Path to str; Path works fine on Linux, but not on Windows.
+ command_str = [str(arg) for arg in command]
+ proc = subprocess.run(command_str, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
timeout=30)
coloured_output = proc.stdout
@@ -76,7 +81,25 @@ class AbstractAlembicTest(AbstractBlenderRunnerTest):
output = output.replace('\r\n', '\n').replace('\r', '\n')
if proc.returncode:
- raise AbcPropError('Error %d running %s:\n%s' % (proc.returncode, ' '.join(command), output))
+ str_command = " ".join(str(c) for c in command)
+ print(f'command {str_command} failed with status {proc.returncode}')
+
+ return (proc.returncode, output)
+
+ def abcprop(self, filepath: pathlib.Path, proppath: str) -> dict:
+ """Uses abcls to obtain compound property values from an Alembic object.
+
+ A dict of subproperties is returned, where the values are Python values.
+
+ The Python bindings for Alembic are old, and only compatible with Python 2.x,
+ so that's why we can't use them here, and have to rely on other tooling.
+ """
+ import collections
+
+ command = ('-vl', '%s%s' % (filepath, proppath))
+ returncode, output = self.abcls(*command)
+ if returncode:
+ raise AbcPropError('Error %d running abcls:\n%s' % (returncode, output))
# Mapping from value type to callable that can convert a string to Python values.
converters = {
@@ -104,9 +127,11 @@ class AbstractAlembicTest(AbstractBlenderRunnerTest):
if proptype == 'CompoundProperty':
# To read those, call self.abcprop() on it.
continue
- if len(parts) < 2:
- raise ValueError('Error parsing result from abcprop: %s', info.strip())
- valtype_and_arrsize, name_and_extent = parts[1:]
+
+ try:
+ valtype_and_arrsize, name_and_extent = parts[1:]
+ except ValueError as ex:
+ raise ValueError(f'Error parsing result from abcprop "{info.strip()}": {ex}') from ex
# Parse name and extent
m = self.abcls_array.match(name_and_extent)
@@ -526,6 +551,41 @@ class LongNamesExportTest(AbstractAlembicTest):
self.assertIn('.faceCounts', abcprop)
+class InvisibleObjectExportTest(AbstractAlembicTest):
+ """Export an object which is invisible.
+
+ This test only tests a small subset of the functionality that is required to
+ export invisible objects. It just tests that the visibility property is
+ written, and that it has the correct initial value. This is a limitation
+ caused by these tests relying on `abcls`.
+ """
+
+ @with_tempdir
+ def test_hierarchical_export(self, tempdir: pathlib.Path):
+ abc = tempdir / 'visibility.abc'
+ script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=2, " \
+ "renderable_only=False, visible_objects_only=False)" % abc.as_posix()
+ self.run_blender('visibility.blend', script)
+
+ def test(cube_name: str, expect_visible: bool):
+ returncode, output = self.abcls('-va', f'{abc}/{cube_name}')
+ if returncode:
+ self.fail(f"abcls failed: {output}")
+ output = output.strip()
+ self.assertEqual(f'Cube .xform visible {int(expect_visible)}', output)
+
+ # This cube is always visible.
+ test('VisibleCube', True)
+
+ # This cube is never visible, and thus will not be pulled into the
+ # depsgraph by the standard builder, only by the all-objects builder.
+ test('InvisibleCube', False)
+
+ # This cube has animated visibility, and thus will be pulled into the
+ # depsgraph by the standard builder as well as the all-objects builder.
+ test('InvisibleAnimatedCube', False)
+
+
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--blender', required=True)
diff --git a/tests/python/boolean_operator.py b/tests/python/boolean_operator.py
index 5a674c35a47..b35c69b7ca5 100644
--- a/tests/python/boolean_operator.py
+++ b/tests/python/boolean_operator.py
@@ -34,16 +34,16 @@ from modules.mesh_test import OperatorTest
def main():
tests = [
- ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_1', 'intersect_boolean', {'operation': 'UNION'}],
- ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_2', 'intersect_boolean', {'operation': 'INTERSECT'}],
- ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_3', 'intersect_boolean', {'operation': 'DIFFERENCE'}],
- ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_4', 'intersect', {'separate_mode': 'CUT'}],
- ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_5', 'intersect', {'separate_mode': 'ALL'}],
- ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_6', 'intersect', {'separate_mode': 'NONE'}],
+ ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_1', 'intersect_boolean', {'operation': 'UNION', 'solver' : 'FAST'}],
+ ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_2', 'intersect_boolean', {'operation': 'INTERSECT', 'solver' : 'FAST'}],
+ ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_3', 'intersect_boolean', {'operation': 'DIFFERENCE', 'solver' : 'FAST'}],
+ ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_4', 'intersect', {'separate_mode': 'CUT', 'solver' : 'FAST'}],
+ ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_5', 'intersect', {'separate_mode': 'ALL', 'solver' : 'FAST'}],
+ ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_6', 'intersect', {'separate_mode': 'NONE', 'solver' : 'FAST'}],
['FACE', {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, 'Cubecube', 'Cubecube_result_7', 'intersect',
- {'mode': 'SELECT', 'separate_mode': 'NONE'}],
- ['FACE', {6, 7, 8, 9, 10}, 'Cubecone', 'Cubecone_result_1', 'intersect_boolean', {'operation': 'UNION'}],
- ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecones', 'Cubecones_result_1', 'intersect_boolean', {'operation': 'UNION'}],
+ {'mode': 'SELECT', 'separate_mode': 'NONE', 'solver' : 'FAST'}],
+ ['FACE', {6, 7, 8, 9, 10}, 'Cubecone', 'Cubecone_result_1', 'intersect_boolean', {'operation': 'UNION', 'solver' : 'FAST'}],
+ ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecones', 'Cubecones_result_1', 'intersect_boolean', {'operation': 'UNION', 'solver' : 'FAST'}],
]
operator_test = OperatorTest(tests)
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/modifiers.py b/tests/python/modifiers.py
index 5e032f658af..4bbcf226aba 100644
--- a/tests/python/modifiers.py
+++ b/tests/python/modifiers.py
@@ -46,7 +46,7 @@ def get_generate_modifiers_list(test_object_name, randomize=False):
generate_modifiers = [
ModifierSpec('array', 'ARRAY', {}),
ModifierSpec('bevel', 'BEVEL', {'width': 0.1}),
- ModifierSpec('boolean', 'BOOLEAN', {'object': boolean_test_object}),
+ ModifierSpec('boolean', 'BOOLEAN', {'object': boolean_test_object, 'solver': 'FAST'}),
ModifierSpec('build', 'BUILD', {'frame_start': 0, 'frame_duration': 1}),
ModifierSpec('decimate', 'DECIMATE', {}),
ModifierSpec('edge split', 'EDGE_SPLIT', {}),
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",